将凭据管理器与“使用 Google 账号登录”集成

启用了 Credential Manager 的底部动作条,显示了可供选择的不同身份。
图 1. 与 Credential Manager API 集成后,“使用 Google 账号登录”底部动作条对话框的外观。

本文将介绍如何使用 Google ID 辅助库将“使用 Google 账号登录”底部动作条对话框从 Google Identity 服务 (GIS) 迁移到 Credentials Manager API。

使用 Credentials Manager API 的应用经过配置,可向最终用户提供一致的 Android 界面,会为用户保存不同的登录选项供其选择,包括启用了通行密钥的账号。建议使用该 Android API 来整合不同的凭据类型和提供方。从 Android 14 开始,用户还可以通过 Credential Manager API 使用第三方密码管理工具。

声明依赖项

在模块的 build.gradle 文件中,使用最新版本声明依赖项:

dependencies {
  // ... other dependencies

  implementation "androidx.credentials:credentials:<latest version>"
  implementation "androidx.credentials:credentials-play-services-auth:<latest version>"
  implementation "com.google.android.libraries.identity.googleid:googleid:<latest version>"
}

按照相关说明设置您的 Google API 控制台项目。关于添加依赖项的指南,请遵循上述说明。

实例化 Google 登录请求

如需开始构建您的实现,请实例化 Google 登录请求。使用 GetGoogleIdOption 检索用户的 Google ID 令牌。

Kotlin

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(SERVER_CLIENT_ID)
  .build()

Java

GetGoogleIdOption googleIdOption = new GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(SERVER_CLIENT_ID)
  .build();

首先,您应调用该 API,并将 setFilterByAuthorizedAccounts 参数设置为 true。如果没有可用的凭据,请再次调用该 API 并将 setFilterByAuthorizedAccounts 设置为 false

如果您希望尽可能让用户自动登录,请在 GetGoogleIdOption 请求中使用 setAutoSelectEnabled 启用该功能。当满足以下条件时,用户就能自动登录:

  • 用户只有一个为您的应用保存的凭据。也就是说,一个已存的密码或一个已存的 Google 账号。
  • 用户尚未在其 Google 账号设置中停用自动登录功能。

为了提高登录安全性并避免重放攻击,请使用 setNonce 在每个请求中添加 Nonce。详细了解如何生成 Nonce

Kotlin

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(SERVER_CLIENT_ID)
  .setNonce(<nonce string to use when generating a Google ID token>)
  .build()

Java

GetGoogleIdOption googleIdOption = new GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(SERVER_CLIENT_ID)
  .setNonce(<nonce string to use when generating a Google ID token>);
  .build();

使用 Google 账号登录

设置“使用 Google 账号登录”流程的步骤如下:

  1. 实例化 GetCredentialRequest,并添加在上文中创建的 googleIdOption 以检索凭据。
  2. 将此请求传递给 getCredential() (Kotlin) 或 getCredentialAsync() (Java) 调用以检索用户的可用凭据。
  3. API 成功运行后,提取存放 GoogleIdTokenCredential 数据结果的 CustomCredential
  4. CustomCredential 的类型应该等于 GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL 的值。使用 GoogleIdTokenCredential.createFrom 方法将该对象转换为 GoogleIdTokenCredential
  5. 如果转换成功,请提取 GoogleIdTokenCredential 的 ID,对其进行验证,并在服务器上对凭据进行身份验证。
  6. 如果转换失败并显示 GoogleIdTokenParsingException,那么您可能需要更新“使用 Google 账号登录”库的版本。
  7. 捕获任何无法识别的自定义凭据类型。

Kotlin

val request: GetCredentialRequest = Builder()
  .addGetCredentialOption(googleIdOption)
  .build()

coroutineScope.launch {
  try {
    val result = credentialManager.getCredential(
      request = request,
      context = activityContext,
    )
    handleSignIn(result)
  } catch (e: GetCredentialException) {
    handleFailure(e)
  }
}

fun handleSignIn(result: GetCredentialResponse) {
  // Handle the successfully returned credential.
  val credential = result.credential

  when (credential) {
    is PublicKeyCredential -> {
      // Share responseJson such as a GetCredentialResponse on your server to
      // validate and authenticate
      responseJson = credential.authenticationResponseJson
    }

    is PasswordCredential -> {
      // Send ID and password to your server to validate and authenticate.
      val username = credential.id
      val password = credential.password
    }

    is CustomCredential -> {
      if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
        try {
          // Use googleIdTokenCredential and extract id to validate and
          // authenticate on your server.
          val googleIdTokenCredential = GoogleIdTokenCredential
            .createFrom(credential.data)
        } catch (e: GoogleIdTokenParsingException) {
          Log.e(TAG, "Received an invalid google id token response", e)
        }
      } else {
        // Catch any unrecognized custom credential type here.
        Log.e(TAG, "Unexpected type of credential")
      }
    }

    else -> {
      // Catch any unrecognized credential type here.
      Log.e(TAG, "Unexpected type of credential")
    }
  }
}

Java

GetCredentialRequest request = new GetCredentialRequest.Builder()
  .addGetCredentialOption(googleIdOption)
  .build();

// Launch sign in flow and do getCredential Request to retrieve the credentials
credentialManager.getCredentialAsync(
  requireActivity(),
  request,
  cancellationSignal,
  <executor>,
  new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
    @Override
    public void onResult(GetCredentialResponse result) {
      handleSignIn(result);
    }

    @Override
    public void onError(GetCredentialException e) {
      handleFailure(e);
    }
  }
);

public void handleSignIn(GetCredentialResponse result) {
  // Handle the successfully returned credential.
  Credential credential = result.getCredential();

  if (credential instanceof PublicKeyCredential) {
    String responseJson = ((PublicKeyCredential) credential).getAuthenticationResponseJson();
    // Share responseJson i.e. a GetCredentialResponse on your server to validate and authenticate
  } else if (credential instanceof PasswordCredential) {
    String username = ((PasswordCredential) credential).getId();
    String password = ((PasswordCredential) credential).getPassword();
    // Use id and password to send to your server to validate and authenticate
  } else if (credential instanceof CustomCredential) {
    if (GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL.equals(credential.getType())) {
      try {
        // Use googleIdTokenCredential and extract id to validate and
        // authenticate on your server
        GoogleIdTokenCredential googleIdTokenCredential = GoogleIdTokenCredential.createFrom(((CustomCredential) credential).getData());
      } catch (GoogleIdTokenParsingException e) {
        Log.e(TAG, "Received an invalid Google ID token response", e);
      }
    } else {
      // Catch any unrecognized custom credential type here.
      Log.e(TAG, "Unexpected type of credential");
    }
  } else {
    // Catch any unrecognized credential type here.
    Log.e(TAG, "Unexpected type of credential");
  }
}

“使用 Google 账号登录”按钮

Credential Manager 采用最新的 Google ID 辅助库,支持“使用 Google 账号登录”按钮。如需触发“使用 Google 账号登录”按钮流程,请使用 GetSignInWithGoogleOption 而非 GetGoogleIdOption,并按照与之前相同的方式处理返回的 GoogleIdTokenCredential

注册 Google 账号

如果在将 setFilterByAuthorizedAccounts 设置为 true 后同时实例化 GetGoogleIdOption 请求并传递到 GetCredentialsRequest 时未返回任何结果,则表示没有用于登录的已获授权的账号。此时,您应该设置 setFilterByAuthorizedAccounts(false) 并调用注册 Google 账号

Kotlin

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(false)
  .setServerClientId(SERVER_CLIENT_ID)
  .build()

Java

GetGoogleIdOption googleIdOption = new GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(false)
  .setServerClientId(SERVER_CLIENT_ID)
  .build();

实例化 Google 账号注册请求后,按照使用 Google 账号登录部分中所述的相似方式启动身份验证流程。

处理退出账号操作

当用户从应用中退出账号时,请调用 API clearCredentialState() 方法以清除当前用户凭据状态并重置内部登录状态。