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))

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

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 validación en el servidor debe lograr lo siguiente:

  • Verifica el emisor: Asegúrate de que el campo iss (emisor) coincida con https://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.

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
}

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.

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.