כניסה באמצעות מפתח גישה

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

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

סקירה כללית

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

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

  1. קבלת אפשרויות לבקשת פרטי כניסה מהשרת: שולחים בקשה מהאפליקציה לשרת האימות כדי להתחיל את תהליך הכניסה באמצעות מפתח הגישה. מהשרת, שולחים את האפשרויות הנדרשות כדי לקבל את פרטי המפתח הציבורי, וגם אתגר ייחודי.
  2. יצירת האובייקט שנדרש כדי לקבל את פרטי הכניסה של המפתח הציבורי: עוטפים את האפשרויות שנשלחו מהשרת באובייקט GetPublicKeyCredentialOption
  3. ‫(אופציונלי) הכנה של getCredential: ב-Android מגרסה 14 ואילך, אפשר להקטין את זמן האחזור על ידי הצגת בורר החשבונות באמצעות השיטה prepareGetCredential() לפני הקריאה ל-getCredential().
  4. הפעלת תהליך הכניסה: קוראים לשיטת getCredential() כדי שהמשתמש ייכנס לחשבון
  5. טיפול בתגובה: טיפול בכל אחת מהתגובות האפשריות של פרטי הכניסה.
  6. טיפול בחריגים: חשוב לוודא שאתם מטפלים בחריגים בצורה מתאימה.

1. קבלת אפשרויות לבקשת פרטי כניסה מהשרת

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

האפשרויות אמורות להיראות כך:

{
  "challenge": "<your app challenge>",
  "allowCredentials": [],
  "rpId": "<your app server domain>"
}

מידע נוסף על השדות זמין בפוסט בבלוג בנושא כניסה באמצעות מפתח גישה.

2. יצירת האובייקט שנדרש כדי לקבל את פרטי המפתח הציבורי

באפליקציה, משתמשים באפשרויות כדי ליצור אובייקט GetPublicKeyCredentialOption. בדוגמה הבאה, requestJson מייצג את האפשרויות שנשלחות על ידי השרת.

// Get password logins from the credential provider on the user's device.
val getPasswordOption = GetPasswordOption()

// Get passkeys from the credential provider on the user's device.
val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(
    requestJson = requestJson
)

לאחר מכן, עוטפים את GetPublicKeyCredentialOption באובייקט GetCredentialRequest.

val credentialRequest = GetCredentialRequest(
    // Include all the sign-in options that your app supports.
    listOf(getPasswordOption, getPublicKeyCredentialOption),
    // Defines whether you prefer to use only immediately available
    // credentials or hybrid credentials.
    preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials
)

3. אופציונלי: הפחתת זמן הטעינה של הכניסה

ב-Android מגרסה 14 ואילך, אפשר לקצר את זמן האחזור כשמציגים את בורר החשבונות באמצעות השיטה prepareGetCredential() לפני הקריאה ל-getCredential().

השיטה prepareGetCredential() מחזירה אובייקט PrepareGetCredentialResponse שנשמר במטמון. כך, השיטה getCredential() בשלב הבא יכולה להציג את בורר החשבונות עם הנתונים שבמטמון.

coroutineScope {
    val response = credentialManager.prepareGetCredential(
        GetCredentialRequest(
            listOf(
                // Include all the sign-in options that your app supports
                getPublicKeyCredentialOption, 
                getPasswordOption
            )
        )
    )
}

4. הפעלת תהליך הכניסה

קוראים לשיטה getCredential() כדי להציג למשתמש את בורר החשבונות. אפשר להשתמש בקטע הקוד הבא כהפניה להפעלת תהליך הכניסה:

coroutineScope {
    try {
        result = credentialManager.getCredential(
            // Use an activity-based context to avoid undefined system UI
            // launching behavior.
            context = activityContext,
            request = credentialRequest
        )
        handleSignIn(result)
    } catch (e: GetCredentialException) {
        // Handle failure
    }
}

5. טיפול בתגובה

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

fun handleSignIn(result: GetCredentialResponse) {
    // Handle the successfully returned credential.
    val credential = result.credential

    when (credential) {
        is PublicKeyCredential -> {
            val responseJson = credential.authenticationResponseJson
            // Share responseJson i.e. a GetCredentialResponse on your server to
            // validate and  authenticate
        }

        is PasswordCredential -> {
            val username = credential.id
            val password = credential.password
            // Use id and password to send to your server to validate
            // and authenticate
        }

        is CustomCredential -> {
            // If you are also using any external sign-in libraries, parse them
            // here with the utility functions provided.
            if (credential.type == ExampleCustomCredential.TYPE) {
                try {
                    val ExampleCustomCredential =
                        ExampleCustomCredential.createFrom(credential.data)
                    // Extract the required credentials and complete the authentication as per
                    // the federated sign in or any external sign in library flow
                } catch (e: ExampleCustomCredential.ExampleCustomCredentialParsingException) {
                    // Unlikely to happen. If it does, you likely need to update the dependency
                    // version of your external sign-in library.
                    Log.e(TAG, "Failed to parse an ExampleCustomCredential", e)
                }
            } else {
                // Catch any unrecognized custom credential type here.
                Log.e(TAG, "Unexpected type of credential")
            }
        }
        else -> {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential")
        }
    }
}

האישור PublicKeyCredential שמוחזר מהאימות הוא למעשה הצהרה חתומה, והמבנה שלו הוא כזה:

{
  "id": "<credential ID>",
  "type": "public-key",
  "rawId": "<raw credential ID>",
  "response": {
    "clientDataJSON": "<signed client data containing challenge>",
    "authenticatorData": "<authenticator metadata>",
    "signature": "<digital signature to be verified>",
    "userHandle": "<user ID from credential registration>"
  }
}

בשרת, צריך לאמת את פרטי הכניסה. מידע נוסף זמין במאמר בנושא אימות המשתמש והתחברות.

6. טיפול בחריגים

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

coroutineScope {
    try {
        result = credentialManager.getCredential(
            context = activityContext,
            request = credentialRequest
        )
    } catch (e: GetCredentialException) {
        Log.e("CredentialManager", "No credential available", e)
    }
}

השלבים הבאים