導入「使用 Google 帳戶登入」功能

本指南說明如何導入「使用 Google 帳戶登入」,並涵蓋下列步驟:

  • 為應用程式新增依附元件。
  • 例項化 CredentialManager
  • 建立底部功能表流程。
  • 建立按鈕流程。
  • 處理登入回應。
  • 處理錯誤。
  • 處理登出作業。

為應用程式新增依附元件

在模組的 build.gradle 檔案中,使用最新版本的 Credential Manager、Play 服務驗證googleid 宣告依附元件:

Kotlin

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

Groovy

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

例項化 Credential Manager

使用應用程式或活動情境建立 CredentialManager 物件。

// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)

建立底部功能表流程

底部功能表是 Credential Manager 的內建 UI。使用這個 UI,即可在所有驗證方法 (例如密碼、密碼金鑰和「使用 Google 帳戶登入」) 中提供一致的體驗。

為先前授權的帳戶設定登入要求

使用 GetGoogleIdOption 嘗試發出 Google 登入要求,以擷取使用者的 Google ID 權杖。

下列程式碼片段會檢查帳戶是否為授權帳戶。

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
    .setFilterByAuthorizedAccounts(true)
    .setServerClientId(WEB_CLIENT_ID)
    .setAutoSelectEnabled(true)
    .setNonce(generateSecureRandomNonce())
    .build()

要求 googleIdOption 物件的設定方式如下:

  • 篩選先前授權的帳戶:如要擷取先前用於登入應用程式的授權帳戶,請將 setFilterByAuthorizedAccounts 設為 true

    請注意,setFilterByAuthorizedAccounts 的預設值為 true,這表示底部功能表 UI 的預設行為是只顯示先前已授權的帳戶。

  • 設定伺服器用戶端 ID:設定 setServerClientId 參數。webClientId 是您在完成必要條件時,為 Google Cloud 專案中的 OAuth 設定的網路用戶端 ID。

  • 啟用自動登入 (選用):如要為回訪使用者啟用自動登入,請使用 setAutoSelectEnabled(true)setFilterByAuthorizedAccounts(true)。如果應用程式使用者先前已登入,這項功能可避免不必要的阻礙。

    只有在符合下列條件時,才能自動登入:

    • 裝置上只有一個已授權帳戶,且該帳戶先前曾用於登入裝置上的應用程式。裝置上有多個已授權帳戶時,系統會停用自動登入功能。
    • 使用者在先前的工作階段中,並未明確登出應用程式。
    • 使用者未在 Google 帳戶設定中停用自動登入功能。
  • 設定 Nonce (選用):如要啟用強化安全性,請為伺服器端驗證設定 Nonce。為防止重播攻擊,您可以透過 setNonce() 納入隨機數,進行伺服器端驗證。請確保伺服器端程式碼會驗證要求和回應隨機碼是否相同。

    如要產生 Nonce,請使用類似於下列函式的函式,產生指定長度的加密編譯強隨機 Nonce,並使用 Base64 編碼:

fun generateSecureRandomNonce(byteLength: Int = 32): String {
    val randomBytes = ByteArray(byteLength)
    SecureRandom().nextBytes(randomBytes)
    return Base64.encodeToString(randomBytes, Base64.NO_WRAP or Base64.URL_SAFE or Base64.NO_PADDING)
}

要求登入

呼叫 getCredential 方法,檢查使用者在裝置上是否有已授權的帳戶:

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

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

如果沒有可用的授權帳戶,請設定登入要求

如果裝置上沒有應用程式的授權使用者,CredentialManager會傳回 NoCredentialException。在這種情況下,請停用授權帳戶篩選器,讓使用者能透過其他帳戶註冊。

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
    .setFilterByAuthorizedAccounts(false)
    .setServerClientId(WEB_CLIENT_ID)
    .setNonce(generateSecureRandomNonce())
    .build()

接著,請要求登入,與授權帳戶的操作方式相同。

建立按鈕流程

如要讓使用者在下列情況下使用「使用 Google 帳戶登入」功能,請使用按鈕:

  • 使用者關閉了「Credential Manager」底部功能表 UI。
  • 裝置上沒有 Google 帳戶。
  • 裝置上的現有帳戶需要重新驗證。

建立按鈕 UI

雖然可以使用 Jetpack Compose 按鈕完成這項操作,但建議使用「使用 Google 帳戶登入品牌宣傳指南」頁面中預先核准的品牌圖示。

建立登入流程

使用 GetSignInWithGoogleOption 建立 Google 登入要求,以擷取 Google ID 權杖。

val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption.Builder(
    serverClientId = WEB_CLIENT_ID
).setNonce(generateSecureRandomNonce())
    .build()

接著,請要求登入,方式與底部功能表 UI 相同。

為底部功能表和按鈕建立共用登入函式

如要處理登入作業,請完成下列步驟:

  1. 使用 CredentialManager 的 getCredential() 函式。如果回應成功,請擷取 CustomCredential,該值應為 GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL 類型。
  2. 使用 GoogleIdTokenCredential.createFrom() 方法將物件轉換為 GoogleIdTokenCredential

  3. 在信賴憑證者伺服器上驗證憑證。

  4. 請確保您能妥善處理錯誤。

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

    when (credential) {
        is CustomCredential -> {
            if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
                try {
                    // Use googleIdTokenCredential and extract the ID for server-side validation.
                    val googleIdTokenCredential = GoogleIdTokenCredential
                        .createFrom(credential.data)
                } catch (e: GoogleIdTokenParsingException) {
                    Log.e(TAG, "Received an invalid google id token response", e)
                }
            } else {
                // Catch any unrecognized 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 帳戶,並決定從其他帳戶登入。您可以在設定頁面等位置提供這項資訊。

憑證提供者可能會儲存有效憑證工作階段,並用於限制日後登入要求可用的登入選項。舉例來說,系統可以優先使用有效憑證,而非其他可用憑證。

當使用者登出應用程式時,請呼叫 API clearCredentialState() 方法,從所有憑證提供者清除目前的使用者憑證狀態。這會通知所有憑證供應商,應清除指定應用程式儲存的任何憑證工作階段,讓使用者下次登入時能選擇完整登入選項。