درخواست مدرک دیجیتال

درستی‌سنجی اعتبارنامه دیجیتال در برنامه‌های Android می‌تواند برای اصالت‌سنجی و صدور مجوز برای هویت کاربر (مانند مدرک شناسایی دولتی)، ویژگی‌های مربوط به آن کاربر (مانند گواهینامه رانندگی، مدرک تحصیلی، یا مشخصه‌هایی مانند سن یا نشانی)، یا سناریوهای دیگری که در آن‌ها لازم است اعتبارنامه‌ای برای اثبات اصالت یک نهاد صادر و درستی‌سنجی شود استفاده شود.

Verifer API در Android 9 (سطح میانای برنامه‌سازی کاربردی ۲۸) و بالاتر پشتیبانی می‌شود.

پیاده‌سازی

برای درستی‌سنجی کردن اطلاعات کاربری دیجیتال در پروژه Android، کارهای زیر را انجام دهید:

  1. وابستگی‌ها را به دستورگان ساخت برنامه‌تان اضافه کنید و کلاس CredentialManager را مقداردهی اولیه کنید.
  2. درخواست اعتبارنامه دیجیتال بسازید و از آن برای مقداردهی اولیه DigitalCredentialOption استفاده کنید، سپس GetCredentialRequest را بسازید.
  3. جریان getCredential را با درخواست ساخته‌شده راه‌اندازی کنید تا GetCredentialResponse موفقیت‌آمیز دریافت کنید یا هرگونه استثنایی را که ممکن است رخ دهد مدیریت کنید. پس‌از بازیابی موفق، پاسخ را اعتبارسنجی کنید.

افزودن وابستگی‌ها و مقداردهی اولیه

وابستگی‌های زیر را به دستورگان ساخت 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)
)

مثال زیر درخواست OpenId4Vp۱ را نشان می‌دهد. برای جزئیات بیشتر درباره فیلدهای این درخواست، به اسناد 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 نشان می‌دهد.
شکل ۱. انتخاب‌گر اعتبارنامه دیجیتال نمایش‌داده‌شده هنگام فراخوانی 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"
          ]
        }
      ]
    }
  ]
}
تصویری که انتخابگر اطلاعات اعتباری دیجیتال نمایش‌داده‌شده را هنگام فراخوانی
  انواع مختلف اطلاعات اعتباری نشان می‌دهد.
شکل ۱. انتخاب‌گر اعتبارنامه دیجیتال نمایش‌داده‌شده هنگام فراخوانی چندین نوع اعتبارنامه.

مدیریت پاسخ

پس‌از درخواست اطلاعات کاربری، 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}")
    }
}