1. Avant de commencer
L'API Web Authentication, également appelée WebAuthn, vous permet de créer et d'utiliser des identifiants de clé publique au niveau de l'origine pour authentifier les utilisateurs.
L'API est compatible avec l'utilisation d'authentificateurs U2F ou FIDO2 BLE, NFC et USB (également appelés "clés de sécurité") ainsi qu'avec l'authentificateur de plate-forme, qui permet aux utilisateurs de s'authentifier avec leur empreinte ou le verrouillage de l'écran.
Dans cet atelier de programmation, vous allez concevoir un site Web avec une fonctionnalité de réauthentification simple basée sur un lecteur d'empreinte digitale. La réauthentification protège les données de compte, car elle oblige les utilisateurs qui se sont déjà connectés à un site Web à s'authentifier à nouveau lorsqu'ils tentent d'accéder à des sections importantes de ce site ou de revenir sur le site après un certain temps.
Conditions préalables
- Comprendre les principes de base de WebAuthn
- Maîtriser les compétences de base en programmation avec JavaScript
Objectifs de l'atelier
- Créer un site Web avec une fonctionnalité de réauthentification simple basée sur un lecteur d'empreinte digitale
Ce dont vous avez besoin
- L'un des appareils suivants :
- Un appareil Android, de préférence équipé d'un capteur biométrique
- Un iPhone ou un iPad avec Touch ID ou Face ID sur iOS 14 ou version ultérieure
- Un MacBook Pro ou Air avec Touch ID sur macOS Big Sur ou version ultérieure
- Windows 10 19H1 ou version ultérieure avec Windows Hello configuré
- L'un des navigateurs suivants :
- Google Chrome 67 ou version ultérieure
- Microsoft Edge 85 ou version ultérieure
- Safari 14 ou version ultérieure
2. Configuration
Dans cet atelier de programmation, vous allez utiliser un service appelé glitch. Vous pouvez y modifier le code côté client et côté serveur avec JavaScript, et le déployer instantanément.
Accédez à https://glitch.com/edit/#!/webauthn-codelab-start.
Fonctionnement
Pour connaître l'état initial du site Web, procédez comme suit :
- Cliquez sur Show (Afficher) > In a New Window (Dans une nouvelle fenêtre) pour voir le site Web en ligne.
- Saisissez le nom d'utilisateur de votre choix, puis cliquez sur Next (Suivant).
- Saisissez un mot de passe, puis cliquez sur Sign-in (Se connecter).
Le mot de passe est ignoré, mais vous êtes toujours authentifié. Vous accédez à la page d'accueil.
- Cliquez sur Try reauth (Essayer de vous réauthentifier), puis répétez les deuxième, troisième et quatrième étapes.
- Cliquez sur Sign out (Se déconnecter).
Notez que vous devez saisir le mot de passe chaque fois que vous essayez de vous connecter. Cela permet d'émuler un utilisateur qui doit s'authentifier à nouveau pour accéder à une section importante d'un site Web.
Remixer le code
- Accédez à l'atelier de programmation sur l'API WebAuthn/FIDO2.
- Cliquez sur le nom de votre projet > Remix project (Remixer le projet) pour le dupliquer et continuer avec votre propre version sur une nouvelle URL.
3. Enregistrer un identifiant avec une empreinte
Vous devez enregistrer un identifiant généré par un UVPA, c'est-à-dire un authentificateur intégré à l'appareil qui permet de valider l'identité de l'utilisateur. Il s'agit généralement d'un lecteur d'empreinte digitale, en fonction de l'appareil de l'utilisateur.
Ajoutez cette fonctionnalité à la page /home
:
Créer une fonction registerCredential()
Créez une fonction registerCredential()
qui enregistre les nouveaux identifiants.
public/client.js
export const registerCredential = async () => {
};
Récupérer la question d'authentification et d'autres options auprès du point de terminaison du serveur
Avant de demander à l'utilisateur d'enregistrer un nouvel identifiant, demandez au serveur de renvoyer les paramètres pour qu'ils transmettent les informations d'identification dans WebAuthn, y compris une question d'authentification. Heureusement, vous avez déjà un point de terminaison de serveur qui répond avec ces paramètres.
Ajoutez le code suivant à registerCredential()
.
public/client.js
const opts = {
attestation: 'none',
authenticatorSelection: {
authenticatorAttachment: 'platform',
userVerification: 'required',
requireResidentKey: false
}
};
const options = await _fetch('/auth/registerRequest', opts);
Le protocole entre un serveur et un client ne fait pas partie de la spécification WebAuthn. Cependant, cet atelier de programmation est conçu pour respecter la spécification WebAuthn. Pour que vous puissiez l'utiliser de manière intuitive, l'objet JSON que vous transmettez au serveur ressemble beaucoup à PublicKeyCredentialCreationOptions
. Le tableau suivant contient les paramètres importants que vous pouvez transmettre au serveur et explique à quoi ils servent :
Paramètres | Descriptions | ||
| Préférence pour le transfert d'attestation : | ||
| Tableau de | ||
|
| Filtre des authentificateurs disponibles. Si vous souhaitez associer un authentificateur à l'appareil, utilisez " | |
| Déterminez si la validation de l'utilisateur local par l'authentificateur est " | ||
| Utilisez |
Pour en savoir plus sur ces options, consultez la section 5.4. Options for Credential Creation (dictionary PublicKeyCredentialCreationOptions
) (Options de création d'identifiants (dictionnaire PublicKeyCredentialCreationOptions
)).
Voici des exemples d'options que le serveur envoie :
{
"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"
}
}
Créer un identifiant
- Étant donné que ces options sont diffusées avec un encodage pour passer par le protocole HTTP, convertissez certains paramètres à nouveau en binaire (plus précisément,
user.id
,challenge
et les instances deid
comprises dans le tableauexcludeCredentials
) :
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);
}
}
- Appelez la méthode
navigator.credentials.create()
pour créer un identifiant.
Grâce à cet appel, le navigateur interagit avec l'authentificateur et tente de valider l'identité de l'utilisateur à l'aide de l'UVPA.
public/client.js
const cred = await navigator.credentials.create({
publicKey: options,
});
Une fois que l'utilisateur a validé son identité, vous devriez recevoir un objet d'identification que vous pouvez envoyer au serveur. Ensuite, enregistrez l'authentificateur.
Enregistrer les identifiants sur le point de terminaison du serveur
Voici un exemple d'objet d'identification que vous devriez avoir reçu.
{
"id": "...",
"rawId": "...",
"type": "public-key",
"response": {
"clientDataJSON": "...",
"attestationObject": "..."
}
}
- De même que lorsque vous recevez un objet d'option permettant d'enregistrer un identifiant, vous devez encoder les paramètres binaires de l'identifiant afin de le transmettre au serveur sous forme de chaîne :
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,
};
}
- Stockez l'ID d'identification localement afin de pouvoir l'utiliser pour l'authentification lorsque l'utilisateur revient :
public/client.js
localStorage.setItem(`credId`, credential.id);
- Envoyez l'objet au serveur et, s'il renvoie
HTTP code 200
, vous pouvez considérer que les nouveaux identifiants sont correctement enregistrés.
public/client.js
return await _fetch('/auth/registerResponse' , credential);
Vous disposez désormais de l'intégralité de la fonction registerCredential()
.
Code final de cette section
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. Créer l'UI pour enregistrer, obtenir et supprimer les identifiants
Il est utile de disposer d'une liste d'identifiants enregistrés et de boutons pour les supprimer.
Créer l'espace réservé de l'UI
Ajoutez une UI pour lister les identifiants et un bouton pour en enregistrer un nouveau. Selon que la fonctionnalité est disponible ou non, vous devez supprimer la classe hidden
du message d'avertissement ou du bouton permettant d'enregistrer un nouvel identifiant. ul#list
est l'espace réservé permettant d'ajouter une liste d'identifiants enregistrés.
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>
Détection des fonctionnalités et disponibilité de l'UVPA
Pour vérifier la disponibilité d'un UVPA, procédez comme suit :
- Examinez
window.PublicKeyCredential
pour vérifier si WebAuthn est disponible. - Appelez
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
pour vérifier si un UVPA est disponible. S'ils sont disponibles, le bouton permettant d'enregistrer un nouvel identifiant s'affiche. Vous verrez le message d'avertissement si l'un de ces éléments n'est pas disponible.
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');
}
Récupérer et afficher une liste d'identifiants
- Créez une fonction
getCredentials()
pour obtenir les identifiants enregistrés et les afficher dans une liste. Heureusement, vous disposez déjà d'un point de terminaison pratique sur le serveur/auth/getKeys
à partir duquel vous pouvez récupérer les identifiants enregistrés pour l'utilisateur connecté.
Le fichier JSON renvoyé inclut des informations d'identification, telles que id
et publicKey
. Vous pouvez créer du code HTML pour le présenter à l'utilisateur.
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);
};
- Appelez
getCredentials()
pour afficher les identifiants disponibles dès que l'utilisateur accède à la page/home
.
views/home.html
getCredentials();
Supprimer l'identifiant
Dans la liste des identifiants, vous avez ajouté un bouton permettant de les supprimer. Pour ce faire, vous pouvez envoyer une requête à /auth/removeKey
avec le paramètre de requête credId
.
public/client.js
export const unregisterCredential = async (credId) => {
localStorage.removeItem('credId');
return _fetch(`/auth/removeKey?credId=${encodeURIComponent(credId)}`);
};
- Ajoutez
unregisterCredential
à l'instructionimport
existante.
views/home.html
import { _fetch, unregisterCredential } from '/client.js';
- Ajoutez une fonction à appeler lorsque l'utilisateur clique sur Remove (Supprimer).
views/home.html
const removeCredential = async e => {
try {
await unregisterCredential(e.target.id);
getCredentials();
} catch (e) {
alert(e);
}
};
Enregistrer un identifiant
Vous pouvez appeler registerCredential()
pour enregistrer un nouvel identifiant lorsque l'utilisateur clique sur Add a credential (Ajouter un identifiant).
- Ajoutez
registerCredential
à l'instructionimport
existante.
views/home.html
import { _fetch, registerCredential, unregisterCredential } from '/client.js';
- Appelez
registerCredential()
avec les options associées ànavigator.credentials.create()
.
N'oubliez pas de renouveler la liste des identifiants en appelant getCredentials()
après l'enregistrement.
views/home.html
register.addEventListener('click', e => {
registerCredential().then(user => {
getCredentials();
}).catch(e => alert(e));
});
Vous devriez maintenant pouvoir enregistrer un nouvel identifiant et afficher les informations correspondantes. Vous pouvez l'essayer sur votre site Web en ligne.
Code final de cette section
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/[email protected]/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. Authentifier l'utilisateur à l'aide d'une empreinte
Vous disposez désormais d'un identifiant enregistré que vous pouvez utiliser pour authentifier l'utilisateur. Vous allez maintenant ajouter une fonctionnalité de réauthentification au site Web. Voici comment se déroule l'expérience utilisateur :
Lorsqu'un utilisateur accède à la page /reauth
, un bouton Authenticate (S'authentifier) s'affiche si l'authentification biométrique est possible. L'authentification à l'aide d'une empreinte digitale (UVPA) commence lorsque l'utilisateur appuie sur Authenticate (S'authentifier), parvient à s'authentifier et accède à la page /home
. Si l'authentification biométrique n'est pas disponible ou si elle échoue, l'UI bascule sur le formulaire de mot de passe existant.
Créer une fonction authenticate()
Créez une fonction appelée authenticate()
, qui valide l'identité de l'utilisateur à l'aide d'une empreinte. Vous devez ajouter du code JavaScript ici :
public/client.js
export const authenticate = async () => {
};
Récupérer la question d'authentification et d'autres options auprès du point de terminaison du serveur
- Avant l'authentification, vérifiez si un utilisateur dispose d'un ID d'identification stocké et, le cas échéant, définissez-le en tant que paramètre de requête.
Lorsque vous fournissez un ID d'identification ainsi que d'autres options, le serveur peut fournir des allowCredentials
pertinents. Ainsi, la validation de l'utilisateur est plus fiable.
public/client.js
const opts = {};
let url = '/auth/signinRequest';
const credId = localStorage.getItem(`credId`);
if (credId) {
url += `?credId=${encodeURIComponent(credId)}`;
}
- Avant d'inviter l'utilisateur à s'authentifier, demandez au serveur de renvoyer une question d'authentification et d'autres paramètres. Appelez
_fetch()
avecopts
comme argument pour envoyer une requête POST au serveur.
public/client.js
const options = await _fetch(url, opts);
Voici des exemples d'options que vous devriez recevoir (en lien avec PublicKeyCredentialRequestOptions
) :
{
"challenge": "...",
"timeout": 1800000,
"rpId": "webauthn-codelab.glitch.me",
"userVerification": "required",
"allowCredentials": [
{
"id": "...",
"type": "public-key",
"transports": [
"internal"
]
}
]
}
L'option la plus importante est allowCredentials
. Lorsque vous recevez des options de la part du serveur, allowCredentials
doit être un objet unique dans un tableau ou un tableau vide, selon qu'un identifiant avec l'ID dans le paramètre de requête existe côté serveur.
- Résolvez la promesse avec
null
lorsqueallowCredentials
est un tableau vide afin que l'UI demande à nouveau un mot de passe.
if (options.allowCredentials.length === 0) {
console.info('No registered credentials found.');
return Promise.resolve(null);
}
Valider l'utilisateur localement et obtenir des identifiants
- Étant donné que ces options sont diffusées avec un encodage pour passer par le protocole HTTP, convertissez certains paramètres à nouveau en binaire (plus précisément,
challenge
et les instances deid
comprises dans le tableauallowCredentials
) :
public/client.js
options.challenge = base64url.decode(options.challenge);
for (let cred of options.allowCredentials) {
cred.id = base64url.decode(cred.id);
}
- Appelez la méthode
navigator.credentials.get()
pour valider l'identité de l'utilisateur à l'aide d'un UVPA.
public/client.js
const cred = await navigator.credentials.get({
publicKey: options
});
Une fois que l'utilisateur a validé son identité, vous devriez recevoir un objet d'identification que vous pouvez envoyer au serveur. Ensuite, authentifiez l'utilisateur.
Valider l'identifiant
Voici un exemple d'objet PublicKeyCredential
(response
correspond à AuthenticatorAssertionResponse
) que vous devriez avoir reçu :
{
"id": "...",
"type": "public-key",
"rawId": "...",
"response": {
"clientDataJSON": "...",
"authenticatorData": "...",
"signature": "...",
"userHandle": ""
}
}
- Encodez les paramètres binaires de l'identifiant afin qu'ils soient distribués au serveur sous forme de chaîne :
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,
};
}
- Envoyez l'objet au serveur et, s'il renvoie
HTTP code 200
, vous pouvez considérer que l'utilisateur est connecté.
public/client.js
return await _fetch(`/auth/signinResponse`, credential);
Vous disposez désormais de l'intégralité de la fonction authentication()
.
Code final de cette section
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. Activer l'expérience de réauthentification
Créer l'UI
Lorsque l'utilisateur revient, vous devez lui demander de s'authentifier à nouveau de la façon la plus simple et la plus sécurisée possible. C'est là qu'intervient l'authentification biométrique. Toutefois, dans certains cas, ce type d'authentification peut ne pas fonctionner :
- L'UVPA n'est pas disponible.
- L'utilisateur n'a pas encore enregistré d'identifiants sur son appareil.
- L'espace de stockage est vide et l'appareil ne se souvient plus de l'ID d'identification.
- Un problème empêche l'utilisateur de valider son identité. Par exemple, son doigt est mouillé ou il porte un masque.
C'est pourquoi il est important de toujours fournir des options de connexion de remplacement. Dans cet atelier de programmation, vous allez utiliser la solution du mot de passe basé sur un formulaire.
- Ajoutez une UI pour afficher un bouton d'authentification qui appelle l'authentification biométrique en plus du formulaire de mot de passe.
Utilisez la classe hidden
pour afficher et masquer l'une d'entre elles en fonction de l'état de l'utilisateur.
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>
- Ajoutez
class="hidden"
au formulaire :
views/reauth.html
<form id="form" method="POST" action="/auth/password" class="hidden">
Détection des fonctionnalités et disponibilité de l'UVPA
Les utilisateurs doivent se connecter avec un mot de passe si l'une des conditions suivantes est remplie :
- WebAuthn n'est pas disponible.
- L'UVPA n'est pas disponible.
- Aucun ID d'identification associé à cet UVPA n'est visible.
Choisissez d'afficher ou de masquer le bouton d'authentification :
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');
}
Basculer sur le formulaire de mot de passe
L'utilisateur doit également pouvoir choisir de se connecter avec un mot de passe.
Affichez le formulaire de mot de passe et masquez le bouton d'authentification lorsque l'utilisateur clique sur Sign in with password (Se connecter avec un mot de passe) :
views/reauth.html
const cancel = document.querySelector('#cancel');
cancel.addEventListener('click', e => {
form.classList.remove('hidden');
document
.querySelector('#uvpa_available')
.classList.add('hidden');
});
Appeler l'authentification biométrique
Pour terminer, activez l'authentification biométrique.
- Ajoutez
authenticate
à l'instructionimport
existante :
views/reauth.html
import { _fetch, authenticate } from '/client.js';
- Appelez
authenticate()
quand l'utilisateur appuie sur Authenticate (S'authentifier) pour lancer l'authentification biométrique.
Assurez-vous que le formulaire de mot de passe s'affiche lorsque l'authentification biométrique échoue.
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');
});
});
Code final de cette section
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/[email protected]/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. Félicitations
Vous avez terminé cet atelier de programmation.
En savoir plus
- Web Authentication : une API pour accéder aux identifiants de clé publique (niveau 1)
- Présentation de l'API WebAuthn
- Atelier FIDO WebAuthn
- Guide WebAuthn : DUOSEC
- Votre première API Android FIDO2
Nous tenons à remercier Yuriy Ackermann de FIDO Alliance pour son aide.