Questa guida descrive come implementare il recupero delle email verificate utilizzando l'API Digital Credentials Verifier tramite una richiesta OpenID for Verifiable Presentations (OpenID4VP).
Aggiungi dipendenze
Nel file build.gradle dell'app, aggiungi le seguenti dipendenze per Credential
Manager:
Kotlin
dependencies { implementation("androidx.credentials:credentials:1.7.0-alpha02") implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha02") }
Trendy
dependencies { implementation "androidx.credentials:credentials:1.7.0-alpha02" implementation "androidx.credentials:credentials-play-services-auth:1.7.0-alpha02" }
Inizializzare Gestore delle credenziali
Utilizza il contesto dell'app o dell'attività per creare un oggetto CredentialManager.
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
Costruire la richiesta di credenziali digitali
Per richiedere un'email verificata, crea un GetCredentialRequest
contenente un GetDigitalCredentialOption. Questa opzione richiede una
stringa requestJson formattata come richiesta OpenID for Verifiable Presentations
(OpenID4VP).
Il JSON della richiesta OpenID4VP deve seguire una struttura specifica. I provider attuali supportano una struttura JSON con un wrapper "digital": {"requests":
[...]} esterno.
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))
La richiesta contiene le seguenti informazioni chiave:
Query DCQL:
dcql_queryspecifica il tipo di credenziale e le attestazioni richieste (email_verified). Puoi richiedere altre attestazioni per determinare il livello di verifica. Ecco alcune possibili rivendicazioni:email_verified: nella risposta, questo valore booleano indica se l'email è verificata.hd(dominio ospitato): nella risposta, questo campo è vuoto.
Se l'email non è @gmail.com, Google l'ha verificata al momento della creazione dell'Account Google, ma non è stata verificata di recente. Pertanto, per le email non Google, dovresti prendere in considerazione una verifica aggiuntiva, ad esempio un OTP, per verificare l'utente. Per comprendere lo schema della credenziale e le regole specifiche per la convalida di campi come
email_verified, consulta le guide di Google Identity.Nonce: per ogni richiesta viene generato un valore casuale univoco e sicuro dal punto di vista crittografico. Questo è fondamentale per la sicurezza, in quanto impedisce gli attacchi di riproduzione.
UserInfoCredential: questo valore implica un tipo specifico di credenziale digitale che contiene gli attributi utente. L'inclusione di questo elemento nella richiesta è fondamentale per distinguere il caso d'uso della verifica email.
Successivamente, racchiudi il JSON openId4vpRequest in un GetDigitalCredentialOption, crea un GetCredentialRequest e chiama getCredential().
Presentare la richiesta all'utente
Presenta la richiesta all'utente utilizzando l'interfaccia utente integrata di Gestore delle credenziali.
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
}
Analizza la risposta sul client
Dopo aver ricevuto la risposta, puoi eseguire un'analisi preliminare sul client. Ciò è utile per aggiornare immediatamente la UI, ad esempio mostrando il nome dell'utente.
Il seguente codice estrae il JWT per la divulgazione selettiva (SD-JWT) non elaborato e utilizza un helper per decodificare le relative rivendicazioni.
// 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"))
)
Gestire la risposta
L'API Credential Manager restituirà una risposta DigitalCredential.
Di seguito è riportato un esempio dell'aspetto del responseJsonString non elaborato e
delle rivendicazioni dopo l'analisi dell'SD-JWT interno, in cui ottieni
anche metadati aggiuntivi insieme all'email verificata:
/*
// 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": ""
}
*/
Convalida lato server per la creazione dell'account
Poiché l'email recuperata viene verificata crittograficamente, puoi omettere il passaggio di verifica OTP dell'email, riducendo significativamente l'attrito durante la registrazione e potenzialmente aumentando le conversioni. Questa procedura viene gestita al meglio sul tuo server. Il client
invia la risposta non elaborata (contenente vp_token) e il nonce originale a un
nuovo endpoint del server.
Per la verifica, la tua applicazione deve inviare l'responseJsonString completo al tuo server per la convalida crittografica prima di creare un account o accedere all'utente.
La credenziale digitale fornisce due livelli di verifica fondamentali per il tuo server:
- Autenticità dei dati: la verifica dell'URL dell'emittente (
iss) e della firmaSD-JWTdimostra che questi dati sono stati emessi da un'autorità attendibile. - Identità del presentatore: la verifica del campo
cnfe della firma Key Binding (kb) conferma che la credenziale viene condivisa dallo stesso dispositivo a cui è stata originariamente rilasciata, impedendone l'intercettazione o l'utilizzo su un altro dispositivo.
La convalida sul server deve ottenere quanto segue:
- Verifica emittente: assicurati che il campo
iss(emittente) corrisponda ahttps://verifiablecredentials-pa.googleapis.com. - Verifica firma: controlla la firma del JWT con firma verificabile utilizzando le chiavi pubbliche (JWK) disponibili all'indirizzo https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks.
Per una sicurezza completa, assicurati di convalidare anche nonce per impedire
attacchi di riproduzione.
Combinando questi passaggi, il server può convalidare sia l'autenticità dei dati sia l'identità del presentatore, assicurandosi che la credenziale non sia stata intercettata o falsificata prima del provisioning del nuovo account.
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
}
Creazione di passkey
Un passaggio successivo facoltativo, ma vivamente consigliato, dopo il provisioning di un account è creare immediatamente una passkey per quell'account. In questo modo, l'utente può accedere in modo sicuro e senza password. Questo flusso è identico a una registrazione standard della passkey.
Supporto di WebView
Affinché il flusso funzioni su una WebView, gli sviluppatori devono implementare un bridge JavaScript (JS Bridge) per facilitare il trasferimento. Questo bridge consente alla WebView di segnalare l'app nativa, che può quindi eseguire la chiamata effettiva all'API Credential Manager.