Crea una experiencia de acceso que aproveche las llaves de acceso y, al mismo tiempo, se adapte a los usuarios que usan contraseñas.
Las llaves de acceso reemplazan a las contraseñas y hacen que las cuentas de usuario en la Web sean más seguras, sencillas y fáciles de usar. Sin embargo, la transición de la autenticación basada en contraseñas a la basada en llaves de acceso puede complicar la experiencia del usuario. El uso del autocompletado de formularios para sugerir llaves de acceso puede ayudar a crear una experiencia unificada.
¿Por qué usar el autocompletado de formularios para acceder con una llave de acceso?
Con una llave de acceso, los usuarios pueden acceder a un sitio web con solo usar la huella dactilar, el rostro o el PIN del dispositivo.
Idealmente, no habría usuarios de contraseñas y el flujo de autenticación podría ser tan simple como un botón de inicio de sesión único. Cuando el usuario presiona el botón, aparece un diálogo del selector de cuentas. El usuario puede elegir una cuenta, desbloquear la pantalla para verificar y acceder.
Sin embargo, la transición de la autenticación basada en contraseñas a la basada en llaves de acceso puede ser un desafío. A medida que los usuarios cambien a las llaves de acceso, seguirán existiendo quienes usen contraseñas y los sitios web deberán admitir ambos tipos de usuarios. No se espera que los usuarios recuerden en qué sitios cambiaron a las llaves de acceso, por lo que pedirles que seleccionen qué método usar de antemano sería una UX deficiente.
Las llaves de acceso también son una tecnología nueva. Explicarlas y asegurarse de que los usuarios se sientan cómodos al usarlas puede ser un desafío para los sitios web. Podemos confiar en experiencias de usuario conocidas para autocompletar contraseñas y resolver ambos problemas.
IU condicional
Para crear una experiencia del usuario eficiente para los usuarios de llaves de acceso y contraseñas, puedes incluir llaves de acceso en las sugerencias de autocompletado. Esto se denomina IU condicional y forma parte del estándar WebAuthn.
En cuanto el usuario presiona el campo de entrada de nombre de usuario, aparece un diálogo de sugerencias de autocompletado que destaca las llaves de acceso almacenadas junto con las sugerencias de autocompletado de contraseñas. Luego, el usuario puede elegir una cuenta y usar el bloqueo de pantalla del dispositivo para acceder.
De esta manera, los usuarios pueden acceder a tu sitio web con el formulario existente como si nada hubiera cambiado, pero con el beneficio de seguridad adicional de las llaves de acceso, si tienen una.
Cómo funciona
Para autenticar con una llave de acceso, usa la API de WebAuthn.
Los cuatro componentes de un flujo de autenticación de llaves de acceso son los siguientes: El usuario:
- Backend: Es el servidor de backend que contiene la base de datos de cuentas que almacena la clave pública y otros metadatos sobre la llave de acceso.
- Frontend: Es tu frontend que se comunica con el navegador y envía solicitudes de recuperación al backend.
- Navegador: Es el navegador del usuario que ejecuta tu código JavaScript.
- Autenticador: Es el autenticador del usuario que crea y almacena la llave de acceso. Puede estar en el mismo dispositivo que el navegador (p. ej., cuando usas Windows Hello) o en otro dispositivo, como un teléfono.
- En cuanto un usuario llega al frontend, solicita un desafío al backend para autenticarse con una llave de acceso y llama a
navigator.credentials.get()
para iniciar la autenticación con una llave de acceso. Esto muestra unPromise
. - Cuando el usuario coloca el cursor en el campo de acceso, el navegador muestra un diálogo de autocompletado de contraseñas que incluye llaves de acceso. Aparecerá un diálogo de autenticación si el usuario selecciona una llave de acceso.
- Después de que el usuario verifica su identidad con el bloqueo de pantalla del dispositivo, se resuelve la promesa y se devuelve una credencial de clave pública al frontend.
- El frontend envía la credencial de clave pública al backend. El backend verifica la firma con la clave pública de la cuenta coincidente en la base de datos. Si se realiza correctamente, el usuario accederá.
Cómo autenticar con una llave de acceso mediante el autocompletado del formulario
Cuando un usuario quiera acceder, puedes realizar una llamada get
condicional de WebAuthn para indicar que se pueden incluir llaves de acceso en las sugerencias de autocompletado. Una llamada condicional a la API de navigator.credentials.get()
de WebAuthn no muestra la IU y permanece pendiente hasta que el usuario elige una cuenta para acceder con las sugerencias de Autocompletar. Si el
usuario elige una llave de acceso, el navegador resolverá la promesa con una credencial
en lugar de completar el formulario de acceso. Luego, es responsabilidad de la página permitir que el usuario acceda.
Anota el campo de entrada del formulario
Si es necesario, agrega un atributo autocomplete
al campo input
del nombre de usuario.
Agrega username
y webauthn
como sus tokens para permitir que sugiera llaves de acceso.
<input type="text" name="username" autocomplete="username webauthn" ...>
Detección de atributos
Antes de invocar una llamada a la API de WebAuthn condicional, verifica lo siguiente:
- El navegador admite WebAuthn con
PublicKeyCredential
.
- El navegador admite la IU condicional de WebAuthn con
PublicKeyCredenital.isConditionalMediationAvailable()
.
// 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
}
}
Recupera un desafío del servidor de RP
Recupera un desafío del servidor de RP que se requiere para llamar a navigator.credentials.get()
:
challenge
: Es un desafío generado por el servidor en un ArrayBuffer. Esto es necesario para evitar ataques de reproducción. Asegúrate de generar un nuevo desafío en cada intento de acceso y, luego, ignóralo después de un período determinado o después de que un intento de acceso no se valide. Considéralo como un token CSRF.allowCredentials
: Es un array de credenciales aceptables para esta autenticación. Pasa un array vacío para permitir que el usuario seleccione una llave de acceso disponible de una lista que muestra el navegador.userVerification
: Indica si la verificación del usuario con el bloqueo de pantalla del dispositivo es"required"
,"preferred"
o"discouraged"
. El valor predeterminado es"preferred"
, lo que significa que el autenticador puede omitir la verificación del usuario. Establece esta opción en"preferred"
o omite la propiedad.
Llama a la API de WebAuthn con la marca conditional
para autenticar al usuario
Llama a navigator.credentials.get()
para comenzar a esperar la autenticación del usuario.
// To abort a WebAuthn call, instantiate an `AbortController`.
const abortController = new AbortController();
const publicKeyCredentialRequestOptions = {
// Server generated challenge
challenge: ****,
// The same RP ID as used during registration
rpId: 'example.com',
};
const credential = await navigator.credentials.get({
publicKey: publicKeyCredentialRequestOptions,
signal: abortController.signal,
// Specify 'conditional' to activate conditional UI
mediation: 'conditional'
});
rpId
: Un ID de RP es un dominio, y un sitio web puede especificar su dominio o un sufijo registrable. Este valor debe coincidir con el rp.id que se usó cuando se creó la llave de acceso.
Recuerda especificar mediation: 'conditional'
para que la solicitud sea condicional.
Envía la credencial de clave pública que se muestra al servidor de RP.
Después de que el usuario selecciona una cuenta y da su consentimiento para usar el bloqueo de pantalla del dispositivo, la promesa se resuelve y muestra un objeto PublicKeyCredential
al frontend del RP.
Una promesa puede rechazarse por varias razones diferentes. Debes manejar los errores según corresponda, según la propiedad name
del objeto Error
:
NotAllowedError
: El usuario canceló la operación.- Otras excepciones: Se produjo un error inesperado. El navegador le muestra al usuario un diálogo de error.
El objeto de credencial de clave pública contiene las siguientes propiedades:
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 de RP deberá verificar.response.authenticatorData
: Un ArrayBuffer de datos de autenticador. Este campo contiene información como el ID del RP.response.signature
: Un ArrayBuffer de la firma. Este valor es el núcleo de la credencial y se debe verificar en el servidor.response.userHandle
: Un ArrayBuffer que contenía el ID de usuario establecido en el momento de la creación. Se puede usar este valor en lugar del ID de credencial si el servidor necesita elegir los valores de ID que usa o si el backend desea evitar la creación de un índice en los IDs de credenciales.authenticatorAttachment
: Muestraplatform
cuando esta credencial proviene del dispositivo local. De lo contrario, muestracross-platform
, en particular cuando el usuario utiliza un teléfono para acceder. Si el usuario tuvo que usar un teléfono para acceder, se recomienda que le pidas que cree una llave de acceso en el dispositivo local.type
: Este campo siempre se establece en"public-key"
.
Si usas una biblioteca para controlar el objeto de credencial de clave pública en el servidor RP, te recomendamos que envíes el objeto completo al servidor después de codificarlo de forma parcial con base64url.
Verifica la firma
Cuando recibas la credencial de clave pública en el servidor, pásala a la biblioteca de FIDO para procesar el objeto.
Busca el ID de credencial que coincida con la propiedad id
(si necesitas determinar la cuenta de usuario, usa la propiedad userHandle
, que es el user.id
que especificaste cuando creaste la credencial). Comprueba si se puede verificar el signature
de la credencial con la clave pública almacenada. Para ello, te recomendamos que uses una biblioteca o una solución del servidor en lugar de escribir tu propio código. Puedes encontrar bibliotecas de código abierto en el repositorio de GitHub de awesome-webauth.
Una vez que se verifique la credencial con una clave pública coincidente, haz que el usuario acceda.
Sigue las instrucciones más detalladas en Autenticación de llaves de acceso del servidor.