התכונה 'כניסה לחשבון מקושר' מפעילה את התכונה כניסה בהקשה אחת באמצעות Google למשתמשים שחשבון Google שלהם כבר מקושר לשירות שלכם. כך ניתן לשפר את החוויה של המשתמשים, כי הם יכולים להיכנס בלחיצה אחת בלי להזין שוב את שם המשתמש והסיסמה שלהם. הוא גם מפחית את הסיכויים שהמשתמשים ייצרו חשבונות כפולים בשירות שלך.
האפשרות 'כניסה לחשבון מקושר' זמינה כחלק מתהליך הכניסה בהקשה אחת ל-Android. כלומר, אין צורך לייבא ספרייה נפרדת אם התכונה One Tap כבר מופעלת באפליקציה.
במסמך הזה נסביר איך לשנות את האפליקציה ל-Android כך שתתמוך ב'כניסה לחשבון מקושר'.
איך זה עובד
- הבעת הסכמה להצגת חשבונות מקושרים בתהליך הכניסה בהקשה אחת.
- אם המשתמש נכנס לחשבון Google וקישר את חשבון Google שלו לחשבון שלו בשירות שלכם, יוחזר אסימון מזהה לחשבון המקושר.
- מוצגת למשתמש הודעת כניסה בהקשה אחת עם אפשרות להיכנס לשירות שלכם באמצעות החשבון המקושר שלו.
- אם המשתמש בוחר להמשיך עם החשבון המקושר, האסימון המזהה של המשתמש מוחזר לאפליקציה. צריך להתאים אותו לאסימון שנשלח לשרת שלכם בשלב 2 כדי לזהות את המשתמש המחובר.
הגדרה
הגדרת סביבת הפיתוח
אפשר לקבל את הגרסה העדכנית ביותר של Google Play Services אצל מארח הפיתוח:
- פותחים את מנהל ה-SDK של Android.
בקטע SDK Tools, מאתרים את Google Play Services.
אם הסטטוס של החבילות האלה אינו Installed, בוחרים את שתיהן ולוחצים על Install Packages (התקנת חבילות).
הגדרת האפליקציה
בקובץ
build.gradle
ברמת הפרויקט, כוללים את מאגר Maven של Google בקטעbuildscript
וגם בקטעallprojects
.buildscript { repositories { google() } } allprojects { repositories { google() } }
מוסיפים את יחסי התלות של ה-API "Link with Google" לקובץ gradle ברמת האפליקציה של המודול, בדרך כלל
app/build.gradle
:dependencies { implementation 'com.google.android.gms:play-services-auth:21.2.0' }
שינוי האפליקציה ל-Android כך שתתמוך בכניסה לחשבון מקושר
בסיום תהליך הכניסה לחשבון המקושר, אסימון מזהה מוחזר לאפליקציה. צריך לאמת את התקינות של האסימון המזהה לפני כניסת המשתמש לחשבון.
בדוגמת הקוד הבאה מפורטים השלבים לשחזור, אימות האסימון המזהה, ולאחר מכן כניסה של המשתמש.
צריך ליצור פעילות כדי לקבל את התוצאה של כוונת הכניסה
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()
הפעלה של Intent בהמתנה לכניסה
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); });
אימות התקינות של האסימון המזהה
כדי לוודא שהאסימון תקין, צריך לוודא שהקריטריונים הבאים מתקיימים:
- Google חתומה באופן תקין על האסימון המזהה. יש להשתמש במפתחות הציבוריים של Google
(זמינים בפורמט
JWK או
PEM)
לאימות החתימה של האסימון. המפתחות האלה עוברים רוטציה באופן קבוע. כדאי לבדוק את הכותרת
Cache-Control
בתשובה כדי לקבוע מתי צריך לאחזר אותם שוב. - הערך של
aud
באסימון המזהה שווה לאחד ממזהי הלקוח של האפליקציה שלך. הבדיקה הזו נדרשת כדי למנוע אסימונים מזהים שהונפקו לאפליקציה זדונית שנעשה בה שימוש כדי לגשת לנתונים של אותו משתמש בשרת הקצה העורפי של האפליקציה. - הערך של
iss
באסימון המזהה שווה ל-accounts.google.com
או ל-https://accounts.google.com
. - מועד התפוגה (
exp
) של האסימון המזהה לא חלף. - כדי לוודא שהאסימון המזהה מייצג חשבון ארגוני ב-Google Workspace או ב-Cloud, אפשר לבדוק את ההצהרה
hd
, שבה מצוין הדומיין המתארח של המשתמש. צריך להשתמש באפשרות הזו כשמגבילים את הגישה למשאב רק לחברים בדומיינים מסוימים. אם לא ההצהרה הזו לא תשויך, החשבון לא שייך לדומיין באירוח של Google.
במקום לכתוב קוד משלך לביצוע שלבי האימות האלה, מומלץ מאוד להשתמש בספריית לקוח של Google API לפלטפורמה שלך, או בספריית JWT לשימוש כללי. לפיתוח ולניפוי באגים, אפשר לקרוא לנקודת הקצה של האימות tokeninfo
.
שימוש בספריית לקוח של Google API
השימוש בספריית הלקוח של Java Google API הוא הדרך המומלצת לאמת אסימונים מזהים של Google בסביבת הייצור.
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.");
}
ה-method GoogleIdTokenVerifier.verify()
מאמתת את חתימת ה-JWT, את ההצהרה aud
, את ההצהרה iss
ואת ההצהרהexp
.
אם אתם צריכים לוודא שהאסימון המזהה מייצג חשבון ארגוני ב-Google Workspace או ב-Cloud, תוכלו לאמת את ההצהרה hd
על ידי בדיקת שם הדומיין שהוחזר באמצעות השיטה Payload.getHostedDomain()
.
מתבצעת קריאה לנקודת הקצה של פרטי האסימון
דרך קלה לאמת חתימה של אסימון מזהה לצורך ניפוי באגים היא
להשתמש בנקודת הקצה tokeninfo
. הפעלה של נקודת הקצה הזו כרוכה בבקשת רשת נוספת שמבצעת את רוב תהליך האימות בזמן הבדיקה של האימות והחילוץ של המטען הייעודי (payload) בקוד שלך. היא לא מתאימה לשימוש בקוד של סביבת הייצור כי הבקשות עשויות להיות מווסתות או שעשויות לכלול שגיאות מדי פעם.
כדי לאמת אסימון מזהה באמצעות נקודת הקצה של tokeninfo
, יש לשלוח בקשת HTTPS
POST או GET לנקודת הקצה, ולהעביר את האסימון המזהה
בפרמטר id_token
.
לדוגמה, כדי לאמת את האסימון "XYZ123", יש לשלוח את בקשת GET הבאה:
https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123
אם האסימון נחתם כראוי וההצהרות של iss
ו-exp
כוללות את הערכים הצפויים, תתקבל תגובת HTTP 200, שבה הגוף
מכיל את ההצהרות על אסימונים בפורמט JSON.
זוהי דוגמה לתשובה:
{ // 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" }
כדי לוודא שהאסימון המזהה מייצג חשבון Google Workspace, אפשר לבדוק את
ההצהרה hd
, המציינת את הדומיין המתארח של המשתמש. צריך להשתמש באפשרות הזו כשמגבילים את הגישה למשאב רק לחברים בדומיינים מסוימים. אם לא ההצהרה הזו לא תיכלל,
המשמעות היא שהחשבון לא שייך לדומיין מתארח ב-Google Workspace.