בקשה לקבלת אישור דיגיטלי

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

‫Verifier API נתמך ב-Android מגרסה 9 (רמת API‏ 28) ואילך.

הטמעה

כדי לאמת את פרטי הכניסה הדיגיטליים בפרויקט Android:

  1. מוסיפים יחסי תלות לסקריפט ה-build של האפליקציה ומפעילים את המחלקה CredentialManager.
  2. יוצרים בקשה לתעודה דיגיטלית ומשתמשים בה כדי לאתחל DigitalCredentialOption, ואז יוצרים את ה-GetCredentialRequest.
  3. מפעילים את התהליך של getCredential עם הבקשה שנוצרה כדי לקבל GetCredentialResponse או לטפל בחריגות שעלולות להתרחש. אחרי שהתשובה מתקבלת, מאמתים אותה.

הוספת יחסי תלות והפעלה

מוסיפים את יחסי התלות הבאים לסקריפט ה-build של Gradle:

dependencies {
    implementation("androidx.credentials:credentials:1.6.0-beta01")
    implementation("androidx.credentials:credentials-play-services-auth:1.6.0-beta01")
}

בשלב הבא, מאתחלים מופע של המחלקה CredentialManager.

val credentialManager = CredentialManager.create(context)

יצירת בקשה לאישור דיגיטלי

יוצרים בקשה לתעודה דיגיטלית ומשתמשים בה כדי להפעיל DigitalCredentialOption.

// The request in the JSON format to conform with
// the JSON-ified Credential Manager - Verifier API request definition.
val requestJson = generateRequestFromServer()
val digitalCredentialOption =
    GetDigitalCredentialOption(requestJson = requestJson)

// Use the option from the previous step to build the `GetCredentialRequest`.
val getCredRequest = GetCredentialRequest(
    listOf(digitalCredentialOption)
)

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

{
  "requests": [
    {
      "protocol": "openid4vp-v1-unsigned",
      "data": {
        "response_type": "vp_token",
        "response_mode": "dc_api",
        "nonce": "OD8eP8BYfr0zyhgq4QCVEGN3m7C1Ht_No9H5fG5KJFk",
        "dcql_query": {
          "credentials": [
            {
              "id": "cred1",
              "format": "mso_mdoc",
              "meta": {
                "doctype_value": "org.iso.18013.5.1.mDL"
              },
              "claims": [
                {
                  "path": [
                    "org.iso.18013.5.1",
                    "family_name"
                  ]
                },
                {
                  "path": [
                    "org.iso.18013.5.1",
                    "given_name"
                  ]
                },
                {
                  "path": [
                    "org.iso.18013.5.1",
                    "age_over_21"
                  ]
                }
              ]
            }
          ]
        }
      }
    }
  ]
}

קבלת פרטי הכניסה

מפעילים את התהליך של getCredential עם הבקשה שנוצרה. אם הבקשה תצליח, תקבלו את התשובה GetCredentialResponse, ואם היא תיכשל, תקבלו את התשובה GetCredentialException.

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

coroutineScope.launch {
    try {
        val result = credentialManager.getCredential(
            context = activityContext,
            request = getCredRequest
        )
        verifyResult(result)
    } catch (e : GetCredentialException) {
        handleFailure(e)
    }
}

// Handle the successfully returned credential.
fun verifyResult(result: GetCredentialResponse) {
    val credential = result.credential
    when (credential) {
        is DigitalCredential -> {
            val responseJson = credential.credentialJson
            validateResponseOnServer(responseJson)
        }
        else -> {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential ${credential.type}")
        }
    }
}

// Handle failure.
fun handleFailure(e: GetCredentialException) {
  when (e) {
        is GetCredentialCancellationException -> {
            // The user intentionally canceled the operation and chose not
            // to share the credential.
        }
        is GetCredentialInterruptedException -> {
            // Retry-able error. Consider retrying the call.
        }
        is NoCredentialException -> {
            // No credential was available.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java}")
    }
}
תמונה של בורר התעודות הדיגיטליות שמוצג כשמפעילים את getCredential.
תרשים 1. בורר התעודות הדיגיטליות שמוצג כשמפעילים את getCredential.

בקשות גמישות לכמה פרטי כניסה

‫Digital Credentials API תומך גם בבקשה של כמה סוגים של פרטי כניסה בו-זמנית באמצעות OpenID4VP:

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

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

{
  "credentials": [
    {
      "id": "gov_id1",
      "format": "mso_mdoc",
      "meta": {
        "doctype_value": "org.iso.18013.5.1.mDL"
      },
      "claims": [
        {
          "path": [
            "org.iso.18013.5.1",
            "age_over_21"
          ]
        }
      ]
    },
    {
      "id": "boarding_pass1",
      "format": "dc+sd-jwt",
      "meta": {
        "vct_values": [
          "https://credentials.example.airline/boarding-pass"
        ]
      },
      "claims": [
        {
          "path": [
            "flight_number"
          ]
        },
        {
          "path": [
            "seat"
          ]
        }
      ]
    }
  ]
}
תמונה שמראה את בורר התעודות הדיגיטליות שמוצג כשמבקשים כמה סוגים של תעודות.
תרשים 1. הבורר של פרטי הכניסה הדיגיטליים שמוצג כשמפעילים כמה סוגים של פרטי כניסה.

טיפול בתגובה

אחרי ששולחים את הבקשה לפרטי הכניסה, מטפלים ב-GetCredentialResponse שמוחזר מ-CredentialManager.

// Handle the successfully returned credential.
fun verifyResult(result: GetCredentialResponse) {
    val credential = result.credential
    when (credential) {
        is DigitalCredential -> {
            val responseJson = credential.credentialJson
            validateResponseOnServer(responseJson)
        }
        else -> {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential ${credential.type}")
        }
    }
}

טיפול בחריגות

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

// Handle failure.
fun handleFailure(e: GetCredentialException) {
    when (e) {
        is GetCredentialCancellationException -> {
            // The user intentionally canceled the operation and chose not
            // to share the credential.
        }
        is GetCredentialInterruptedException -> {
            // Retry-able error. Consider retrying the call.
        }
        is NoCredentialException -> {
            // No credential was available.
        }
        is CreateCredentialUnknownException -> {
            // An unknown, usually unexpected, error has occurred. Check the
            // message error for any additional debugging information.
        }
        is CreateCredentialCustomException -> {
            // You have encountered a custom error thrown by the wallet.
            // If you made the API call with a request object that's a
            // subclass of CreateCustomCredentialRequest using a 3rd-party SDK,
            // then you should check for any custom exception type constants
            // within that SDK to match with e.type. Otherwise, drop or log the
            // exception.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java}")
    }
}