將憑證管理工具與「使用 Google 帳戶登入」功能整合

已啟用 Credential Manager 的底部功能表,顯示多個可選取的身分。
圖 1 與 Credential Manager API 整合後,畫面上會呈現的「使用 Google 帳戶登入」底部功能表對話方塊。

本文說明如何使用 Google ID 輔助程式庫,將「使用 Google 帳戶登入」底部功能表對話方塊從 Google Identity 服務 (GIS) 遷移至 Credential Manager API。

如果應用程式使用 Credential 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();

您應先呼叫 setFilterByAuthorizedAccounts 參數設為 true 的 API。如果沒有可用憑證,請再次呼叫 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 帳戶註冊

如果在將 GetGoogleIdOption 例項化及傳遞至 GetCredentialsRequest 時將 setFilterByAuthorizedAccounts 設為 true,之後系統卻沒有傳回任何結果,就表示沒有可登入的已授權帳戶。此時,您應設定 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() 方法,清除目前的使用者憑證狀態,並重設登入程序的內部狀態。