このガイドでは、 Digital Credentials Verifier APIを使用して、OpenID for Verifiable Presentations (OpenID4VP)リクエストで検証済みのメールアドレスを取得する方法について説明します。
依存関係を追加する
アプリの build.gradle ファイルに、Credential Manager の次の依存関係を追加します。
Kotlin
dependencies { implementation("androidx.credentials:credentials:1.7.0-alpha02") implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha02") }
Groovy
dependencies { implementation "androidx.credentials:credentials:1.7.0-alpha02" implementation "androidx.credentials:credentials-play-services-auth:1.7.0-alpha02" }
認証情報マネージャーを初期化する
アプリまたはアクティビティのコンテキストを使用して、CredentialManager オブジェクトを作成します。
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
デジタル認証情報リクエストを作成する
検証済みのメールアドレスをリクエストするには、GetCredentialRequest
を含む GetDigitalCredentialOptionを作成します。このオプションには、OpenID for Verifiable Presentations(OpenID4VP)リクエストとしてフォーマットされた requestJson 文字列が必要です。
OpenID4VP リクエスト JSON は特定の構造に従う必要があります。現在の
プロバイダは、外側の "digital": {"requests":
[...]} ラッパーを含む JSON 構造をサポートしています。
val nonce = generateSecureRandomNonce()
// This request follows the OpenID4VP spec
val openId4vpRequest = """
{
"requests": [
{
"protocol": "openid4vp-v1-unsigned",
"data": {
"response_type": "vp_token",
"response_mode": "dc_api",
"nonce": "$nonce",
"dcql_query": {
"credentials": [
{
"id": "user_info_query",
"format": "dc+sd-jwt",
"meta": {
"vct_values": ["UserInfoCredential"]
},
"claims": [
{"path": ["email"]},
{"path": ["name"]},
{"path": ["given_name"]},
{"path": ["family_name"]},
{"path": ["picture"]},
{"path": ["hd"]},
{"path": ["email_verified"]}
]
}
]
}
}
}
]
}
"""
val getDigitalCredentialOption = GetDigitalCredentialOption(requestJson = openId4vpRequest)
val request = GetCredentialRequest(listOf(getDigitalCredentialOption))
リクエストには次の重要な情報が含まれます。
DCQL クエリ:
dcql_queryは、認証情報のタイプとリクエストされるクレーム(email_verified)を指定します。他のクレームをリクエストして、検証レベルを判断できます。使用できるクレームは次のとおりです。email_verified: レスポンスでは、メールアドレスが確認済みかどうかを示すブール値です。hd(所有ドメイン): レスポンスでは空です。
メールアドレスが @gmail.com 以外の場合、Google アカウントの作成時に Google がこのメールアドレスを確認しましたが、鮮度クレームはありません。そのため、Google 以外のメールアドレスの場合は、ユーザーを確認するために OTP などの追加のチャレンジを検討する必要があります。認証情報のスキーマと、
email_verifiedなどのフィールドを検証するための具体的なルールについては、 Google Identity のガイドをご覧ください。nonce: リクエストごとに、一意の暗号学的に安全なランダム値が生成されます。これはリプレイ攻撃を防ぐため、セキュリティ上非常に重要です。
UserInfoCredential: この値は、ユーザー属性を含む特定のタイプのデジタル認証情報を意味します。リクエストにこれを含めることは、メールアドレスの確認ユースケースを区別するために不可欠です。
次に、openId4vpRequest JSON を GetDigitalCredentialOption でラップし、GetCredentialRequest を作成して、getCredential() を呼び出します。
リクエストをユーザーに提示する
認証情報マネージャーの組み込み UI を使用して、リクエストをユーザーに提示します。
try {
// Requesting Digital Credential from user...
val result = credentialManager.getCredential(activity, request)
when (val credential = result.credential) {
is DigitalCredential -> {
val responseJsonString = credential.credentialJson
// Successfully received digital credential response.
// Next, parse this response and send it to your server.
// ...
}
else -> {
// handle Unexpected State() - Up to the developer
}
}
} catch (e: Exception) {
// handle exceptions - Up to the developer
}
クライアントでレスポンスを解析する
レスポンスを受信したら、クライアントで予備解析を行うことができます。これは、ユーザー名を表示するなど、UI をすぐに更新する場合に便利です。
次のコードは、未加工の Selective Disclosure JWT (SD-JWT) を抽出し、ヘルパーを使用してそのクレームをデコードします。
// 1. Parse the outer JSON wrapper to get the `vp_token`
val responseData = JSONObject(responseJsonString)
val vpToken = responseData.getJSONObject("vp_token")
// 2. Extract the raw SD-JWT string
val credentialId = vpToken.keys().next()
val rawSdJwt = vpToken.getJSONArray(credentialId).getString(0)
// 3. Use your parser to get the verified claims
// Server-side validation/parsing is highly recommended.
// Assumes a local parser like the one in our SdJwtParser.kt sample
val claims = SdJwtParser.parse(rawSdJwt)
Log.d("TAG", "Parsed Claims: ${claims.toString(2)}")
// 4. Create your VerifiedUserInfo object with REAL data
val userInfo = VerifiedUserInfo(
email = claims.getString("email"),
displayName = claims.optString("name", claims.getString("email"))
)
レスポンスを処理する
Credential Manager API は DigitalCredential
レスポンスを返します。
未加工の responseJsonString の例と、内部 SD-JWT を解析した後のクレームの例を次に示します。検証済みのメールアドレスとともに、追加のメタデータも取得できます。
/*
// Example of the raw JSON response from credential.credentialJson:
{
"vp_token": {
// This key matches the 'id' you set in your dcql_query
"user_info_query": [
// The SD-JWT string (Issuer JWT ~ Disclosures ~ Key Binding JWT)
"eyJhbGciOiJ...~WyI...IiwgImVtYWlsIiwgInVzZXJAZXhhbXBsZS5jb20iXQ~...~eyJhbGciOiJ..."
]
}
}
// Example of the parsed and verified claims from the SD-JWT on your server:
{
"cnf": {
"jwk": {..}
},
"exp": 1775688222,
"iat": 1775083422,
"iss": "https://verifiablecredentials-pa.googleapis.com",
"vct": "UserInfoCredential",
"email": "jane.doe.246745@gmail.com",
"email_verified": true,
"given_name": "Jane",
"family_name": "Doe",
"name": "Jane Doe",
"picture": "http://example.com/janedoe/me.jpg",
"hd": ""
}
*/
アカウント作成のサーバーサイド検証
取得したメールアドレスは暗号学的に検証されるため、メールアドレスの OTP 検証手順を省略できます。これにより、登録時の手間が大幅に軽減され、コンバージョン率が向上する可能性があります。このプロセスはサーバーで処理することをおすすめします。クライアントは、未加工のレスポンス(vp_token を含む)と元の nonce を新しいサーバー エンドポイントに送信します。
検証を行うには、アカウントを作成する前に、またはユーザーをログインさせる前に、アプリケーションで完全な responseJsonString をサーバーに送信して暗号検証を行う必要があります。
デジタル認証情報は、サーバーに 2 つの重要な検証レベルを提供します。
- データの信頼性: 発行者(
iss)URL とSD-JWT署名を検証することで、信頼できる機関がこのデータを発行したことを証明します。 - プレゼンターの ID:
cnfフィールドと Key Binding (kb)署名を検証することで、認証情報が元々発行されたデバイスと同じ デバイスで共有されていることを確認し、別のデバイスで傍受または 使用されるのを防ぎます。
サーバーでの検証では、次のことを行う必要があります。
- 発行者を確認する:
iss(発行者)フィールドがhttps://verifiablecredentials-pa.googleapis.comと一致していることを確認します。 - 署名を確認する: https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks で入手できる公開鍵(JWK)を使用して、SD-JWT の署名を確認します。
完全なセキュリティを確保するには、リプレイ攻撃を防ぐために nonce も検証してください。
これらの手順を組み合わせることで、サーバーはデータの信頼性とプレゼンターの ID の両方を検証し、新しいアカウントをプロビジョニングする前に認証情報が傍受またはなりすましされていないことを確認できます。
try {
// Send the raw credential response and the original nonce to your server.
// Your server must validate the response. createAccountWithVerifiedCredentials
// is a custom implementation per each RP for server side verification and account creation.
val serverResponse = createAccountWithVerifiedCredentials(responseJsonString, nonce)
// Server returns the new account info (e.g., email, name)
val claims = JSONObject(serverResponse.json)
val userInfo = VerifiedUserInfo(
email = claims.getString("email"),
displayName = claims.optString("name", claims.getString("email"))
)
// handle response - Up to the developer
} catch (e: Exception) {
// handle exceptions - Up to the developer
}
パスキーの作成
アカウントのプロビジョニング後の次のステップとして、 すぐにパスキーを作成することをおすすめします。これにより、ユーザーは安全なパスワードレスの方法でログインできます。このフローは、標準のパスキー登録と同じです。
WebView のサポート
WebView でフローを機能させるには、デベロッパーが JavaScript ブリッジ(JS ブリッジ)を実装してハンドオフを容易にする必要があります。このブリッジにより、WebView はネイティブ アプリにシグナルを送信できます。ネイティブ アプリは、Credential Manager API への実際の呼び出しを実行できます。