In diesem Leitfaden wird beschrieben, wie Sie den Abruf bestätigter E‑Mail-Adressen mithilfe der Digital Credentials Verifier API über eine OpenID for Verifiable Presentations-Anfrage (OpenID4VP) implementieren.
Abhängigkeiten hinzufügen
Fügen Sie der Datei build.gradle Ihrer App die folgenden Abhängigkeiten für Credential Manager hinzu:
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" }
Credential Manager initialisieren
Verwenden Sie den App- oder Aktivitätskontext, um ein CredentialManager-Objekt zu erstellen.
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
Anfrage für digitale Anmeldedaten erstellen
Wenn Sie eine bestätigte E‑Mail-Adresse anfordern möchten, erstellen Sie eine GetCredentialRequest
mit einer GetDigitalCredentialOption. Für diese Option ist ein requestJson-String erforderlich, der als OpenID for Verifiable Presentations-Anfrage (OpenID4VP) formatiert ist.
Die OpenID4VP-Anfrage-JSON muss einer bestimmten Struktur folgen. Die aktuellen Anbieter unterstützen eine JSON-Struktur mit einem äußeren "digital": {"requests":
[...]} Wrapper.
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))
Die Anfrage enthält die folgenden wichtigen Informationen:
DCQL-Abfrage: Mit
dcql_querywerden der Anmeldedatentyp und die angeforderten Anforderungen (email_verified) angegeben. Sie können andere Anforderungen anfordern, um den Grad der Verifizierung zu bestimmen. Einige mögliche Claims sind:email_verified: In der Antwort ist dies ein boolescher Wert, der angibt, ob die E‑Mail-Adresse bestätigt ist.hd(gehostete Domain): In der Antwort ist dieses Feld leer.
Wenn die E‑Mail-Adresse nicht auf @gmail.com endet, hat Google diese E‑Mail-Adresse beim Erstellen des Google-Kontos bestätigt. Es gibt jedoch keinen Claim zur Aktualität. Daher sollten Sie für E‑Mail-Adressen, die nicht von Google stammen, eine zusätzliche Bestätigung wie ein Einmalpasswort in Betracht ziehen, um den Nutzer zu bestätigen. Informationen zum Schema der Anmeldedaten und zu den spezifischen Regeln für die Validierung von Feldern wie
email_verifiedfinden Sie in den Google Identity-Leitfäden.nonce: Für jede Anfrage wird ein eindeutiger, kryptografisch sicherer Zufallswert generiert. Das ist wichtig für die Sicherheit, da es Wiederholungsversuche verhindert.
UserInfoCredential: Dieser Wert gibt einen bestimmten Typ digitaler Anmeldedaten an, der Nutzerattribute enthält. Es ist wichtig, diesen Wert in die Anfrage aufzunehmen, um den Anwendungsfall der E‑Mail-Bestätigung zu unterscheiden.
Umschließen Sie als Nächstes die openId4vpRequest-JSON in einer GetDigitalCredentialOption, erstellen Sie eine GetCredentialRequest und rufen Sie getCredential() auf.
Anfrage für den Nutzer präsentieren
Präsentieren Sie dem Nutzer die Anfrage über die integrierte UI von Credential Manager.
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
}
Antwort auf dem Client parsen
Nachdem Sie die Antwort erhalten haben, können Sie auf dem Client eine vorläufige Analyse durchführen. Das ist nützlich, um die UI sofort zu aktualisieren, z. B. indem Sie den Namen des Nutzers anzeigen.
Der folgende Code extrahiert das unformatierte SD-JWT (Selective Disclosure JWT) und verwendet eine Hilfsfunktion, um die Claims zu decodieren.
// 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"))
)
Antwort verarbeiten
Die Credential Manager API gibt eine DigitalCredential
Antwort zurück.
Im Folgenden sehen Sie ein Beispiel dafür, wie der unformatierte responseJsonString aussieht und wie die Claims nach dem Parsen des inneren SD-JWT aussehen. Dabei erhalten Sie neben der bestätigten E‑Mail-Adresse auch zusätzliche Metadaten:
/*
// 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": ""
}
*/
Serverseitige Validierung für die Kontoerstellung
Da die abgerufene E‑Mail-Adresse kryptografisch bestätigt ist, können Sie den Schritt der E‑Mail-OTP-Bestätigung überspringen. Dadurch wird die Registrierung erheblich vereinfacht und die Conversion kann möglicherweise gesteigert werden. Dieser Vorgang wird am besten auf Ihrem Server ausgeführt. Der Client sendet die unformatierte Antwort (mit dem vp_token) und die ursprüngliche Nonce an einen neuen Serverendpunkt.
Zur Bestätigung muss Ihre Anwendung den vollständigen responseJsonString zur kryptografischen Validierung an Ihren Server senden, bevor ein Konto erstellt oder der Nutzer angemeldet wird.
Die digitalen Anmeldedaten bieten zwei wichtige Bestätigungsebenen für Ihren Server:
- Authentizität der Daten: Durch die Bestätigung der Aussteller-URL (
iss) und derSD-JWTSignatur wird nachgewiesen, dass diese Daten von einer vertrauenswürdigen Stelle ausgestellt wurden. - Identität des Präsentators: Durch die Bestätigung des Felds
cnfund der Key Binding (kb)-Signatur wird bestätigt, dass die Anmeldedaten von demselben Gerät freigegeben werden, für das sie ursprünglich ausgestellt wurden. So wird verhindert, dass sie abgefangen oder auf einem anderen Gerät verwendet werden.
Die Validierung auf dem Server muss Folgendes leisten:
- Aussteller bestätigen: Prüfen Sie, ob das Feld
iss(Aussteller) mithttps://verifiablecredentials-pa.googleapis.comübereinstimmt. - Signatur bestätigen: Prüfen Sie die Signatur des SD-JWT mit den öffentlichen Schlüsseln (JWKs), die unter https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks verfügbar sind.
Für maximale Sicherheit sollten Sie auch die nonce validieren, um Wiederholungsversuche zu verhindern.
Durch die Kombination dieser Schritte kann Ihr Server sowohl die Authentizität der Daten als auch die Identität des Präsentators bestätigen. So wird sichergestellt, dass die Anmeldedaten nicht abgefangen oder gefälscht wurden, bevor das neue Konto bereitgestellt wird.
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
}
Passkey erstellen
Ein optionaler, aber sehr empfehlenswerter nächster Schritt nach der Bereitstellung eines Kontos ist das sofortige Erstellen eines Passkeys für dieses Konto. So kann sich der Nutzer sicher und ohne Passwort anmelden. Dieser Ablauf ist mit einer Standard-Passkey-Registrierung identisch.
WebView-Unterstützung
Damit der Ablauf in einer WebView funktioniert, sollten Entwickler eine JavaScript Bridge (JS-Bridge) implementieren, um die Übergabe zu erleichtern. Über diese Bridge kann die WebView die systemeigene App benachrichtigen, die dann den eigentlichen Aufruf der Credential Manager API ausführen kann.