En esta guía, se describe cómo implementar la recuperación de correos electrónicos verificados con la API de Digital Credentials Verifier a través de una solicitud de OpenID for Verifiable Presentations (OpenID4VP).
Cómo agregar dependencias
En el archivo build.gradle de tu app, agrega las siguientes dependencias para Credential Manager:
Kotlin
dependencies { implementation("androidx.credentials:credentials:1.7.0-alpha01") implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha01") }
Groovy
dependencies { implementation "androidx.credentials:credentials:1.7.0-alpha01" implementation "androidx.credentials:credentials-play-services-auth:1.7.0-alpha01" }
Cómo inicializar Credential Manager
Usa el contexto de tu app o actividad para crear un objeto CredentialManager.
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
Cómo construir la solicitud de credencial digital
Para solicitar un correo electrónico verificado, construye un GetCredentialRequest que contenga
un GetDigitalCredentialOption. Esta opción requiere una cadena requestJson con el formato de una solicitud de OpenID for Verifiable Presentations (OpenID4VP).
El JSON de la solicitud de OpenID4VP debe seguir una estructura específica. Los proveedores actuales admiten una estructura JSON con un wrapper "digital": {"requests":
[...]} externo.
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 solicitud contiene la siguiente información clave:
Consulta de DCQL: El
dcql_queryespecifica el tipo de credencial y las declaraciones que se solicitan (email_verified). Puedes solicitar otras declaraciones para determinar el nivel de verificación. Estas son algunas declaraciones posibles:email_verified: En la respuesta, es un valor booleano que indica si el correo electrónico está verificado.hd(dominio alojado): En la respuesta, está vacío.
Si el correo electrónico no es @gmail.com, Google lo verificó cuando se creó la Cuenta de Google, pero no hay ninguna declaración de actualización. Por lo tanto, para los correos electrónicos que no son de Google, debes considerar un desafío adicional, como una OTP, para verificar al usuario. Para comprender el esquema de la credencial y las reglas específicas para validar campos como
email_verified, consulta las guías de Google Identity.nonce: Se genera un valor aleatorio único y seguro en términos criptográficos para cada solicitud. Esto es fundamental para la seguridad, ya que evita ataques de repetición.
UserInfoCredential: Este valor implica un tipo específico de credencial digital que contiene atributos del usuario. Incluirlo en la solicitud es fundamental para distinguir el caso de uso de verificación de correo electrónico.
A continuación, incluye el JSON de openId4vpRequest en un GetDigitalCredentialOption, crea un GetCredentialRequest y llama a getCredential().
Cómo presentar la solicitud al usuario
Presenta la solicitud al usuario con la IU integrada de 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
}
Cómo analizar la respuesta en el cliente
Después de recibir la respuesta, puedes realizar un análisis preliminar en el cliente. Esto es útil para actualizar la IU de inmediato, por ejemplo, mostrando el nombre del usuario.
El siguiente código extrae el JWT de divulgación selectiva (SD-JWT) sin procesar y usa un auxiliar para decodificar sus declaraciones.
// 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"))
)
Cómo controlar la respuesta
La API de Credential Manager mostrará una DigitalCredential respuesta.
A continuación, se muestra un ejemplo de cómo se ve el responseJsonString sin procesar y cómo se ven las declaraciones después de analizar el SD-JWT interno, en el que también obtienes metadatos adicionales junto con el correo electrónico verificado:
/*
// 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": "[email protected]",
"email_verified": true,
"given_name": "Jane",
"family_name": "Doe",
"name": "Jane Doe",
"picture": "http://example.com/janedoe/me.jpg",
"hd": ""
}
*/
Validación del servidor para la creación de cuentas
Dado que el correo electrónico recuperado se verifica de forma criptográfica, puedes omitir el paso de verificación de OTP por correo electrónico, lo que reduce significativamente la fricción del registro y, potencialmente, aumenta la conversión. Este proceso se maneja mejor en tu servidor. El cliente envía la respuesta sin procesar (que contiene el vp_token) y el nonce original a un extremo de servidor nuevo.
Para la verificación, tu aplicación debe enviar el responseJsonString completo a tu servidor para la validación criptográfica antes de crear una cuenta o hacer que el usuario acceda.
La credencial digital proporciona dos niveles de verificación fundamentales para tu servidor:
- Autenticidad de los datos: La verificación de la URL del emisor (
iss) y laSD-JWTfirma demuestran que una autoridad confiable emitió estos datos. - Identidad del presentador: La verificación del campo
cnfy la firma de vinculación de claves (kb) confirman que la credencial se comparte desde el mismo dispositivo al que se emitió originalmente, lo que impide que se intercepte o se use en otro dispositivo.
La validación en el servidor debe lograr lo siguiente:
- Verificar el emisor: Asegúrate de que el campo
iss(emisor) coincida conhttps://verifiablecredentials-pa.googleapis.com. - Verificar la firma: Verifica la firma del SD-JWT con las claves públicas (JWK) disponibles en https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks.
Para obtener seguridad completa, asegúrate de validar también el nonce para evitar ataques de repetición.
Si combinas estos pasos, tu servidor puede validar la autenticidad de los datos y la identidad del presentador, lo que garantiza que la credencial no se haya interceptado ni falsificado antes de aprovisionar la cuenta nueva.
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
}
Creación de llaves de acceso
Un siguiente paso opcional, pero muy recomendable, después de aprovisionar una cuenta es crear de inmediato una llave de acceso para esa cuenta. Esto proporciona un método seguro y sin contraseña para que el usuario acceda. Este flujo es idéntico a un registro de llave de acceso estándar.
Compatibilidad con WebView
Para que el flujo funcione en un WebView, los desarrolladores deben implementar un puente de JavaScript (JS Bridge) para facilitar la transferencia. Este puente permite que WebView le indique a la aplicación nativa, que luego puede realizar la llamada real a la API de Credential Manager.