La emisión de la API de Digital Credentials se lanza en Chrome 155

Publicado: 23 de octubre de 2026

En Chrome 155 y versiones posteriores, la emisión de la API de Digital Credentials está habilitada de forma predeterminada, por lo que ya no necesitas un token de prueba de origen ni un parámetro para usarla. La API de Digital Credentials es una capa de transporte que permite que los sitios web se conecten a las apps de billetera instaladas en el dispositivo móvil del usuario. Con la emisión, las entidades emisoras pueden agregar credenciales nuevas (como IDs digitales) directamente a la app de la billetera de un usuario.

Fondo

El ecosistema de identidad digital tiene tres partes: emisores, verificadores (también llamados partes de confianza) y titulares (apps de billetera). Los usuarios suelen agregar un documento de identidad digital a su app de billetera a través del aprovisionamiento push, en el que el usuario inicia la emisión desde el sitio web o la app de la entidad emisora. Por ejemplo, un estudiante en el sitio web de su universidad (entidad emisora) puede guardar su documento de identidad digital de estudiante en su app de billetera preferida (titular).

Para que este modelo tenga éxito, la entidad emisora necesita una forma segura y confiable de invocar la app de la billetera para emitir el documento de identidad. Antes de esta API, cada entidad emisora debía crear su propia forma de abrir las apps de billetera con las que se asociaba, por lo general, con esquemas de URI personalizados. Las entidades emisoras debían mantener un mecanismo independiente para cada app de billetera, y los usuarios no tenían una forma coherente de elegir una app de billetera.

Cómo funciona la emisión de credenciales digitales

Para agilizar este proceso, Chrome admite la emisión con la API de Digital Credentials del W3C. Esto se suma a las capacidades de presentación (compartir datos selectivos solicitados de una credencial digital con un verificador), que se lanzaron en Chrome 141. Cuando un sitio web de entidad emisora llama a navigator.credentials.create() con un miembro digital que contiene una solicitud de emisión, Chrome pasa la solicitud al sistema operativo (en Android, Credential Manager). Luego, el sistema operativo muestra su propia interfaz, en la que el usuario elige una app de billetera. La API funciona con protocolos estándar de la industria, como OpenID para la emisión de credenciales verificables (OpenID4VCI). La emisión admite dos flujos: uno en el mismo dispositivo en Android y otro multidispositivo que comienza en una computadora de escritorio y finaliza en un dispositivo Android.

Flujo en el mismo dispositivo en Android

En Android, Chrome pasa la solicitud de emisión al Administrador de credenciales de Android, que muestra una lista de apps de billetera compatibles en el dispositivo. El usuario selecciona una billetera y acepta agregar la credencial. Luego, la app de billetera obtiene la credencial de la entidad emisora con el protocolo de emisión, como OpenID4VCI.

Flujo multidispositivo en computadoras de escritorio

En computadoras, Chrome muestra un código QR. El usuario escanea el código con su dispositivo Android y completa el flujo en ese dispositivo, como se describe en la sección anterior. Los dispositivos usan Bluetooth para confirmar que están cerca entre sí, por lo que Chrome le pide al usuario que active el Bluetooth si está desactivado. Chrome usa el mismo mecanismo multidispositivo para la presentación de la API de Digital Credentials.

Implementa la emisión con la API

Para iniciar el flujo de emisión, llama a navigator.credentials.create() y pasa tus solicitudes de emisión dentro de un miembro digital. Realiza esta llamada desde un controlador de clics o algún otro controlador de gestos del usuario, ya que la llamada requiere la activación transitoria del usuario. En el siguiente ejemplo, se usa una oferta de credenciales de OpenID4VCI con el flujo de código con autorización previa:

// Call this function from a user gesture handler, for example, a button's
// click handler. The pre-authorized code is a short-lived, single-use code
// that your server generates for the signed-in user.
async function issueDigitalCredential(preAuthorizedCode) {
  // Check for Digital Credentials API and protocol support.
  if (!window.DigitalCredential ||
      !DigitalCredential.userAgentAllowsProtocol('openid4vci-v1')) {
    // Provide an alternative issuance method.
    showAlternativeIssuanceFlow();
    return;
  }

  // Construct a credential offer according to the OpenID4VCI
  // specification. This example uses the format that the
  // demo CMWallet supports.
  const credentialOffer = {
    credential_issuer: 'https://issuer.example',
    credential_configuration_ids: ['org.iso.18013.5.1.mDL'],
    grants: {
      'urn:ietf:params:oauth:grant-type:pre-authorized_code': {
        'pre-authorized_code': preAuthorizedCode
      }
    },
    // Metadata that the wallet uses to request and display the credential.
    credential_issuer_metadata: {
      credential_issuer: 'https://issuer.example',
      credential_endpoint: 'https://issuer.example/openid4vci/credential',
      nonce_endpoint: 'https://issuer.example/openid4vci/nonce',
      credential_configurations_supported: {
        'org.iso.18013.5.1.mDL': {
          format: 'mso_mdoc',
          doctype: 'org.iso.18013.5.1.mDL',
          // Bind the credential to a key that the wallet generates.
          cryptographic_binding_methods_supported: ['cose_key'],
          proof_types_supported: {
            jwt: { proof_signing_alg_values_supported: ['ES256'] }
          },
          credential_metadata: {
            display: [{ name: 'Mobile driving license', locale: 'en-US' }],
            // The claims that this credential contains. For a full list of
            // standard mDL claims, see the ISO/IEC 18013-5 specification.
            claims: [{
              path: ['org.iso.18013.5.1', 'family_name'],
              display: [{ name: 'Family name', locale: 'en-US' }]
            }
            // ... Add any other claims for your credential here.
            ]
          }
        }
      }
    },
    // Metadata of the authorization server that issues access tokens.
    authorization_server_metadata: {
      issuer: 'https://issuer.example',
      token_endpoint: 'https://issuer.example/openid4vci/token'
    }
  };

  try {
    const credential = await navigator.credentials.create({
      digital: {
        requests: [{
          // The protocol identifier for OpenID4VCI 1.0.
          protocol: 'openid4vci-v1',
          // Pass the offer as an object, not as a JSON string.
          data: credentialOffer
        }]
      }
    });

    // The wallet handled the request. credential.data is an object that
    // contains the wallet's protocol-specific response. It is not the issued
    // credential, and it does not necessarily mean that issuance is complete.
    // Your server knows when the wallet retrieves the credential from your
    // credential endpoint.
    // Implement your custom logic here as needed.
    showIssuanceStatus(credential);
  } catch (error) {
    switch (error.name) {
      case 'NotAllowedError':
        // The user canceled the flow, no wallet can handle the request, or
        // the call does not have transient user activation.
        break;
      case 'NotSupportedError':
        // The browser does not support Digital Credentials API issuance.
        // Fall back to your custom alternative flow logic.
        showAlternativeIssuanceFlow();
        break;
      case 'TypeError':
        // The request is malformed, for example, data is not an object.
        console.error('Invalid issuance request:', error);
        break;
      default:
        // For example, AbortError or NetworkError.
        console.error('Credential issuance failed:', error);
    }
  }
}

const addButton = document.querySelector('#add-to-wallet');
addButton.addEventListener('click', () => {
  // Your server adds a pre-authorized code for the signed-in user
  // to the button's data-pre-authorized-code attribute.
  issueDigitalCredential(addButton.dataset.preAuthorizedCode);
});

En este ejemplo, showAlternativeIssuanceFlow() y showIssuanceStatus() representan la lógica de tu propia app.

Para llamar a create() desde un iframe de origen cruzado, la página de incorporación debe permitir la función de política de permisos digital-credentials-create. De lo contrario, la llamada se rechaza con un NotAllowedError:

<iframe src="https://issuer.example" allow="digital-credentials-create">
</iframe>

Cambios desde la prueba de origen

Los métodos y parámetros de la API no cambiaron desde la prueba de origen. Sin embargo, los siguientes cambios se enviaron entre Chrome 143 y Chrome 155, y es posible que algunos de ellos requieran que actualices tu código. En la siguiente tabla, se resumen los cambios, y en las secciones posteriores, se explica cada uno.

Área Prueba de origen (Chrome 143) Chrome 155
Disponibilidad Token de prueba de origen o la marca chrome://flags/#web-identity-digital-credentials-creation Habilitado de forma predeterminada
Activación del usuario Obligatorio, pero no consumido Obligatorio y consumido por cada llamada a create() (Chrome 146)
Errores de cancelación NetworkError NotAllowedError (Chrome 148)
Identificadores de protocolo Chrome pasa cualquier identificador al sistema operativo. Los identificadores no reconocidos activan una advertencia de baja (Chrome 151)
Orígenes opacos Permitido Bloqueado con NotAllowedError (Chrome 151)
Pruebas automáticas No hay compatibilidad integrada Billetera virtual en el protocolo de Herramientas para desarrolladores de Chrome (Chrome 152)

Se consume la activación del usuario.

A partir de Chrome 146, una llamada a create() con un miembro digital consume la activación transitoria del usuario, como lo requiere la especificación. Durante la prueba de origen, Chrome verificó la activación del usuario, pero no la consumió.

Por lo tanto, cada llamada a create() necesita su propio gesto del usuario, por ejemplo, un clic en un botón. Sin la activación del usuario, la promesa se rechaza con un NotAllowedError. Después de la llamada, window.open() y otras APIs que requieren la activación del usuario no pueden usar el mismo gesto.

La cancelación se rechaza con NotAllowedError

A partir de Chrome 148, la promesa que devuelve create() se rechaza con un NotAllowedError cuando el usuario cancela el flujo, por ejemplo, descartando el selector de billetera en Android o cerrando el diálogo del código QR en computadoras. La promesa también se rechaza con un NotAllowedError cuando ninguna app de billetera puede controlar la solicitud. Durante la prueba de origen, estos casos se rechazaron con un NetworkError.

Si tu código verifica un NetworkError para detectar la cancelación, actualízalo para que verifique un NotAllowedError en su lugar.

Los identificadores de protocolo no reconocidos activan una advertencia

La especificación define un solo identificador de protocolo de emisión, openid4vci-v1, para OpenID4VCI 1.0. Chrome 155 reconoce openid4vci-v1 y también openid4vci. Como openid4vci no forma parte de la especificación, te recomendamos que uses openid4vci-v1.

Chrome pasa las solicitudes con cualquier identificador de protocolo a la plataforma. Sin embargo, a partir de Chrome 151, las Herramientas para desarrolladores muestran una advertencia de baja cuando una página llama a create() con un identificador de protocolo no reconocido. Chrome tiene la intención de bloquear las solicitudes con identificadores de protocolo no reconocidos. Para conocer el estado, consulta la entrada Filtrado de protocolos más específico en la API de Digital Credential.

Para probar este comportamiento, habilita chrome://flags/#enable-experimental-web-platform-features. Con esta marca habilitada, create() ignora las solicitudes con identificadores de protocolo no reconocidos y las rechaza con un TypeError si no quedan solicitudes. Además, DigitalCredential.userAgentAllowsProtocol() devuelve true solo para los protocolos reconocidos. Sin la marca, solo se verifica que el identificador esté bien formado. Por lo tanto, no dependas solo de userAgentAllowsProtocol() para la detección de funciones. Controla NotSupportedError como se muestra en el ejemplo.

Se bloquean los orígenes opacos

A partir de Chrome 151, create() rechaza con un NotAllowedError cuando un documento con un origen opaco lo llama, como un iframe de zona de pruebas sin el token allow-same-origin. Durante la prueba de origen, Chrome no bloqueó estas llamadas.

Realiza pruebas con una billetera virtual

A partir de Chrome 152, puedes probar tu flujo de emisión sin un dispositivo Android ni una app de billetera. El comando experimental DigitalCredentials.setVirtualWalletBehavior en el protocolo de Herramientas para desarrolladores de Chrome (CDP) implementa la billetera virtual que define la especificación para las pruebas automatizadas. La billetera virtual puede responder con los datos que proporciones, rechazar solicitudes como si el usuario hubiera cancelado el flujo o dejar solicitudes pendientes para probar los tiempos de espera y las anulaciones.

La siguiente prueba de Puppeteer configura la billetera virtual para que responda a las solicitudes de openid4vci-v1 y, luego, hace clic en el botón que inicia la emisión:

const client = await page.createCDPSession();
await client.send('DigitalCredentials.setVirtualWalletBehavior', {
  action: 'respond',
  protocol: 'openid4vci-v1',
  response: {}
});

// Clicking the button provides the user activation that create() requires.
await page.click('#add-to-wallet');

Adopción por parte de la industria

Vemos la adopción de la emisión de la API de Digital Credentials. Por ejemplo, la institución financiera alemana Sparkasse usa la API de Digital Credentials para permitir que los titulares de la cuenta guarden credenciales de edad verificadas en la Billetera de Google. Luego, los titulares de las cuentas pueden usar estas credenciales para verificar su edad en línea y en persona.

Probar

Antes de probar la emisión de la API de Digital Credentials, asegúrate de tener lo siguiente:

  • Actualiza a Chrome 155 o una versión posterior.
  • Instala una app de billetera compatible en tu dispositivo Android, por ejemplo, la demo de CMWallet.
  • Bluetooth activado en ambos dispositivos (solo para el flujo multidispositivo)

Para probar el flujo multidispositivo, haz lo siguiente:

  1. En tu computadora, abre el sitio de la entidad emisora de demostración.
  2. Haz clic en el botón Agregar.
  3. Escanea el código QR con tu dispositivo Android y sigue las instrucciones que aparecen en pantalla.

Para probar el flujo en el mismo dispositivo, abre el sitio de la entidad emisora de demostración en Chrome en tu dispositivo Android.

Recursos para desarrolladores

Para comenzar a integrar la emisión de la API de Digital Credentials en tu sitio web, usa los siguientes recursos:

Comparte tus comentarios

Para informar un error o solicitar una función, envía un problema en la herramienta de seguimiento de errores de Chromium.