1. Antes de comenzar
La API de Web Authentication, también conocida como WebAuthn, te permite crear y utilizar credenciales de clave pública limitadas al origen para autenticar usuarios.
La API admite el uso de autenticadores U2F o FIDO2 con BLE, NFC y roaming de USB, también conocidos como llaves de seguridad, así como un autenticador de plataforma, que les permite a los usuarios autenticarse con sus huellas dactilares o bloqueos de pantalla.
En este codelab, compilarás un sitio web con una funcionalidad de reautenticación sencilla que utiliza un sensor de huellas dactilares. La reautenticación protege los datos de la cuenta porque obliga a los usuarios que ya accedieron a un sitio web a autenticarse nuevamente cuando intentan ingresar a secciones importantes del sitio web o volver a visitarlo después de un tiempo determinado.
Requisitos previos
- Comprensión de los aspectos básicos del funcionamiento de WebAuthn
- Habilidades básicas de programación con JavaScript
Actividades
- Compila un sitio web con una funcionalidad de reautenticación sencilla que utiliza un sensor de huellas dactilares
Requisitos
- Uno de los siguientes dispositivos:
- Un dispositivo Android, preferentemente con un sensor biométrico
- Un iPhone o iPad con ID táctil o ID facial en iOS 14 o versiones posteriores
- Una MacBook Pro o Air con ID táctil en macOS Big Sur o versiones posteriores
- Windows 10 19H1 o versiones posteriores con Windows Hello configurado
- Uno de los siguientes navegadores:
- Google Chrome 67 o versiones posteriores
- Microsoft Edge 85 o versiones posteriores
- Safari 14 o versiones posteriores
2. Prepárate
En este codelab, usarás un servicio llamado glitch. Aquí puedes editar el código del cliente y del servidor con JavaScript, además de implementarlos al instante.
Ve a https://glitch.com/edit=\"_/webauthn-codelab-start.
Ve cómo funciona
Sigue estos pasos para ver el estado inicial del sitio web:
- Haz clic en Mostrar > En una ventana nueva para ver el sitio web publicado.
- Ingresa un nombre de usuario y haz clic en Siguiente.
- Ingresa una contraseña y haz clic en Acceder.
La contraseña se ignora, pero aún tienes la autenticación. De esta forma, llegas a la página principal.
- Haz clic en Reintentar autenticación y repite los pasos del dos al cuatro.
- Haz clic en Cerrar sesión.
Ten en cuenta que debes ingresar la contraseña cada vez que intentes acceder. De esta forma, se emula un usuario que debe volver a autenticarse antes de poder acceder a una sección importante de un sitio web.
Crea un remix del código
- Navega al Codelab de API de WebAuthn/FIDO2.
- Haz clic en el nombre de tu proyecto > Remix Project para bifurcarlo y continuar con tu propia versión en una URL nueva.
3. Registra una credencial con una huella digital
Debes registrar una credencial generada por un UVPA, un autenticador integrado en el dispositivo que verifica la identidad del usuario. Aunque depende del dispositivo del usuario, por lo general, se ve como un sensor de huellas dactilares.
Agrega esta función a la página /home
:
Crea la función registerCredential()
Crea una función registerCredential()
, que registra una credencial nueva.
public/client.js
export const registerCredential = async () => {
};
Obtén el desafío y otras opciones del extremo del servidor
Antes de solicitarle al usuario que registre una credencial nueva, pídele al servidor que muestre los parámetros para pasar en WebAuthn, incluido un desafío. Afortunadamente, ya tienes un extremo de servidor que cumple con esos parámetros.
Agrega el siguiente código a registerCredential()
.
public/client.js
const opts = {
attestation: 'none',
authenticatorSelection: {
authenticatorAttachment: 'platform',
userVerification: 'required',
requireResidentKey: false
}
};
const options = await _fetch('/auth/registerRequest', opts);
El protocolo entre un servidor y un cliente no forma parte de la especificación de WebAuthn. Sin embargo, este codelab está diseñado para cumplir con la especificación de WebAuthn y el objeto JSON que pasas al servidor es muy similar a PublicKeyCredentialCreationOptions
, por lo que te resultará intuitivo. La siguiente tabla contiene los parámetros importantes que puedes pasar al servidor y, además, explica sus funciones:
Parámetros | Descripciones | ||
| Preferencia de transmisión de certificación: | ||
| Array de | ||
|
| Filtra los autenticadores disponibles. Si quieres adjuntar un autenticador al dispositivo, usa " | |
| Determina si la verificación del usuario local del autenticador es " | ||
| Usa |
Para obtener más información sobre estas opciones, consulta 5.4. Opciones para la creación de credenciales (diccionario PublicKeyCredentialCreationOptions
).
Las siguientes son opciones de ejemplo que recibes del servidor.
{
"rp": {
"name": "WebAuthn Codelab",
"id": "webauthn-codelab.glitch.me"
},
"user": {
"displayName": "User Name",
"id": "...",
"name": "test"
},
"challenge": "...",
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
}, {
"type": "public-key",
"alg": -257
}
],
"timeout": 1800000,
"attestation": "none",
"excludeCredentials": [
{
"id": "...",
"type": "public-key",
"transports": [
"internal"
]
}
],
"authenticatorSelection": {
"authenticatorAttachment": "platform",
"userVerification": "required"
}
}
Crea una credencial
- Dado que estas opciones se entregan codificadas para pasar por el protocolo HTTP, vuelve a convertir algunos parámetros en objetos binarios, específicamente
user.id
,challenge
y, también, instancias deid
incluidas en el arrayexcludeCredentials
:
public/client.js
options.user.id = base64url.decode(options.user.id);
options.challenge = base64url.decode(options.challenge);
if (options.excludeCredentials) {
for (let cred of options.excludeCredentials) {
cred.id = base64url.decode(cred.id);
}
}
- Llama al método
navigator.credentials.create()
para crear una credencial nueva.
Con esta llamada, el navegador interactúa con el autenticador e intenta verificar la identidad del usuario con el UVPA.
public/client.js
const cred = await navigator.credentials.create({
publicKey: options,
});
Cuando el usuario verifique su identidad, deberías recibir un objeto de credencial que puedes enviar al servidor para registrar el autenticador.
Registra la credencial en el extremo del servidor
Este es un objeto de credencial de ejemplo que deberías haber recibido.
{
"id": "...",
"rawId": "...",
"type": "public-key",
"response": {
"clientDataJSON": "...",
"attestationObject": "..."
}
}
- Del mismo modo que cuando recibes un objeto de opción para registrar una credencial, codifica los parámetros binarios de la credencial para que se pueda entregar al servidor como una string:
public/client.js
const credential = {};
credential.id = cred.id;
credential.rawId = base64url.encode(cred.rawId);
credential.type = cred.type;
if (cred.response) {
const clientDataJSON =
base64url.encode(cred.response.clientDataJSON);
const attestationObject =
base64url.encode(cred.response.attestationObject);
credential.response = {
clientDataJSON,
attestationObject,
};
}
- Almacena el ID de las credenciales de forma local a fin de usarlo para la autenticación cuando el usuario regrese:
public/client.js
localStorage.setItem(`credId`, credential.id);
- Envía el objeto al servidor y, si muestra
HTTP code 200
, considera que la credencial nueva se registró correctamente.
public/client.js
return await _fetch('/auth/registerResponse' , credential);
Ahora tienes la función registerCredential()
completa.
Código final para esta sección
public/client.js
...
export const registerCredential = async () => {
const opts = {
attestation: 'none',
authenticatorSelection: {
authenticatorAttachment: 'platform',
userVerification: 'required',
requireResidentKey: false
}
};
const options = await _fetch('/auth/registerRequest', opts);
options.user.id = base64url.decode(options.user.id);
options.challenge = base64url.decode(options.challenge);
if (options.excludeCredentials) {
for (let cred of options.excludeCredentials) {
cred.id = base64url.decode(cred.id);
}
}
const cred = await navigator.credentials.create({
publicKey: options
});
const credential = {};
credential.id = cred.id;
credential.rawId = base64url.encode(cred.rawId);
credential.type = cred.type;
if (cred.response) {
const clientDataJSON =
base64url.encode(cred.response.clientDataJSON);
const attestationObject =
base64url.encode(cred.response.attestationObject);
credential.response = {
clientDataJSON,
attestationObject
};
}
localStorage.setItem(`credId`, credential.id);
return await _fetch('/auth/registerResponse' , credential);
};
...
4. Compila la IU para registrar, obtener y quitar credenciales
Es bueno tener una lista de credenciales registradas y botones para quitarlas.
Compila el marcador de posición de la IU
Agrega una IU para enumerar credenciales y un botón a fin de registrar una credencial nueva. Dependiendo de si la función está disponible o no, debes quitar la clase hidden
del mensaje de advertencia o el botón para registrar una credencial nueva. ul#list
es el marcador de posición para agregar una lista de credenciales registradas.
views/home.html
<p id="uvpa_unavailable" class="hidden">
This device does not support User Verifying Platform Authenticator. You can't register a credential.
</p>
<h3 class="mdc-typography mdc-typography--headline6">
Your registered credentials:
</h3>
<section>
<div id="list"></div>
</section>
<mwc-button id="register" class="hidden" icon="fingerprint" raised>Add a credential</mwc-button>
Detección de funciones y disponibilidad de UVPA
Sigue estos pasos para verificar la disponibilidad de UVPA:
- Examina
window.PublicKeyCredential
para verificar si WebAuthn está disponible. - Llama a
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
para verificar si hay un UVPA disponible. En caso afirmativo, verás el botón para registrar una credencial nueva. Si alguno no está disponible, se mostrará el mensaje de advertencia.
views/home.html
const register = document.querySelector('#register');
if (window.PublicKeyCredential) {
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
.then(uvpaa => {
if (uvpaa) {
register.classList.remove('hidden');
} else {
document
.querySelector('#uvpa_unavailable')
.classList.remove('hidden');
}
});
} else {
document
.querySelector('#uvpa_unavailable')
.classList.remove('hidden');
}
Obtén y muestra una lista de credenciales
- Crea una función
getCredentials()
para obtener credenciales registradas y mostrarlas en una lista. Afortunadamente, ya tienes un extremo práctico en el servidor/auth/getKeys
, desde el que puedes obtener las credenciales registradas para el usuario que accedió.
El JSON que se muestra incluye información de credenciales, como id
y publicKey
. Puedes compilar HTML para mostrárselo al usuario.
views/home.html
const getCredentials = async () => {
const res = await _fetch('/auth/getKeys');
const list = document.querySelector('#list');
const creds = html`${res.credentials.length > 0 ? res.credentials.map(cred => html`
<div class="mdc-card credential">
<span class="mdc-typography mdc-typography--body2">${cred.credId}</span>
<pre class="public-key">${cred.publicKey}</pre>
<div class="mdc-card__actions">
<mwc-button id="${cred.credId}" @click="${removeCredential}" raised>Remove</mwc-button>
</div>
</div>`) : html`
<p>No credentials found.</p>
`}`;
render(creds, list);
};
- Invoca
getCredentials()
para mostrar las credenciales disponibles en cuanto el usuario llega a la página/home
.
views/home.html
getCredentials();
Quita la credencial
En la lista de credenciales, agregaste un botón para quitar cada credencial. Puedes enviar una solicitud a /auth/removeKey
junto con el parámetro de búsqueda credId
a fin de quitarlas.
public/client.js
export const unregisterCredential = async (credId) => {
localStorage.removeItem('credId');
return _fetch(`/auth/removeKey?credId=${encodeURIComponent(credId)}`);
};
- Agrega
unregisterCredential
a la sentenciaimport
existente.
views/home.html
import { _fetch, unregisterCredential } from '/client.js';
- Agrega una función a la que se llame cuando el usuario haga clic en Quitar.
views/home.html
const removeCredential = async e => {
try {
await unregisterCredential(e.target.id);
getCredentials();
} catch (e) {
alert(e);
}
};
Registra una credencial
Puedes llamar a registerCredential()
para registrar una credencial nueva cuando el usuario haga clic en Agregar una credencial.
- Agrega
registerCredential
a la sentenciaimport
existente.
views/home.html
import { _fetch, registerCredential, unregisterCredential } from '/client.js';
- Invoca
registerCredential()
con opciones paranavigator.credentials.create()
.
No olvides renovar la lista de credenciales mediante una llamada a getCredentials()
después del registro.
views/home.html
register.addEventListener('click', e => {
registerCredential().then(user => {
getCredentials();
}).catch(e => alert(e));
});
Ahora deberías poder registrar una credencial nueva y mostrar la información correspondiente. Puedes probarla en tu sitio web publicado.
Código final para esta sección
views/home.html
...
<p id="uvpa_unavailable" class="hidden">
This device does not support User Verifying Platform Authenticator. You can't register a credential.
</p>
<h3 class="mdc-typography mdc-typography--headline6">
Your registered credentials:
</h3>
<section>
<div id="list"></div>
<mwc-fab id="register" class="hidden" icon="add"></mwc-fab>
</section>
<mwc-button raised><a href="/reauth">Try reauth</a></mwc-button>
<mwc-button><a href="/auth/signout">Sign out</a></mwc-button>
</main>
<script type="module">
import { _fetch, registerCredential, unregisterCredential } from '/client.js';
import { html, render } from 'https://unpkg.com/lit-html@1.0.0/lit-html.js?module';
const register = document.querySelector('#register');
if (window.PublicKeyCredential) {
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
.then(uvpaa => {
if (uvpaa) {
register.classList.remove('hidden');
} else {
document
.querySelector('#uvpa_unavailable')
.classList.remove('hidden');
}
});
} else {
document
.querySelector('#uvpa_unavailable')
.classList.remove('hidden');
}
const getCredentials = async () => {
const res = await _fetch('/auth/getKeys');
const list = document.querySelector('#list');
const creds = html`${res.credentials.length > 0 ? res.credentials.map(cred => html`
<div class="mdc-card credential">
<span class="mdc-typography mdc-typography--body2">${cred.credId}</span>
<pre class="public-key">${cred.publicKey}</pre>
<div class="mdc-card__actions">
<mwc-button id="${cred.credId}" @click="${removeCredential}" raised>Remove</mwc-button>
</div>
</div>`) : html`
<p>No credentials found.</p>
`}`;
render(creds, list);
};
getCredentials();
const removeCredential = async e => {
try {
await unregisterCredential(e.target.id);
getCredentials();
} catch (e) {
alert(e);
}
};
register.addEventListener('click', e => {
registerCredential({
attestation: 'none',
authenticatorSelection: {
authenticatorAttachment: 'platform',
userVerification: 'required',
requireResidentKey: false
}
})
.then(user => {
getCredentials();
})
.catch(e => alert(e));
});
</script>
...
public/client.js
...
export const unregisterCredential = async (credId) => {
localStorage.removeItem('credId');
return _fetch(`/auth/removeKey?credId=${encodeURIComponent(credId)}`);
};
...
5. Autentica el usuario con una huella digital
Ahora tienes una credencial registrada y lista para ser la forma de autenticar al usuario. Lo siguiente es agregar la funcionalidad de reautenticación al sitio web. Esta es la experiencia del usuario:
Cuando un usuario llega a la página /reauth
, ve un botón Autenticar siempre que la autenticación biométrica sea posible. La autenticación con una huella digital (UVPA) comienza cuando el usuario presiona Autenticar, luego se autentica y llega a la página /home
. Si la autenticación biométrica no está disponible o falla, la IU recurre al uso de la contraseña existente.
Crea la función authenticate()
Crea una función llamada authenticate()
, que verifica la identidad del usuario con una huella digital. Agrega el código JavaScript aquí:
public/client.js
export const authenticate = async () => {
};
Obtén el desafío y otras opciones del extremo del servidor
- Antes de la autenticación, examina si el usuario tiene un ID de credencial almacenado y, de ser así, configúralo como un parámetro de búsqueda.
Cuando proporcionas un ID de credencial junto con otras opciones, el servidor puede entregar allowCredentials
relevantes para que la verificación del usuario sea confiable.
public/client.js
const opts = {};
let url = '/auth/signinRequest';
const credId = localStorage.getItem(`credId`);
if (credId) {
url += `?credId=${encodeURIComponent(credId)}`;
}
- Antes de pedirle al usuario que se autentique, solicítale al servidor que muestre un desafío y otros parámetros. Llama a
_fetch()
conopts
como un argumento para enviar una solicitud POST al servidor.
public/client.js
const options = await _fetch(url, opts);
A continuación, se muestran opciones de ejemplo que deberías recibir (cumplen con PublicKeyCredentialRequestOptions
).
{
"challenge": "...",
"timeout": 1800000,
"rpId": "webauthn-codelab.glitch.me",
"userVerification": "required",
"allowCredentials": [
{
"id": "...",
"type": "public-key",
"transports": [
"internal"
]
}
]
}
La opción más importante es allowCredentials
. Cuando recibes opciones del servidor, allowCredentials
debe ser un solo objeto en un array o un array vacío. Esto dependerá de que haya o no una credencial con el ID en el parámetro de búsqueda en el servidor.
- Resuelve la promesa con
null
siallowCredentials
es un array vacío para que la IU solicite una contraseña.
if (options.allowCredentials.length === 0) {
console.info('No registered credentials found.');
return Promise.resolve(null);
}
Verifica el usuario de forma local y obtén una credencial
- Dado que estas opciones se entregan codificadas para pasar por el protocolo HTTP, vuelve a convertir algunos parámetros en objetos binarios, específicamente
challenge
y, también, las instancias deid
incluidas en el arrayallowCredentials
:
public/client.js
options.challenge = base64url.decode(options.challenge);
for (let cred of options.allowCredentials) {
cred.id = base64url.decode(cred.id);
}
- Llama al método
navigator.credentials.get()
para verificar la identidad del usuario con un UVPA.
public/client.js
const cred = await navigator.credentials.get({
publicKey: options
});
Cuando el usuario verifique su identidad, deberías recibir un objeto de credencial que puedes enviar al servidor para autenticarlo.
Verifica la credencial
A continuación, se muestra un objeto de ejemplo PublicKeyCredential
(response
es AuthenticatorAssertionResponse
) que deberías haber recibido:
{
"id": "...",
"type": "public-key",
"rawId": "...",
"response": {
"clientDataJSON": "...",
"authenticatorData": "...",
"signature": "...",
"userHandle": ""
}
}
- Codifica los parámetros binarios de la credencial para que se pueda entregar al servidor como una string:
public/client.js
const credential = {};
credential.id = cred.id;
credential.type = cred.type;
credential.rawId = base64url.encode(cred.rawId);
if (cred.response) {
const clientDataJSON =
base64url.encode(cred.response.clientDataJSON);
const authenticatorData =
base64url.encode(cred.response.authenticatorData);
const signature =
base64url.encode(cred.response.signature);
const userHandle =
base64url.encode(cred.response.userHandle);
credential.response = {
clientDataJSON,
authenticatorData,
signature,
userHandle,
};
}
- Envía el objeto al servidor y, si muestra
HTTP code 200
, considera que el usuario accedió correctamente:
public/client.js
return await _fetch(`/auth/signinResponse`, credential);
Ahora tienes la función authentication()
completa.
Código final para esta sección
public/client.js
...
export const authenticate = async () => {
const opts = {};
let url = '/auth/signinRequest';
const credId = localStorage.getItem(`credId`);
if (credId) {
url += `?credId=${encodeURIComponent(credId)}`;
}
const options = await _fetch(url, opts);
if (options.allowCredentials.length === 0) {
console.info('No registered credentials found.');
return Promise.resolve(null);
}
options.challenge = base64url.decode(options.challenge);
for (let cred of options.allowCredentials) {
cred.id = base64url.decode(cred.id);
}
const cred = await navigator.credentials.get({
publicKey: options
});
const credential = {};
credential.id = cred.id;
credential.type = cred.type;
credential.rawId = base64url.encode(cred.rawId);
if (cred.response) {
const clientDataJSON =
base64url.encode(cred.response.clientDataJSON);
const authenticatorData =
base64url.encode(cred.response.authenticatorData);
const signature =
base64url.encode(cred.response.signature);
const userHandle =
base64url.encode(cred.response.userHandle);
credential.response = {
clientDataJSON,
authenticatorData,
signature,
userHandle,
};
}
return await _fetch(`/auth/signinResponse`, credential);
};
...
6. Habilita la experiencia de reautenticación
Compila la IU
Cuando el usuario regrese, te sugerimos que vuelva a autenticarse de la manera más fácil y segura posible. Este es uno de los puntos fuertes de la autenticación biométrica. Sin embargo, hay casos en los que la autenticación biométrica puede no funcionar. Estos son algunos:
- El UVPA no está disponible.
- El usuario aún no registró ninguna credencial en su dispositivo.
- Se libera el almacenamiento y el dispositivo ya no recuerda el ID de la credencial.
- El usuario no puede verificar su identidad por algún motivo, por ejemplo, cuando tiene las manos mojadas o está usando una mascarilla.
Por eso, siempre es importante que ofrezcas otras opciones de acceso como resguardos. En este codelab, usarás la solución de contraseña basada en formularios.
- Agrega la IU para mostrar un botón de autenticación que invoque la autenticación biométrica además del formulario de contraseñas.
Usa la clase hidden
para mostrar y ocultar de forma selectiva una de ellas según el estado del usuario.
views/reauth.html
<div id="uvpa_available" class="hidden">
<h2>
Verify your identity
</h2>
<div>
<mwc-button id="reauth" raised>Authenticate</mwc-button>
</div>
<div>
<mwc-button id="cancel">Sign-in with password</mwc-button>
</div>
</div>
- Agrega
class="hidden"
al formulario:
views/reauth.html
<form id="form" method="POST" action="/auth/password" class="hidden">
Detección de funciones y disponibilidad de UVPA
Los usuarios deben acceder con una contraseña si se cumple una de las siguientes condiciones:
- WebAuthn no está disponible.
- UVPA no está disponible.
- No se puede encontrar un ID de credencial para este UVPA.
Oculta o muestra el botón de autenticación de manera selectiva:
views/reauth.html
if (window.PublicKeyCredential) {
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
.then(uvpaa => {
if (uvpaa && localStorage.getItem(`credId`)) {
document
.querySelector('#uvpa_available')
.classList.remove('hidden');
} else {
form.classList.remove('hidden');
}
});
} else {
form.classList.remove('hidden');
}
Resguardo para el formulario de contraseña
El usuario también debería tener la posibilidad de acceder con una contraseña.
Muestra el formulario de contraseña y oculta el botón de autenticación cuando el usuario haga clic en Acceder con contraseña:
views/reauth.html
const cancel = document.querySelector('#cancel');
cancel.addEventListener('click', e => {
form.classList.remove('hidden');
document
.querySelector('#uvpa_available')
.classList.add('hidden');
});
Invoca la autenticación biométrica
Por último, habilita la autenticación biométrica.
- Agrega
authenticate
a la sentenciaimport
existente:
views/reauth.html
import { _fetch, authenticate } from '/client.js';
- Invoca
authenticate()
cuando el usuario presione Autenticar para iniciar la autenticación biométrica.
Asegúrate de que se produzca un error de autenticación biométrica antes de ofrecer el formulario de contraseña.
views/reauth.html
const button = document.querySelector('#reauth');
button.addEventListener('click', e => {
authenticate().then(user => {
if (user) {
location.href = '/home';
} else {
throw 'User not found.';
}
}).catch(e => {
console.error(e.message || e);
alert('Authentication failed. Use password to sign-in.');
form.classList.remove('hidden');
document.querySelector('#uvpa_available').classList.add('hidden');
});
});
Código final para esta sección
views/reauth.html
...
<main class="content">
<div id="uvpa_available" class="hidden">
<h2>
Verify your identity
</h2>
<div>
<mwc-button id="reauth" raised>Authenticate</mwc-button>
</div>
<div>
<mwc-button id="cancel">Sign-in with password</mwc-button>
</div>
</div>
<form id="form" method="POST" action="/auth/password" class="hidden">
<h2>
Enter a password
</h2>
<input type="hidden" name="username" value="{{username}}" />
<div class="mdc-text-field mdc-text-field--filled">
<span class="mdc-text-field__ripple"></span>
<label class="mdc-floating-label" id="password-label">password</label>
<input type="password" class="mdc-text-field__input" aria-labelledby="password-label" name="password" />
<span class="mdc-line-ripple"></span>
</div>
<input type="submit" class="mdc-button mdc-button--raised" value="Sign-In" />
<p class="instructions">password will be ignored in this demo.</p>
</form>
</main>
<script src="https://unpkg.com/material-components-web@7.0.0/dist/material-components-web.min.js"></script>
<script type="module">
new mdc.textField.MDCTextField(document.querySelector('.mdc-text-field'));
import { _fetch, authenticate } from '/client.js';
const form = document.querySelector('#form');
form.addEventListener('submit', e => {
e.preventDefault();
const form = new FormData(e.target);
const cred = {};
form.forEach((v, k) => cred[k] = v);
_fetch(e.target.action, cred)
.then(user => {
location.href = '/home';
})
.catch(e => alert(e));
});
if (window.PublicKeyCredential) {
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
.then(uvpaa => {
if (uvpaa && localStorage.getItem(`credId`)) {
document
.querySelector('#uvpa_available')
.classList.remove('hidden');
} else {
form.classList.remove('hidden');
}
});
} else {
form.classList.remove('hidden');
}
const cancel = document.querySelector('#cancel');
cancel.addEventListener('click', e => {
form.classList.remove('hidden');
document
.querySelector('#uvpa_available')
.classList.add('hidden');
});
const button = document.querySelector('#reauth');
button.addEventListener('click', e => {
authenticate().then(user => {
if (user) {
location.href = '/home';
} else {
throw 'User not found.';
}
}).catch(e => {
console.error(e.message || e);
alert('Authentication failed. Use password to sign-in.');
form.classList.remove('hidden');
document.querySelector('#uvpa_available').classList.add('hidden');
});
});
</script>
...
7. Felicitaciones.
¡Terminaste este codelab!
Más información
- Web Authentication: una API para acceder a las Credenciales de clave pública de nivel 1
- Introducción a la API de WebAuthn
- Taller FIDO WebAuthn
- Guía de WebAuthn: DUOSEC
- Tu primera API de FIDO2 para Android
Agradecemos especialmente a Yuriy Ackermann de FIDO Alliance por su ayuda.