כניסה לחשבון מקושר עבור Android

התכונה 'כניסה לחשבון מקושר' מפעילה את התכונה כניסה בהקשה אחת באמצעות Google למשתמשים שחשבון Google שלהם כבר מקושר לשירות שלכם. כך ניתן לשפר את החוויה של המשתמשים, כי הם יכולים להיכנס בלחיצה אחת בלי להזין שוב את שם המשתמש והסיסמה שלהם. הוא גם מפחית את הסיכויים שהמשתמשים ייצרו חשבונות כפולים בשירות שלך.

האפשרות 'כניסה לחשבון מקושר' זמינה כחלק מתהליך הכניסה בהקשה אחת ל-Android. כלומר, אין צורך לייבא ספרייה נפרדת אם התכונה One Tap כבר מופעלת באפליקציה.

במסמך הזה נסביר איך לשנות את האפליקציה ל-Android כך שתתמוך ב'כניסה לחשבון מקושר'.

איך זה עובד

  1. הבעת הסכמה להצגת חשבונות מקושרים בתהליך הכניסה בהקשה אחת.
  2. אם המשתמש נכנס לחשבון Google וקישר את חשבון Google שלו לחשבון שלו בשירות שלכם, יוחזר אסימון מזהה לחשבון המקושר.
  3. מוצגת למשתמש הודעת כניסה בהקשה אחת עם אפשרות להיכנס לשירות שלכם באמצעות החשבון המקושר שלו.
  4. אם המשתמש בוחר להמשיך עם החשבון המקושר, האסימון המזהה של המשתמש מוחזר לאפליקציה. צריך להתאים אותו לאסימון שנשלח לשרת שלכם בשלב 2 כדי לזהות את המשתמש המחובר.

הגדרה

הגדרת סביבת הפיתוח

אפשר לקבל את הגרסה העדכנית ביותר של Google Play Services אצל מארח הפיתוח:

  1. פותחים את מנהל ה-SDK של Android.
  1. בקטע SDK Tools, מאתרים את Google Play Services.

  2. אם הסטטוס של החבילות האלה אינו Installed, בוחרים את שתיהן ולוחצים על Install Packages (התקנת חבילות).

הגדרת האפליקציה

  1. בקובץ build.gradle ברמת הפרויקט, כוללים את מאגר Maven של Google בקטע buildscript וגם בקטע allprojects.

    buildscript {
        repositories {
            google()
        }
    }
    
    allprojects {
        repositories {
            google()
        }
    }
    
  2. מוסיפים את יחסי התלות של ה-API "Link with Google" לקובץ gradle ברמת האפליקציה של המודול, בדרך כלל app/build.gradle:

    dependencies {
      implementation 'com.google.android.gms:play-services-auth:21.2.0'
    }
    

שינוי האפליקציה ל-Android כך שתתמוך בכניסה לחשבון מקושר

בסיום תהליך הכניסה לחשבון המקושר, אסימון מזהה מוחזר לאפליקציה. צריך לאמת את התקינות של האסימון המזהה לפני כניסת המשתמש לחשבון.

בדוגמת הקוד הבאה מפורטים השלבים לשחזור, אימות האסימון המזהה, ולאחר מכן כניסה של המשתמש.

  1. צריך ליצור פעילות כדי לקבל את התוצאה של כוונת הכניסה

    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")
        }
    });
    
  2. יצירה של בקשת הכניסה

    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()
    
  3. הפעלה של 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);
    });
    

אימות התקינות של האסימון המזהה

如需验证令牌是否有效,请确保满足以下条件:

  • ID 令牌由 Google 正确签名。使用 Google 的公钥(以 JWKPEM 格式提供)来验证令牌的签名。这些密钥会定期轮替;请检查响应中的 Cache-Control 标头,以确定何时应再次检索它们。
  • ID 令牌中 aud 的值等于应用的某个客户端 ID。通过进行这项检查,可以防止向恶意应用颁发的 ID 令牌用于访问应用后端服务器上同一用户的数据。
  • ID 令牌中 iss 的值等于 accounts.google.comhttps://accounts.google.com
  • ID 令牌的过期时间 (exp) 尚未过去。
  • 如果您需要验证该 ID 令牌代表的是 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.