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

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

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

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

איך זה עובד

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

הגדרה

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

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

  1. פותחים את Android SDK Manager.
  1. בקטע SDK Tools, מאתרים את Google Play Services.

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

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

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

    buildscript {
        repositories {
            google()
        }
    }
    
    allprojects {
        repositories {
            google()
        }
    }
    
  2. מוסיפים את יחסי התלות של ה-API 'קישור ל-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. הפעלת הכוונה 'המתנה לכניסה'

    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.

באמצעות השדות email, email_verified ו-hd אפשר לקבוע אם Google מארחת כתובת אימייל מסוימת, והיא מהימן. במקרים שבהם Google היא גורם מוסמך, ידוע שהמשתמש הוא הבעלים החוקיים של החשבון, תוכל לדלג על הזנת סיסמה שיטות לאתגר.

מקרים שבהם Google היא מהימן:

  • ל-email יש סיומת @gmail.com. זהו חשבון Gmail.
  • email_verified מוגדר כ-True ו-hd מוגדר. זהו חשבון G Suite.

משתמשים יכולים להירשם לחשבונות Google בלי להשתמש ב-Gmail או ב-G Suite. מתי email לא מכיל סיומת @gmail.com ו-hd חסר, Google אינה מומלץ לאמת סיסמה או סיסמה או שיטות אחרות לאימות למשתמש. email_verified יכול להיות גם נכון כי Google אימתה בהתחלה את משתמש כשחשבון 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.");
  }

השיטה GoogleIdTokenVerifier.verify() מאמתת את החתימה של JWT, את ההצהרה aud, את ההצהרה iss ואת ההצהרה exp.

אם אתם צריכים לאמת שהאסימון המזהה מייצג Google Workspace או Cloud חשבון ארגוני, אפשר לאמת את הצהרת הזמן לתפוגה hd על ידי בדיקת שם הדומיין שהוחזרו באמצעות השיטה Payload.getHostedDomain().

קריאה לנקודת הקצה של Tokeninfo

דרך קלה לאמת חתימה של אסימון מזהה לצורך ניפוי באגים היא משתמשים בנקודת הקצה 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.