認証情報マネージャー - Verifier API

Android アプリ内のデジタル認証情報の検証は、ユーザーの身元(政府発行の ID など)、ユーザーに関するプロパティ(運転免許証、学位、年齢や住所などの属性)、またはエンティティの信頼性を主張するために認証情報の発行と検証が必要となるその他のシナリオを認証して承認するために使用できます。

デジタル認証情報は、デジタル ウォレットからユーザーの検証可能なデジタル認証情報にアクセスする方法を指定する公開の W3C 標準です。W3C Credential Management API を使用してウェブ ユースケース向けに実装されています。Android では、Credential Manager の DigitalCredential API がデジタル認証情報の検証に使用されます。

Android バージョンの互換性

Verifier API は、Android 9(API レベル 28)以降でサポートされています。

実装

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 リクエストの例を次に示します。完全なリファレンスについては、こちらのウェブサイトをご覧ください。

{
  "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 システム ダイアログをトリガーして、ユーザーが利用可能な認証情報オプションを表示し、選択内容を収集します。次に、選択した認証情報オプションを含むウォレット アプリが、同意を収集し、デジタル認証情報レスポンスを生成するために必要なアクションを実行するための UI を表示します。

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.
        }
        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}")
    }
}