En esta guía, se describe cómo solicitar credenciales digitales con el Administrador de credenciales en una app para Android que usa WebView. Usa un puente de JavaScript para facilitar la comunicación entre la página web y la app.
Descripción general
En los siguientes pasos, se describe la vida de una solicitud de credencial digital desde un WebView:
- Generación de solicitudes: La página web en WebView realiza una solicitud al servidor de la app, que genera un JSON de solicitud de OpenID4VP y lo devuelve.
- Inicio de la solicitud: La página web usa
navigator.credentials.get({ digital: ...}) para solicitar credenciales digitales con el JSON de la solicitud. - Intercepción: El
bridge.jsinsertado intercepta esta llamada, extrae el JSON de la solicitud de OpenID4VP específico y lo pasa a la app para Android. - Control de Android: La app para Android recibe el mensaje y llama a la API de
CredentialManager.getCredential. - Selección del usuario: El Administrador de credenciales muestra una IU en la que se le solicita al usuario que comparta su credencial. La credencial se devuelve a la app para Android.
- Respuesta del puente: La app para Android envía la respuesta a la página web con el puente.
- Finalización: La página web recibe y controla la respuesta.
Requisitos previos
Antes de implementar el puente de JavaScript para credenciales digitales en WebView, asegúrate de que tu proyecto cumpla con los siguientes requisitos:
- Compatibilidad con versiones de Android: Tu app debe orientarse a Android 6.0 (nivel de API 23) o versiones posteriores.
- Control de la app host: Debes controlar la app para Android que aloja la
WebViewpara poder adjuntar unWebMessageListenerantes de cargar la página web. - HTTPS y marco de nivel superior: La página web que solicita la credencial debe
entregarse a través de
https://y ejecutarse en el marco principal (isMainFramedebe sertrue). No se admiten los submarcos ni los elementos<iframe>incorporados. - Habilitación de la API experimental: Como la API de Digital Credentials es experimental, debes anotar las clases o los métodos que usan
GetDigitalCredentialOptioncon@OptIn(ExperimentalDigitalCredentialApi::class).
Cómo agregar dependencias
Agrega las dependencias del Administrador de credenciales y de WebKit de AndroidX al archivo build.gradle.kts del módulo de tu app:
dependencies {
// Credential Manager dependencies
implementation("androidx.credentials:credentials:1.7.0-alpha03")
implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha03")
// AndroidX WebKit (required for WebMessageListener bridge)
implementation("androidx.webkit:webkit:1.14.0")
}
Implementa credenciales digitales en WebView
Deberás agregar JavaScript a tu WebView para realizar la solicitud de credenciales
y, luego, interceptarla con bridge.js.
Crea la solicitud de JavaScript
Para establecer la comunicación entre JavaScript que se ejecuta en un WebView y el código de Android, inserta código de JavaScript en tu WebView:
// Example digital credential request to request verified email
const request = {
"providers": [
{
"protocol": "openid4vp",
"request": JSON.stringify({
"requests": [
{
"protocol": "openid4vp-v1-unsigned",
"data": {
"response_type": "vp_token",
"response_mode": "dc_api",
"nonce": "n-0S6_W_2F", // Generate dynamically
"dcql_query": {
"credentials": [
{
"id": "user_info_query",
"format": "dc+sd-jwt",
"meta": {
"vct_values": ["UserInfoCredential"]
},
"claims": [
{"path": ["email"]},
{"path": ["email_verified"]},
{"path": ["name"]}
]
}
]
}
}
}
]
})
}
]
};
try {
const credential = await navigator.credentials.get({
digital: request
});
// Process response
} catch (err) {
// Handle error
}
Crea el puente de JavaScript
Crea un archivo llamado bridge.js o inserta este código con evaluateJavascript.
Esta secuencia de comandos anula navigator.credentials.get para interceptar la solicitud y enviar la cadena JSON interna de OpenID4VP a la app para Android.
(function() {
if (!navigator.credentials) {
navigator.credentials = {};
}
const originalGet = navigator.credentials.get;
// Overrides navigator.credentials.get() to intercept W3C Digital Credential requests.
navigator.credentials.get = async function(options) {
if (options && options.digital) {
if (window.DigitalCredentialBridge) {
return new Promise((resolve, reject) => {
const digital = options.digital;
if (digital.providers && digital.providers.length > 0) {
const provider = digital.providers[0];
if (provider.protocol === "openid4vp") {
const requestJson = provider.request;
const message = {
type: "get",
request: requestJson
};
console.log("Sending message to native bridge:", JSON.stringify(message));
// Send the request string to native Android code
window.DigitalCredentialBridge.postMessage(JSON.stringify(message));
// Receive the response sent from native code using replyProxy.postMessage()
window.DigitalCredentialBridge.onmessage = function(event) {
console.log("Received response from native bridge:", event.data);
try {
const response = JSON.parse(event.data);
if (response.error) {
reject(new Error(response.error));
} else {
resolve(response);
}
} catch (e) {
// If it's not JSON, just resolve with the raw data
resolve(event.data);
}
};
} else {
reject(new Error("Unsupported protocol: " + provider.protocol));
}
} else {
reject(new Error("No providers specified in digital credential request"));
}
});
} else {
console.warn("DigitalCredentialBridge not found, falling back to original");
}
}
if (originalGet) {
return originalGet.call(navigator.credentials, options);
} else {
throw new Error("navigator.credentials.get is not supported and no bridge available");
}
};
})();
Implementa credenciales digitales en Android
Configura tu app para Android para que controle las solicitudes de credenciales enviadas desde bridge.js.
Crea el objeto de escucha de Android
Configura una clase DigitalCredentialWebListener que implemente WebViewCompat.WebMessageListener. Esta clase controla los mensajes bidireccionales
entre JavaScript y el código nativo de Android, aplica verificaciones de seguridad en
isMainFrame y sourceOrigin, y evita las solicitudes superpuestas.
Implementa el controlador del Administrador de credenciales
Llama a getCredential para solicitar credenciales digitales del dispositivo.
@OptIn(ExperimentalDigitalCredentialApi::class)
class CredentialManagerHandler(private val context: Context) {
private val credentialManager = CredentialManager.create(context)
suspend fun getDigitalCredential(requestJson: String): String {
val digitalCredentialOption = GetDigitalCredentialOption(requestJson)
val getCredRequest = GetCredentialRequest(listOf(digitalCredentialOption))
return try {
val result = credentialManager.getCredential(context, getCredRequest)
val credential = result.credential
if (credential is DigitalCredential) {
credential.credentialJson
} else {
"{\"error\": \"Unexpected credential type\"}"
}
} catch (e: GetCredentialException) {
"{\"error\": \"${e.message}\"}"
} catch (e: Exception) {
"{\"error\": \"Unknown exception\"}"
}
}
companion object {
private const val TAG = "CredManHandler"
}
}
Aplica el framework a tu actividad
En tu Activity o View, registra el WebMessageListener y las funciones
asociadas.
// Inside your Activity or View setup
AndroidView(
factory = { context ->
val handler = CredentialManagerHandler(context)
WebView(context).apply {
settings.javaScriptEnabled = true
// Register the bridge before loading URL
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
val listener = DigitalCredentialWebListener(coroutineScope, credentialManagerHandler)
WebViewCompat.addWebMessageListener(
webView,
INTERFACE_NAME,
setOf("*"),
listener
)
loadUrl("https://your-secure-domain.com/login")
}
},
)
Probando el flujo
Prueba el flujo para confirmar la comunicación entre WebView y la app para Android. El selector de credenciales aparece en WebView durante el proceso, y la credencial seleccionada vuelve a WebView cuando se completa.