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-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" }
Inicializa el Administrador de credenciales
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)
Construye la solicitud de credencial digital
Para solicitar un correo electrónico verificado, crea un objeto GetCredentialRequest que contenga un objeto 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 objeto
dcql_queryespecifica el tipo de credencial y los reclamos que se solicitan (email_verified). Puedes solicitar otros reclamos para determinar el nivel de verificación. A continuación, se incluyen algunos reclamos posibles:email_verified: En la respuesta, es un valor booleano que indica si se verificó el correo electrónico.hd(dominio alojado): En la respuesta, este campo está vacío.
Si la dirección de correo electrónico no es de @gmail.com, Google la verificó cuando se creó la Cuenta de Google, pero no hay ninguna declaración de actualidad. Por lo tanto, para los correos electrónicos que no son de Google, debes considerar un desafío adicional, como un 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. Incluir este parámetro en la solicitud es fundamental para distinguir el caso de uso de la verificación por correo electrónico.
A continuación, incluye el objeto JSON openId4vpRequest en un objeto GetDigitalCredentialOption, crea un objeto GetCredentialRequest y llama a getCredential().
Presenta 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
}
Analiza 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 asistente para decodificar sus reclamaciones.
// 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 devolverá una respuesta DigitalCredential.
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": "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": ""
}
*/
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 con OTP por correo electrónico, lo que reduce significativamente la fricción en el 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 nuevo extremo del servidor.
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 permitir que el usuario acceda.
La credencial digital proporciona dos niveles críticos de verificación para tu servidor:
- Autenticidad de los datos: La verificación de la URL del emisor (
iss) y la firma deSD-JWTprueban que una autoridad de confianza emitió estos datos. - Identidad del presentador: La verificación del campo
cnfy la firma de vinculación de clave (kb) confirman que la credencial se comparte desde el mismo dispositivo para el que se emitió originalmente, lo que evita que se intercepte o se use en otro dispositivo.
La validación en el servidor debe lograr lo siguiente:
- Verifica el emisor: Asegúrate de que el campo
iss(emisor) coincida conhttps://verifiablecredentials-pa.googleapis.com. - Verificar 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 una seguridad total, asegúrate de validar también el nonce para evitar ataques de reproducción.
Si combinas estos pasos, tu servidor puede validar la autenticidad de los datos y la identidad del presentador, lo que garantiza que no se interceptó ni suplantó la credencial 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 paso opcional, pero altamente recomendado, 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 al registro estándar de una llave de acceso.
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 envíe una señal a la aplicación nativa, que luego puede realizar la llamada real a la API de Credential Manager.