透過已連結帳戶的登入機制,如果使用者已將自己的 Google 帳戶與您的服務連結,他們就能啟用One Tap Sign In Google 帳戶功能。如此一來,使用者只要按一下滑鼠就能登入,不必重新輸入使用者名稱和密碼,享有更棒的體驗。同時降低使用者在服務中建立重複帳戶的可能性。
Android 適用的 One Tap 登入流程提供已連結帳戶登入功能。這表示如果應用程式已啟用 One Tap 功能,就不需要匯入獨立的程式庫。
本文說明如何修改 Android 應用程式,以支援已連結帳戶登入功能。
運作方式
- 您可以選擇在 One Tap 登入流程中顯示已連結帳戶。
- 如果使用者已登入 Google 帳戶,並將自己的 Google 帳戶連結至您服務中的帳戶,系統就會傳回已連結帳戶的 ID 權杖。
- 使用者會看到 One Tap 登入提示,並提供選項,可透過已連結的帳戶登入服務。
- 如果使用者選擇繼續使用已連結帳戶,系統會將使用者的 ID 權杖傳回應用程式。您可以將此權杖與在步驟 2 中傳送至伺服器的權杖進行比對,以識別登入的使用者。
設定
設定開發環境
在開發主機上取得最新的 Google Play 服務:
在「SDK Tools」下方,找出「Google Play services」。
如果未安裝這些套件的狀態,請同時選取這些套件並按一下「Install Packages」(安裝套件)。
設定應用程式
在專案層級的
build.gradle
檔案中,請同時在buildscript
和allprojects
區段納入 Google 的 Maven 存放區。buildscript { repositories { google() } } allprojects { repositories { google() } }
將「Link with Google」API 的依附元件新增至模組的應用程式層級 Gradle 檔案,通常為
app/build.gradle
:dependencies { implementation 'com.google.android.gms:play-services-auth:21.2.0' }
修改 Android 應用程式,以支援已連結帳戶登入功能
已連結帳戶登入流程結束時,系統會將 ID 權杖傳回至您的應用程式。在使用者登入前,應先驗證 ID 權杖的完整性。
下列程式碼範例會詳細說明擷取步驟、驗證 ID 權杖,然後讓使用者登入。
建立活動來接收登入意圖的結果
Kotlin
private val activityResultLauncher = registerForActivityResult( ActivityResultContracts.StartIntentSenderForResult()) { result -> if (result.resultCode == RESULT_OK) { try { val signInCredentials = Identity.signInClient(this) .signInCredentialFromIntent(result.data) // Review the Verify the integrity of the ID token section for // details on how to verify the ID token verifyIdToken(signInCredential.googleIdToken) } catch (e: ApiException) { Log.e(TAG, "Sign-in failed with error code:", e) } } else { Log.e(TAG, "Sign-in failed") } }
Java
private final ActivityResultLauncher<IntentSenderResult> activityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartIntentSenderForResult(), result -> { If (result.getResultCode() == RESULT_OK) { try { SignInCredential signInCredential = Identity.getSignInClient(this) .getSignInCredentialFromIntent(result.getData()); verifyIdToken(signInCredential.getGoogleIdToken()); } catch (e: ApiException ) { Log.e(TAG, "Sign-in failed with error:", e) } } else { Log.e(TAG, "Sign-in failed") } });
建立登入要求
Kotlin
private val tokenRequestOptions = GoogleIdTokenRequestOptions.Builder() .supported(true) // Your server's client ID, not your Android client ID. .serverClientId(getString("your-server-client-id") .filterByAuthorizedAccounts(true) .associateLinkedAccounts("service-id-of-and-defined-by-developer", scopes) .build()
Java
private final GoogleIdTokenRequestOptions tokenRequestOptions = GoogleIdTokenRequestOptions.Builder() .setSupported(true) .setServerClientId("your-service-client-id") .setFilterByAuthorizedAccounts(true) .associateLinkedAccounts("service-id-of-and-defined-by-developer", scopes) .build()
啟動登入待處理意圖
Kotlin
Identity.signInClient(this) .beginSignIn( BeginSignInRequest.Builder() .googleIdTokenRequestOptions(tokenRequestOptions) .build()) .addOnSuccessListener{result -> activityResultLauncher.launch(result.pendingIntent.intentSender) } .addOnFailureListener {e -> Log.e(TAG, "Sign-in failed because:", e) }
Java
Identity.getSignInClient(this) .beginSignIn( BeginSignInRequest.Builder() .setGoogleIdTokenRequestOptions(tokenRequestOptions) .build()) .addOnSuccessListener(result -> { activityResultLauncher.launch( result.getPendingIntent().getIntentSender()); }) .addOnFailureListener(e -> { Log.e(TAG, "Sign-in failed because:", e); });
驗證 ID 權杖的完整性
如要驗證權杖是否有效,請確認符合下列條件:
- Google 正確簽署 ID 權杖。使用 Google 的公開金鑰 (提供 JWK 或 PEM 格式) 驗證權杖的簽名。這些金鑰會定期輪替;請檢查回應中的
Cache-Control
標頭,判斷何時應再次擷取這些金鑰。 - ID 權杖中的
aud
值等於應用程式的其中一個用戶端 ID。必須進行這項檢查,以免核發至惡意應用程式的 ID 權杖,藉此存取應用程式後端伺服器上的相同使用者相關資料。 - ID 權杖中的
iss
值等於accounts.google.com
或https://accounts.google.com
。 - ID 權杖的到期時間 (
exp
) 尚未超過。 - 如果您需要驗證 ID 權杖代表 Google Workspace 或 Cloud 機構帳戶,可以查看
hd
憑證附加資訊,這就表示使用者的託管網域。將資源的存取權限制為僅限特定網域的成員存取時,必須使用這個屬性。缺少這項聲明,代表帳戶不屬於 Google 代管網域。
比起自行編寫程式碼來執行這些驗證步驟,我們強烈建議您使用適用於您平台的 Google API 用戶端程式庫,或一般用途的 JWT 程式庫。如要進行開發和偵錯,您可以呼叫我們的 tokeninfo
驗證端點。
使用 Google API 用戶端程式庫
如要在實際工作環境中驗證 Google ID 權杖,建議您使用 Java Google API 用戶端程式庫。
Java
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
...
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
// Specify the CLIENT_ID of the app that accesses the backend:
.setAudience(Collections.singletonList(CLIENT_ID))
// Or, if multiple clients access the backend:
//.setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3))
.build();
// (Receive idTokenString by HTTPS POST)
GoogleIdToken idToken = verifier.verify(idTokenString);
if (idToken != null) {
Payload payload = idToken.getPayload();
// Print user identifier
String userId = payload.getSubject();
System.out.println("User ID: " + userId);
// Get profile information from payload
String email = payload.getEmail();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
String pictureUrl = (String) payload.get("picture");
String locale = (String) payload.get("locale");
String familyName = (String) payload.get("family_name");
String givenName = (String) payload.get("given_name");
// Use or store profile information
// ...
} else {
System.out.println("Invalid ID token.");
}
GoogleIdTokenVerifier.verify()
方法會驗證 JWT 簽名、aud
憑證附加資訊、iss
憑證附加資訊及 exp
憑證附加資訊。
如需驗證 ID 權杖是否代表 Google Workspace 或 Cloud 機構帳戶,您可以透過檢查 Payload.getHostedDomain()
方法傳回的網域名稱,驗證 hd
憑證附加資訊。
呼叫 Tokeninfo 端點
如要驗證偵錯所需的 ID 權杖簽章,最簡單的方法是使用 tokeninfo
端點。呼叫這個端點需要額外的網路要求,這類要求會為您執行大部分的驗證作業,同時在您自己的程式碼中測試適當的驗證與酬載擷取作業。不適用於實際工作環境程式碼中,因為要求可能會受到限製或發生間歇性錯誤。
如要使用 tokeninfo
端點驗證 ID 權杖,請向端點發出 HTTPS POST 或 GET 要求,並在 id_token
參數中傳遞 ID 權杖。
舉例來說,如要驗證權杖「XYZ123」,請提出下列 GET 要求:
https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123
如果權杖已正確簽署,且 iss
和 exp
憑證附加資訊含有預期值,您會收到「HTTP 200」回應,其中主體包含 JSON 格式的 ID 權杖憑證附加資訊。以下是回應範例:
{ // These six fields are included in all Google ID Tokens. "iss": "https://accounts.google.com", "sub": "110169484474386276334", "azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com", "aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com", "iat": "1433978353", "exp": "1433981953", // These seven fields are only included when the user has granted the "profile" and // "email" OAuth scopes to the application. "email": "testuser@gmail.com", "email_verified": "true", "name" : "Test User", "picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg", "given_name": "Test", "family_name": "User", "locale": "en" }
如果您需要驗證 ID 權杖是否代表 Google Workspace 帳戶,可以查看 hd
憑證附加資訊,這會指出使用者的代管網域。將資源的存取權限制為僅限特定網域的成員存取時,必須使用這個屬性。若缺少這項聲明,代表帳戶不屬於 Google Workspace 代管的網域。