Si bien las credenciales de FIDO, como las llaves de acceso, tienen como objetivo reemplazar las contraseñas, la mayoría de ellas también pueden liberar al usuario de tener que escribir un nombre de usuario. Esto permite que los usuarios se autentiquen seleccionando una cuenta de una lista de llaves de acceso que tienen para el sitio web actual.
Las versiones anteriores de las llaves de seguridad se diseñaron como métodos de autenticación de 2 pasos y requerían los IDs de las credenciales potenciales, por lo que se debía ingresar un nombre de usuario. Las credenciales que una llave de seguridad puede encontrar sin conocer sus IDs se denominan credenciales detectables. La mayoría de las credenciales de FIDO que se crean hoy en día son credenciales detectables, en particular, las llaves de acceso almacenadas en un administrador de contraseñas o en una llave de seguridad moderna.
Para asegurarte de que tus credenciales se creen como llaves de acceso (credenciales detectables), especifica residentKey
y requireResidentKey
cuando se creen.
Las partes de confianza (RP) pueden usar credenciales detectables omitiendo allowCredentials
durante la autenticación de llaves de acceso. En estos casos, el navegador o el sistema le muestran al usuario una lista de llaves de acceso disponibles, identificadas por la propiedad user.name
establecida en el momento de la creación. Si el usuario selecciona uno, el valor user.id
se incluirá en la firma resultante. Luego, el servidor puede usar ese valor o el ID de credencial que se muestra para buscar la cuenta en lugar de un nombre de usuario escrito.
Las IUs de selector de cuentas, como las que se analizaron antes, nunca muestran credenciales que no se pueden descubrir.
requireResidentKey
y residentKey
Para crear una llave de acceso, especifica authenticatorSelection.residentKey
y authenticatorSelection.requireResidentKey
en navigator.credentials.create()
con los valores que se indican a continuación.
async function register () {
// ...
const publicKeyCredentialCreationOptions = {
// ...
authenticatorSelection: {
authenticatorAttachment: 'platform',
residentKey: 'required',
requireResidentKey: true,
}
};
const credential = await navigator.credentials.create({
publicKey: publicKeyCredentialCreationOptions
});
// This does not run until the user selects a passkey.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// ...
}
residentKey
:
'required'
: Se debe crear una credencial detectable. Si no se puede crear, se muestraNotSupportedError
.'preferred'
: El RP prefiere crear una credencial detectable, pero acepta una credencial no detectable.'discouraged'
: El RP prefiere crear una credencial no detectable, pero acepta una credencial detectable.
requireResidentKey
:
- Esta propiedad se conserva para la retrocompatibilidad con el nivel 1 de WebAuthn, una versión anterior de la especificación. Establece este valor en
true
siresidentKey
es'required'
, de lo contrario, configúralo enfalse
.
allowCredentials
Los RP pueden usar allowCredentials
en navigator.credentials.get()
para controlar la experiencia de autenticación de llaves de acceso. Por lo general, existen tres tipos de experiencias de autenticación con llaves de acceso:
- Cómo mostrar un selector de cuentas modal
- Cómo mostrar el autocompletado de un formulario de llave de acceso
- Reautenticación
Muestra un selector de cuenta modal
Con las credenciales detectables, los RP pueden mostrar un selector de cuenta modal para que el usuario seleccione una cuenta con la que acceder, seguido de la verificación del usuario. Esto es adecuado para el flujo de autenticación con llave de acceso que se inicia presionando un botón dedicado a la autenticación con llave de acceso.
Para lograr esta experiencia del usuario, omite o pasa un array vacío al parámetro allowCredentials
en navigator.credentials.get()
.
async function authenticate() {
// ...
const publicKeyCredentialRequestOptions = {
// Server generated challenge:
challenge: ****,
// The same RP ID as used during registration:
rpId: 'example.com',
// You can omit `allowCredentials` as well:
allowCredentials: []
};
const credential = await navigator.credentials.get({
publicKey: publicKeyCredentialRequestOptions,
signal: abortController.signal
});
// This does not run until the user selects a passkey.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// ...
}
Cómo mostrar el autocompletado de un formulario de llave de acceso
El selector de cuentas modal que se describió anteriormente funciona bien si la mayoría de los usuarios usan llaves de acceso y las tienen disponibles en el dispositivo local. En el caso de un usuario que no tiene llaves de acceso locales, el diálogo modal seguirá apareciendo y le ofrecerá presentar una llave de acceso de otro dispositivo. Mientras realizas la transición de tus usuarios a las llaves de acceso, te recomendamos que evites esa IU para los usuarios que no hayan configurado una.
En cambio, la selección de una llave de acceso se puede incluir en las indicaciones de autocompletado de los campos de un formulario de acceso tradicional, junto con los nombres de usuario y las contraseñas guardados. De esta manera, un usuario con llaves de acceso puede "completar" el formulario de acceso seleccionando su llave de acceso, los usuarios con pares de nombre de usuario y contraseña guardados pueden seleccionarlos, y los usuarios que no tienen ninguno de ellos pueden escribir su nombre de usuario y contraseña.
Esta experiencia del usuario es ideal cuando el RP está en proceso de migración con un uso mixto de contraseñas y llaves de acceso.
Para lograr esta experiencia del usuario, además de pasar un array vacío a la propiedad allowCredentials
o omitir el parámetro, especifica mediation: 'conditional'
en navigator.credentials.get()
y agrega una anotación a un campo de entrada username
HTML con autocomplete="username webauthn"
o a un campo de entrada password
con autocomplete="password webauthn"
.
La llamada a navigator.credentials.get()
no hará que se muestre ninguna IU, pero si el usuario enfoca el campo de entrada con anotaciones, se incluirán las llaves de acceso disponibles en las opciones de autocompletado. Si el usuario selecciona uno, pasará por la verificación de desbloqueo normal del dispositivo y, solo entonces, la promesa que devuelve .get()
se resolverá con un resultado. Si el usuario no selecciona una llave de acceso, la promesa nunca se resuelve.
async function authenticate() {
// ...
const publicKeyCredentialRequestOptions = {
// Server generated challenge:
challenge: ****,
// The same RP ID as used during registration:
rpId: 'example.com',
// You can omit `allowCredentials` as well:
allowCredentials: []
};
const cred = await navigator.credentials.get({
publicKey: publicKeyCredentialRequestOptions,
signal: abortController.signal,
// Specify 'conditional' to activate conditional UI
mediation: 'conditional'
});
// This does not run until the user selects a passkey.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// ...
}
<input type="text" name="username" autocomplete="username webauthn" ...>
Puedes aprender a compilar esta experiencia del usuario en Accede con una llave de acceso mediante el autocompletado de formularios, así como en el codelab Implementa llaves de acceso con el autocompletado de formularios en una app web.
Reautenticación
En algunos casos, como cuando se usan llaves de acceso para la reautenticación, ya se conoce el identificador del usuario. En este caso, nos gustaría usar una llave de acceso sin que el navegador o el SO muestren ningún tipo de selector de cuenta. Para ello, pasa una lista de IDs de credenciales en el parámetro allowCredentials
.
En ese caso, si alguna de las credenciales nombradas está disponible de forma local, se le pedirá al usuario que desbloquee el dispositivo de inmediato. De lo contrario, se le solicita al usuario que presente otro dispositivo (un teléfono o una llave de seguridad) que tenga una credencial válida.
Para lograr esta experiencia del usuario, proporciona una lista de IDs de credenciales para el usuario que accede. El RP debería poder consultarlos porque el usuario ya es conocido. Proporciona los IDs de credenciales como objetos PublicKeyCredentialDescriptor
en la propiedad allowCredentials
en navigator.credentials.get()
.
async function authenticate() {
// ...
const publicKeyCredentialRequestOptions = {
// Server generated challenge:
challenge: ****,
// The same RP ID as used during registration:
rpId: 'example.com',
// Provide a list of PublicKeyCredentialDescriptors:
allowCredentials: [{
id: ****,
type: 'public-key',
transports: [
'internal',
'hybrid'
]
}, {
id: ****,
type: 'public-key',
transports: [
'internal',
'hybrid'
]
}, ...]
};
const credential = await navigator.credentials.get({
publicKey: publicKeyCredentialRequestOptions,
signal: abortController.signal
});
// This does not run until the user selects a passkey.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// ...
}
Un objeto PublicKeyCredentialDescriptor
consta de lo siguiente:
id
: Un ID de la credencial de clave pública que el RP obtuvo en el registro de la llave de acceso.type
: Por lo general, este campo es'public-key'
.transports
: Es una sugerencia de los transportes compatibles con el dispositivo que contiene esta credencial, que usan los navegadores para optimizar la IU que le solicita al usuario que presente un dispositivo externo. Esta lista, si se proporciona, debe contener el resultado de la llamada agetTransports()
durante el registro de cada credencial.
Resumen
Las credenciales detectables hacen que la experiencia de acceso con llave de acceso sea mucho más fácil de usar, ya que les permite omitir la entrada de un nombre de usuario. Con la combinación de residentKey
, requireResidentKey
y allowCredentials
, los RP pueden lograr experiencias de acceso que tengan las siguientes características:
- Muestra un selector de cuenta modal.
- Muestra el autocompletado de un formulario de llave de acceso.
- Reautenticación.
Usa las credenciales detectables de forma inteligente. De esta manera, puedes diseñar experiencias de acceso con llaves de acceso sofisticadas que los usuarios encontrarán fluidas y con las que es más probable que interactúen.