Accede con una llave de acceso mediante el autocompletado del formulario

Crea una experiencia de acceso que aproveche las llaves de acceso y, al mismo tiempo, se adapte a los usuarios que usan contraseñas.

En esta guía, se explica cómo usar el autocompletado de formularios para permitir que los usuarios accedan con llaves de acceso junto con contraseñas. El uso del autocompletado de formularios crea una experiencia de acceso unificada, lo que simplifica la transición de las contraseñas al método de autenticación de llaves de acceso más seguro y fácil de usar.

Obtén más información para implementar la IU condicional de WebAuthn y admitir usuarios de llaves de acceso y contraseñas con la menor fricción posible en tus formularios de acceso existentes.

¿Por qué usar el autocompletado de formularios para acceder con una llave de acceso?

Las llaves de acceso permiten que los usuarios accedan a sitios web con su huella dactilar, rostro o PIN del dispositivo.

Si todos los usuarios tuvieran llaves de acceso, el flujo de autenticación podría ser un solo botón de acceso. Si presiona el botón, el usuario podrá verificar directamente la cuenta con el bloqueo de pantalla y acceder.

Sin embargo, la transición de las contraseñas a las llaves de acceso presenta desafíos. Los sitios web deben admitir usuarios de contraseñas y llaves de acceso durante este período. Esperar que los usuarios recuerden qué sitios usan llaves de acceso y pedirles que elijan un método de acceso por adelantado genera una experiencia del usuario deficiente.

Las llaves de acceso también son una tecnología nueva, y explicarlas con claridad puede ser difícil. El uso de la interfaz de autocompletar conocida ayuda a abordar el desafío de la transición y la necesidad de familiaridad del usuario.

Usa una IU condicional

Para admitir de manera eficaz a los usuarios de llaves de acceso y contraseñas, incluye llaves de acceso en las sugerencias de autocompletado de tu formulario. Este enfoque usa la IU condicional, una función del estándar WebAuthn.

Ejemplo de selección de llaves de acceso a través del autocompletado de formularios.

Cuando el usuario se enfoca en el campo de entrada de nombre de usuario, aparece un diálogo de autocompletado que sugiere llaves de acceso almacenadas junto con las contraseñas guardadas. El usuario puede seleccionar una llave de acceso o una contraseña y acceder con el bloqueo de pantalla del dispositivo si elige una llave de acceso.

Esto permite que los usuarios accedan a tu sitio web con el formulario de acceso existente, pero con el beneficio de seguridad adicional de las llaves de acceso si tienen una.

Cómo funciona la autenticación con llave de acceso

Para autenticar con una llave de acceso, usa la API de WebAuthn.

Los cuatro componentes de un flujo de autenticación con llave de acceso son los siguientes:

  • Backend: Almacena los detalles de la cuenta de usuario, incluida la clave pública.
  • Frontend: Se comunica con el navegador y recupera los datos necesarios del backend.
  • Navegador: Ejecuta tu código JavaScript y, además, interactúa con la API de WebAuthn.
  • Proveedor de llaves de acceso: Crea y almacena la llave de acceso. Por lo general, es un administrador de contraseñas, como el Administrador de contraseñas de Google, o una llave de seguridad.
Flujo de autenticación de la llave de acceso, que muestra la interacción entre el frontend, el backend, el navegador y el proveedor de llaves de acceso.
Flujo de autenticación de llave de acceso completo.

El proceso de autenticación de llaves de acceso sigue este flujo:

  1. El usuario visita la página de acceso y el frontend solicita una verificación de autenticación al backend.
  2. El backend genera y muestra un desafío de WebAuthn asociado con la cuenta del usuario.
  3. El frontend llama a navigator.credentials.get() con el desafío para iniciar la autenticación con el navegador.
  4. El navegador, que interactúa con el proveedor de llaves de acceso, le solicita al usuario que seleccione una llave de acceso (a menudo, con un diálogo de autocompletar activado cuando se enfoca el campo de acceso) y verifique su identidad con el bloqueo de pantalla o los datos biométricos del dispositivo.
  5. Después de que se realiza correctamente la verificación del usuario, el proveedor de llaves de acceso firma el desafío, y el navegador muestra la credencial de clave pública resultante (incluida la firma) al frontend.
  6. El frontend envía esta credencial al backend.
  7. El backend verifica la firma de la credencial con la clave pública almacenada del usuario. Si la verificación se realiza correctamente, el backend permite que el usuario acceda.

Cómo autenticar con una llave de acceso mediante el autocompletado del formulario

Para iniciar la autenticación con llave de acceso mediante el autocompletado de formularios, realiza una llamada get de WebAuthn condicional cuando se cargue la página de acceso. Esta llamada a navigator.credentials.get() incluye la opción mediation: 'conditional'.
Una solicitud condicional a la API de navigator.credentials.get() de WebAuthn no muestra la IU de inmediato. En su lugar, espera en un estado pendiente hasta que el usuario interactúe con el mensaje de autocompletado del campo de nombre de usuario. Si el usuario selecciona una llave de acceso, el navegador resuelve la promesa pendiente con una credencial para que el usuario acceda y omita el envío de formularios tradicional. Si el usuario elige una contraseña, no se resuelve la promesa y continúa el flujo de acceso estándar con contraseña. En ese caso, es responsabilidad de la página permitir que el usuario acceda.

Anota el campo de entrada del formulario

Para habilitar el autocompletado de llaves de acceso, agrega el atributo autocomplete al campo input del nombre de usuario de tu formulario. Incluye username y webauthn como valores separados por espacios.

<input type="text" name="username" autocomplete="username webauthn" autofocus>

Si agregas autofocus a este campo, se activará automáticamente el mensaje de autocompletado cuando se cargue la página y se mostrarán las contraseñas y llaves de acceso disponibles de inmediato.

Detección de atributos

Antes de invocar una llamada a la API de WebAuthn condicional, verifica lo siguiente:

  • El navegador admite WebAuthn con PublicKeyCredential.

Browser Support

  • Chrome: 67.
  • Edge: 18.
  • Firefox: 60.
  • Safari: 13.

Source

Browser Support

  • Chrome: 108.
  • Edge: 108.
  • Firefox: 119.
  • Safari: 16.

Source

En el siguiente fragmento, se muestra cómo puedes verificar si el navegador admite estas funciones:

// Availability of window.PublicKeyCredential means WebAuthn is usable.  
if (window.PublicKeyCredential &&  
    PublicKeyCredential.isConditionalMediationAvailable) {  
  // Check if conditional mediation is available.  
  const isCMA = await PublicKeyCredential.isConditionalMediationAvailable();  
  if (isCMA) {  
    // Call WebAuthn authentication  
  }  
}  

Cómo recuperar información del backend

Tu backend debe proporcionar varias opciones al frontend para iniciar la llamada a navigator.credentials.get(). Por lo general, estas opciones se recuperan como un objeto JSON desde un extremo en tu servidor.

Entre las propiedades clave del objeto options, se incluyen las siguientes:

  • challenge: Es un desafío generado por el servidor en un ArrayBuffer (por lo general, codificado en Base64URL para el transporte JSON). Esto es esencial para evitar ataques de repetición. Tu servidor debe generar un desafío nuevo para cada intento de acceso y debe invalidarlo después de un tiempo breve o si un intento falla.
  • allowCredentials: Un array de descriptores de credenciales. Pasa un array vacío. Esto le solicita al navegador que enumere todas las credenciales para el rpId especificado.
  • userVerification: especifica tu preferencia para la verificación del usuario, como requerir un bloqueo de pantalla del dispositivo. El valor predeterminado y recomendado es "preferred". Los valores posibles son los siguientes:

    • "required": El autenticador debe realizar la verificación del usuario (como un PIN o datos biométricos). La operación fallará si no se puede realizar la verificación.
    • "preferred": El autenticador intenta verificar al usuario, pero la operación puede tener éxito sin él.
    • "discouraged": El autenticador debe evitar la verificación del usuario si es posible.
    .
  • rpId: El ID de la parte de confianza, por lo general, el dominio de tu sitio web (como example.com). Este valor debe coincidir exactamente con el rp.id que se usó cuando se creó la credencial de llave de acceso.

Tu servidor debe crear este objeto de opciones. Los valores de ArrayBuffer (como challenge) deben estar codificados en Base64URL para el transporte JSON. En el frontend, después de analizar el JSON, usa PublicKeyCredential.parseRequestOptionsFromJSON() para convertir el objeto (incluida la decodificación de cadenas Base64URL) en el formato que espera navigator.credentials.get().

En el siguiente fragmento de código, se muestra cómo puedes recuperar y decodificar la información necesaria para autenticarse con una llave de acceso.

// Fetch an encoded PubicKeyCredentialRequestOptions from the server.
const _options = await fetch('/webauthn/signinRequest');

// Deserialize and decode the PublicKeyCredentialRequestOptions.
const decoded_options = JSON.parse(_options);
const options = PublicKeyCredential.parseRequestOptionsFromJSON(decoded_options);
...

Llama a la API de WebAuthn con la marca conditional para autenticar al usuario

Una vez que tengas preparado el objeto publicKeyCredentialRequestOptions (denominado options en el siguiente código de ejemplo), llama a navigator.credentials.get() para iniciar la autenticación de llave de acceso condicional.

// To abort a WebAuthn call, instantiate an AbortController.
const abortController = new AbortController();

// Invoke WebAuthn to authenticate with a passkey.
const credential = await navigator.credentials.get({
  publicKey: options,
  signal: abortController.signal,
  // Specify 'conditional' to activate conditional UI
  mediation: 'conditional'
});

Parámetros clave para esta llamada:

  • publicKey: Debe ser el objeto publicKeyCredentialRequestOptions (llamado options en el ejemplo) que recuperaste de tu servidor y que procesaste en el paso anterior.
  • signal: Pasar un indicador de AbortController (como abortController.signal) te permite cancelar de forma programática la solicitud de get(). Esto es útil cuando quieres invocar otra llamada a WebAuthn.
  • mediation: 'conditional': Esta es la marca fundamental que hace que la llamada a WebAuthn sea condicional. Le indica al navegador que espere la interacción del usuario con un mensaje de autocompletado en lugar de mostrar un diálogo modal de inmediato.

Envía la credencial de clave pública que se muestra al servidor de RP.

Si el usuario selecciona una llave de acceso y verifica correctamente su identidad (por ejemplo, con el bloqueo de pantalla del dispositivo), se resuelve la promesa de navigator.credentials.get(). Esto muestra un objeto PublicKeyCredential en tu frontend.

La promesa se puede rechazar por varios motivos. Para controlar estos errores en tu código, debes verificar la propiedad name del objeto Error:

  • NotAllowedError: El usuario canceló la operación o no se seleccionó ninguna llave de acceso.
  • AbortError: La operación se abortó, posiblemente por tu código con un AbortController.
  • Otras excepciones: Se produjo un error inesperado. Por lo general, el navegador le muestra un diálogo de error al usuario.

El objeto PublicKeyCredential contiene varias propiedades. Entre las propiedades clave relevantes para la autenticación, se incluyen las siguientes:

  • id: El ID codificado en base64url de la credencial de llave de acceso autenticada.
  • rawId: Una versión de ArrayBuffer del ID de credencial.
  • response.clientDataJSON: Un ArrayBuffer de datos del cliente. Este campo contiene información, como el desafío y el origen, que el servidor debe verificar.
  • response.authenticatorData: Un ArrayBuffer de datos de autenticador. Este campo incluye información como el ID del RP.
  • response.signature: Es un ArrayBuffer que contiene la firma. Este valor es el núcleo de la credencial, y tu servidor debe verificar esta firma con la clave pública almacenada de la credencial .
  • response.userHandle: Un ArrayBuffer que contiene el ID de usuario proporcionado durante el registro de la llave de acceso.
  • authenticatorAttachment: Indica si el autenticador es parte del dispositivo cliente (platform) o es externo (cross-platform). Puede ocurrir un archivo adjunto cross-platform si el usuario accedió con un teléfono. En esos casos, considera pedirle que creé una llave de acceso en el dispositivo actual para que le resulte más conveniente en el futuro.
  • type: Este campo siempre se establece como "public-key".

Para enviar este objeto PublicKeyCredential a tu backend, primero llama al método .toJSON(). Este método crea una versión serializable en JSON de la credencial, que controla correctamente la conversión de propiedades ArrayBuffer (como rawId, clientDataJSON, authenticatorData, signature y userHandle) a cadenas codificadas en Base64URL. Luego, usa JSON.stringify() para convertir este objeto en una cadena y enviarlo en el cuerpo de tu solicitud al servidor.

...
// Encode and serialize the PublicKeyCredential.
const _result = credential.toJSON();
const result = JSON.stringify(_result);

// Encode and send the credential to the server for verification.  
const response = await fetch('/webauthn/signinResponse', {
  method: 'post',
  credentials: 'same-origin',
  body: result
});

Verifica la firma

Cuando tu servidor de backend recibe la credencial de clave pública, debe verificar su autenticidad. Esto incluye lo siguiente:

  1. Analiza los datos de la credencial.
  2. Busca la clave pública almacenada asociada con el id de la credencial.
  3. Verifica el signature recibido con la clave pública almacenada.
  4. Validar otros datos, como el desafío y el origen

Recomendamos usar una biblioteca de FIDO/WebAuthn del servidor para controlar estas operaciones de criptografía de forma segura. Puedes encontrar bibliotecas de código abierto en el repositorio de GitHub de awesome-webauthn.

Si la firma y todas las demás afirmaciones son válidas, el servidor puede permitir que el usuario acceda. Para obtener pasos detallados de validación del servidor, consulta Autenticación de llaves de acceso del servidor.

Indica si no se encuentran las credenciales coincidentes en el backend

Si tu servidor de backend no puede encontrar una credencial con un ID coincidente durante el acceso, es posible que el usuario haya borrado esta llave de acceso de tu servidor, pero no de su proveedor. Esta discrepancia puede generar una experiencia del usuario confusa si el proveedor de llaves de acceso sigue sugiriendo una llave de acceso que ya no funciona con tu sitio. Para mejorar esto, debes indicarle al proveedor de llaves de acceso que quite la llave huérfana.

Puedes usar el método PublicKeyCredential.signalUnknownCredential(), que forma parte de la API de Webauthn Signal, para informar al proveedor de llaves de acceso que la credencial especificada se quitó o no existe. Llama a este método estático del cliente si tu servidor indica (por ejemplo, con un código de estado HTTP específico, como 404) que se desconoce un ID de credencial presentado. Proporciona el ID de RP y el ID de credencial desconocida a este método. El proveedor de llaves de acceso, si admite el indicador, debe quitar la llave de acceso.

// Detect authentication failure due to lack of the credential
if (response.status === 404) {
  // Feature detection
  if (PublicKeyCredential.signalUnknownCredential) {
    await PublicKeyCredential.signalUnknownCredential({
      rpId: "example.com",
      credentialId: "vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA" // base64url encoded credential ID
    });
  } else {
    // Encourage the user to delete the passkey from the password manager nevertheless.
    ...
  }
}

Después de la autenticación

Según cómo haya accedido el usuario, sugerimos diferentes flujos para seguir.

Si el usuario accedió sin una llave de acceso

Si el usuario accedió a tu sitio web sin una llave de acceso, es posible que no tenga una registrada para esa cuenta o en su dispositivo actual. Este es un momento oportuno para fomentar la creación de llaves de acceso. Considera los siguientes enfoques:

  • Actualiza las contraseñas a llaves de acceso: Usa la creación condicional, una función de WebAuthn que permite que el navegador cree automáticamente una llave de acceso para el usuario después de un acceso exitoso con contraseña. Esto puede mejorar significativamente la adopción de llaves de acceso, ya que simplifica el proceso de creación. Obtén información sobre cómo funciona y cómo implementarlo en Ayuda a los usuarios a adoptar las llaves de acceso de forma más fluida.
  • Solicita la creación de una llave de acceso de forma manual: Alienta a los usuarios a crear una llave de acceso. Esto puede ser eficaz después de que un usuario completa un proceso de acceso más complejo, como la autenticación de varios factores (MFA). Sin embargo, evita los mensajes excesivos, que pueden ser invasivos para la experiencia del usuario".

Para ver cómo puedes motivar a los usuarios a crear una llave de acceso y conocer otras prácticas recomendadas, consulta los ejemplos sobre cómo comunicar las llaves de acceso a los usuarios.

Si el usuario accedió con una llave de acceso

Después de que un usuario accede correctamente con una llave de acceso, tienes varias oportunidades para mejorar aún más su experiencia y mantener la coherencia de la cuenta.

Se recomienda crear una llave de acceso nueva después de una autenticación multidispositivo

Si un usuario accede con una llave de acceso mediante un mecanismo multidispositivo (por ejemplo, escaneando un código QR con su teléfono), es posible que la llave de acceso que usó no se almacene de forma local en el dispositivo al que accede. Esto puede suceder en los siguientes casos:

  • Tienen una llave de acceso, pero en un proveedor que no es compatible con el navegador o el sistema operativo de acceso.
  • Perdió el acceso al proveedor de llaves de acceso en el dispositivo en el que accedió, pero aún hay una llave de acceso disponible en otro.

En esta situación, considera pedirle al usuario que cree una llave de acceso nueva en el dispositivo actual. Esto puede evitar que repitan el proceso de acceso en varios dispositivos en el futuro. Para determinar si el usuario accedió con una llave de acceso multidispositivo, verifica la propiedad authenticatorAttachment de la credencial. Si su valor es "cross-platform", indica una autenticación entre dispositivos. Si es así, explícale las ventajas de crear una llave de acceso nueva y guíalo a través del proceso de creación.

Sincroniza los detalles de la llave de acceso con el proveedor mediante indicadores

Para garantizar la coherencia y una mejor experiencia del usuario, tu entidad de confianza (RP) puede usar la API de WebAuthn Signals para comunicar actualizaciones sobre las credenciales y la información del usuario al proveedor de llaves de acceso.

Por ejemplo, para mantener la precisión de la lista de llaves de acceso del proveedor de llaves de acceso de un usuario, mantén las credenciales del backend sincronizadas. Puedes indicar que una llave de acceso ya no existe para que los proveedores de llaves de acceso puedan quitar las llaves de acceso innecesarias.

Del mismo modo, puedes indicar si un usuario actualiza su nombre de usuario o nombre visible en tu servicio para mantener actualizada la información del usuario que muestra el proveedor de llaves de acceso (por ejemplo, en los diálogos de selección de cuenta).

Para obtener más información sobre las prácticas recomendadas para mantener la coherencia entre las llaves de acceso, consulta Cómo mantener la coherencia entre las llaves de acceso y las credenciales de tu servidor con la API de Signal.

No solicitar un segundo factor

Las llaves de acceso ofrecen una protección sólida y predeterminada contra amenazas comunes, como el phishing. Por lo tanto, un segundo factor de autenticación no agrega un valor de seguridad significativo. En cambio, crea un paso innecesario para los usuarios durante el acceso.

Lista de tareas

  • Permite que los usuarios accedan con una llave de acceso a través del autocompletado de formularios.
  • Indica cuando no se encuentra la credencial coincidente de una llave de acceso en el backend.
  • Pídeles a los usuarios que creen una llave de acceso de forma manual si aún no lo hicieron después de acceder.
  • Crea automáticamente una llave de acceso (creación condicional) después de que el usuario accede con una contraseña (y un segundo factor).
  • Solicita la creación de una llave de acceso local si el usuario accedió con una llave de acceso multidispositivo.
  • Indica al proveedor la lista de llaves de acceso disponibles y los detalles del usuario actualizados (nombre de usuario, nombre visible) después de acceder o cuando se produzcan cambios.

Recursos