הטמעה של 'כניסה באמצעות חשבון Google'

במדריך הזה מוסבר איך מטמיעים את התכונה 'כניסה באמצעות חשבון Google'. המדריך כולל את השלבים הבאים:

  • מוסיפים יחסי תלות לאפליקציה.
  • יצירת מופע של CredentialManager.
  • יוצרים את תהליך הגיליון התחתון.
  • יוצרים את תהליך העבודה של הכפתור.
  • טיפול בתגובת הכניסה.
  • לטפל בשגיאות.
  • טיפול ביציאה מהחשבון.

הוספת יחסי תלות לאפליקציה

בקובץ build.gradle של המודול, מצהירים על יחסי תלות באמצעות הגרסה העדכנית של Credential Manager,‏ Play Services Auth ו-googleid:

Kotlin

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

Groovy

dependencies {
    implementation "androidx.credentials:credentials:1.6.0-rc02"
    implementation "androidx.credentials:credentials-play-services-auth:1.6.0-rc02"
    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)

יצירת התהליך של הגיליון התחתון

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

הגדרת בקשת הכניסה לחשבונות שכבר אושרו

מנסים לשלוח בקשה לכניסה באמצעות חשבון Google עם GetGoogleIdOption כדי לאחזר את אסימון הזהות של המשתמש ב-Google.

קטעי הקוד הבאים בודקים אם החשבון הוא חשבון מורשה.

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

אובייקט הבקשה googleIdOption מוגדר באופן הבא:

  • סינון חשבונות שאושרו בעבר: כדי לאחזר את החשבונות שאושרו וששימשו בעבר לכניסה לאפליקציה, מגדירים את setFilterByAuthorizedAccounts ל-true.

    שימו לב שערך ברירת המחדל של setFilterByAuthorizedAccounts הוא true, כלומר התנהגות ברירת המחדל של ממשק המשתמש של גיליון תחתון היא להציג רק חשבונות שאושרו בעבר.

  • מגדירים את מזהה הלקוח של השרת: מגדירים את הפרמטר setServerClientId. ‫webClientId הוא מזהה לקוח האינטרנט שהגדרתם עבור OAuth בפרויקט Google Cloud שלכם במהלך השלמת הדרישות המוקדמות.

  • הפעלת כניסה אוטומטית (אופציונלי): כדי להפעיל כניסה אוטומטית למשתמשים חוזרים, משתמשים ב-setAutoSelectEnabled(true) וב-setFilterByAuthorizedAccounts(true). למשתמשי האפליקציה, זה ימנע חיכוך מיותר אם הם כבר נכנסו לחשבון בעבר.

    כניסה אוטומטית אפשרית רק אם מתקיימים הקריטריונים הבאים:

    • במכשיר יש רק חשבון מורשה אחד, ובעבר השתמשו בחשבון המורשה הזה כדי להיכנס לאפליקציה במכשיר. אם יש במכשיר כמה חשבונות מורשים, הכניסה האוטומטית מושבתת.
    • המשתמש לא יצא במפורש מהאפליקציה במהלך ההפעלה הקודמת.
    • המשתמש לא השבית את הכניסה האוטומטית בהגדרות חשבון Google.
  • הגדרת צופן חד-פעמי (אופציונלי): כדי להפעיל אבטחה משופרת, מגדירים צופן חד-פעמי לאימות בצד השרת. כדי למנוע מתקפות שידור חוזר, אפשר לכלול ערך חד-פעמי (nonce) לאימות בצד השרת באמצעות setNonce(). מוודאים שהקוד בצד השרת מאמת את זהות הערכים של ה-nonce בבקשה ובתגובה.

    כדי ליצור את ה-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)
}

בקשת כניסה

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

יצירת ממשק המשתמש של הכפתור

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

יצירת תהליך הכניסה

יוצרים בקשה לכניסה באמצעות חשבון Google עם GetSignInWithGoogleOption כדי לאחזר אסימון מזהה של Google.

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

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

יצירת פונקציית הכניסה המשותפת לגיליון התחתון ולכפתור

כדי לטפל בכניסה, פועלים לפי השלבים הבאים:

  1. משתמשים בפונקציה getCredential() של CredentialManager. אם התגובה מוצלחת, מחלצים את CustomCredential, שצריך להיות מסוג GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL.
  2. ממירים את האובייקט ל-GoogleIdTokenCredential באמצעות השיטה GoogleIdTokenCredential.createFrom().

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