Créer une clé d'accès pour les connexions sans mot de passe

Les clés d'accès rendent les comptes utilisateur plus sûrs, plus simples et plus faciles à utiliser.

L'utilisation de clés d'accès renforce la sécurité, simplifie les connexions et remplace les mots de passe. Contrairement aux mots de passe standards, que les utilisateurs doivent mémoriser et saisir manuellement, les clés d'accès utilisent les mécanismes de verrouillage de l'écran de l'appareil, tels que les données biométriques ou les codes, et réduisent les risques de hameçonnage et de vol d'identifiants.

Les clés d'accès se synchronisent entre les appareils à l'aide de fournisseurs de clés d'accès tels que le Gestionnaire de mots de passe de Google et le trousseau iCloud.

Vous devez créer une clé d'accès, qui stocke la clé privée de manière sécurisée auprès du fournisseur de clés d'accès, ainsi que les métadonnées nécessaires et sa clé publique stockée sur votre serveur pour l'authentification. La clé privée émet une signature après la validation de l'utilisateur sur le domaine valide, ce qui rend les clés d'accès résistantes au hameçonnage. La clé publique valide la signature sans stocker d'identifiants sensibles, ce qui rend les clés d'accès résistantes au vol d'identifiants.

Fonctionnement de la création d'une clé d'accès

Avant qu'un utilisateur puisse se connecter avec une clé d'accès, vous devez la créer, l'associer à un compte utilisateur et stocker sa clé publique sur votre serveur.

Vous pouvez demander aux utilisateurs de créer une clé d'accès dans l'une des situations suivantes:

  • Pendant ou après l'inscription.
  • Après vous être connecté
  • Après vous être connecté à l'aide d'une clé d'accès sur un autre appareil (c'est-à-dire que [authenticatorAttachment](https://web.dev/articles/passkey-form-autofill#authenticator-attachment) est cross-platform).
  • Sur une page dédiée où les utilisateurs peuvent gérer leurs clés d'accès.

Pour créer une clé d'accès, vous devez utiliser l'API WebAuthn.

Le flux d'enregistrement d'une clé d'accès comporte quatre composants:

  • Backend: stocke les informations du compte utilisateur, y compris la clé publique.
  • Implémentation côté client: communique avec le navigateur et récupère les données nécessaires à partir du backend.
  • Navigateur: exécute votre code JavaScript et interagit avec l'API WebAuthn.
  • Fournisseur de clés d'accès: crée et stocke la clé d'accès. Il s'agit généralement d'un gestionnaire de mots de passe tel que le Gestionnaire de mots de passe de Google ou d'une clé de sécurité.
Processus de création et d'enregistrement d'une clé d'accès
Procédé de création et d'enregistrement d'une clé d'accès.

Avant de créer une clé d'accès, assurez-vous que le système remplit les conditions préalables suivantes:

  • Le compte utilisateur est validé à l'aide d'une méthode sécurisée (par exemple, par e-mail, par validation par téléphone ou par fédération d'identité) dans un délai très court.

  • Le frontend et le backend peuvent communiquer de manière sécurisée pour échanger des données d'identifiants.

  • Le navigateur est compatible avec WebAuthn et la création de clés d'accès.

Nous pouvons vous montrer comment vérifier la plupart d'entre eux dans les sections suivantes.

Une fois que le système remplit ces conditions, le processus suivant est exécuté pour créer une clé d'accès:

  1. Le système déclenche le processus de création de clé d'accès lorsque l'utilisateur lance l'action (par exemple, en cliquant sur un bouton "Créer une clé d'accès" sur sa page de gestion des clés d'accès ou après avoir terminé son inscription).
  2. Le frontend demande les données d'identification nécessaires au backend, y compris les informations utilisateur, un défi et des ID d'identifiants pour éviter les doublons.
  3. Le frontend appelle navigator.credentials.create() pour inviter le fournisseur de clés d'accès de l'appareil à générer une clé d'accès à l'aide des informations du backend. Notez que cet appel renvoie une promesse.
  4. L'appareil de l'utilisateur l'authentifie à l'aide d'une méthode biométrique, d'un code ou d'un schéma pour créer la clé d'accès.
  5. Le fournisseur de clés d'accès crée une clé d'accès et renvoie des identifiants de clé publique au frontend, ce qui résout la promesse.
  6. L'interface envoie les identifiants de clé publique générés au backend.
  7. Le backend stocke la clé publique et d'autres données importantes pour une future authentification.
  8. Le backend avertit l'utilisateur (par exemple, par e-mail) pour confirmer la création de la clé d'accès et détecter les accès non autorisés potentiels.

Ce processus garantit un processus d'enregistrement de clés d'accès sécurisé et fluide pour les utilisateurs.

Compatibilités

La plupart des navigateurs sont compatibles avec WebAuthn, à quelques exceptions près. Pour en savoir plus sur la compatibilité des navigateurs et des systèmes d'exploitation, consultez passkeys.dev.

Créer une clé d'accès

Pour créer une clé d'accès, l'interface doit suivre la procédure suivante:

  1. Vérifiez la compatibilité.
  2. Récupérer des informations à partir du backend
  3. Appelez l'API WebAuth pour créer une clé d'accès.
  4. Envoyez la clé publique renvoyée au backend.
  5. Enregistrez les identifiants.

Les sections suivantes vous expliquent comment procéder.

Vérifier la compatibilité

Avant d'afficher un bouton "Créer une clé d'accès", l'interface doit vérifier si:

  • Le navigateur est compatible avec WebAuthn avec PublicKeyCredential.

Browser Support

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

Source

  • L'appareil est compatible avec un authentificateur de plate-forme (peut créer une clé d'accès et s'authentifier avec elle) avec PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable().

Browser Support

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

Source

Browser Support

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

Source

L'extrait de code suivant montre comment vérifier la compatibilité avant d'afficher les options liées à la clé d'accès.

// Availability of `window.PublicKeyCredential` means WebAuthn is usable.  
// `isUserVerifyingPlatformAuthenticatorAvailable` means the feature detection is usable.  
// `isConditionalMediationAvailable` means the feature detection is usable.  
if (window.PublicKeyCredential &&  
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&  
    PublicKeyCredential.isConditionalMediationAvailable) {  
  // Check if user verifying platform authenticator is available.  
  Promise.all([  
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),  
    PublicKeyCredential.isConditionalMediationAvailable(),  
  ]).then(results => {  
    if (results.every(r => r === true)) {  
      // Display "Create a new passkey" button  
    }  
  });  
}  

Dans cet exemple, le bouton Créer une clé d'accès ne doit s'afficher que si toutes les conditions sont remplies.

Récupérer des informations du backend

Lorsque l'utilisateur clique sur le bouton, récupérez les informations requises dans le backend pour appeler navigator.credentials.create().

L'extrait de code suivant présente un objet JSON contenant les informations requises pour appeler navigator.credentials.create():

// Example `PublicKeyCredentialCreationOptions` contents
{
  challenge: *****,
  rp: {
    name: "Example",
    id: "example.com",
  },
  user: {
    id: *****,
    name: "john78",
    displayName: "John",
  },
  pubKeyCredParams: [{
    alg: -7, type: "public-key"
  },{
    alg: -257, type: "public-key"
  }],
  excludeCredentials: [{
    id: *****,
    type: 'public-key',
    transports: ['internal'],
  }],
  authenticatorSelection: {
    authenticatorAttachment: "platform",
    requireResidentKey: true,
  }
}

Les paires clé-valeur de l'objet contiennent les informations suivantes:

  • challenge : question d'authentification générée par le serveur dans ArrayBuffer pour cet enregistrement.
  • rp.id : un ID de partie de confiance (RP), un domaine et un site Web peuvent spécifier leur propre domaine ou un suffixe enregistrable. Par exemple, si l'origine d'une RP est https://login.example.com:1337, l'ID de RP peut être login.example.com ou example.com. Si l'ID de RP est example.com, l'utilisateur peut s'authentifier sur login.example.com ou sur tout sous-domaine de example.com. Pour en savoir plus, consultez la section Autoriser la réutilisation des clés d'accès sur vos sites avec les requêtes d'origine associées.
  • rp.name : nom de la partie prenante. Cette valeur est obsolète dans WebAuthn L3, mais elle est incluse pour des raisons de compatibilité.
  • user.id : ID utilisateur unique dans ArrayBuffer, généré lors de la création du compte. Il doit être permanent, contrairement à un nom d'utilisateur qui peut être modifiable. L'ID utilisateur identifie un compte, mais ne doit pas contenir d'informations permettant d'identifier personnellement l'utilisateur. Vous disposez probablement déjà d'un ID utilisateur dans votre système, mais si nécessaire, créez-en un spécifiquement pour les clés d'accès afin de le garder exempt de toute information personnelle.
  • user.name : identifiant unique du compte que l'utilisateur reconnaîtra, comme son adresse e-mail ou son nom d'utilisateur. Cet identifiant apparaîtra dans le sélecteur de comptes.
  • user.displayName : nom obligatoire, plus convivial pour le compte. Il ne doit pas nécessairement être unique. Il peut s'agir d'un nom choisi par l'utilisateur. Si vous ne disposez pas d'une valeur appropriée à inclure dans ce champ pour votre site, transmettez une chaîne vide. Ce nom peut s'afficher dans le sélecteur de compte selon le navigateur.
  • pubKeyCredParams : spécifie les algorithmes de clé publique acceptés par la partie prenante. Nous vous recommandons de le définir sur [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]. Il indique la compatibilité de l'algorithme ECDSA avec P-256 et RSA PKCS#1. Leur prise en charge vous permet de bénéficier d'une couverture complète.
  • excludeCredentials : liste des ID d'identifiants déjà enregistrés. Empêche l'enregistrement du même appareil à deux reprises en fournissant une liste des identifiants déjà enregistrés. Si le membre transports est fourni, il doit contenir le résultat de l'appel de getTransports() lors de l'enregistrement de chaque identifiant.
  • authenticatorSelection.authenticatorAttachment : définissez cette valeur sur "platform" avec hint: ['client-device'] si cette création de clé d'accès est une mise à niveau à partir d'un mot de passe, par exemple dans une promotion après une connexion. "platform" indique que le RP souhaite un authentificateur de plate-forme (un authentificateur intégré à l'appareil de plate-forme) qui n'invite pas, par exemple, à insérer une clé de sécurité USB. L'utilisateur dispose d'une option plus simple pour créer une clé d'accès.
  • authenticatorSelection.requireResidentKey : définissez-le sur un true booléen. Les identifiants détectables (clé résidente) stockent les informations utilisateur dans la clé d'accès et permettent aux utilisateurs de sélectionner le compte lors de l'authentification.
  • authenticatorSelection.userVerification : indique si la validation de l'utilisateur à l'aide du verrouillage de l'écran de l'appareil est "required", "preferred" ou "discouraged". La valeur par défaut est "preferred", ce qui signifie que l'authentificateur peut ignorer la validation de l'utilisateur. Définissez ce paramètre sur "preferred" ou omettez la propriété.

Nous vous recommandons de créer l'objet sur le serveur, d'encoder l'ArrayBuffer avec Base64URL et de l'extraire du frontend. Vous pouvez ainsi décoder la charge utile à l'aide de PublicKeyCredential.parseCreationOptionsFromJSON() et la transmettre directement à navigator.credentials.create().

L'extrait de code suivant montre comment récupérer et décoder les informations nécessaires à la création de la clé d'accès.

// Fetch an encoded `PubicKeyCredentialCreationOptions` from the server.
const _options = await fetch('/webauthn/registerRequest');

// Deserialize and decode the `PublicKeyCredentialCreationOptions`.
const decoded_options = JSON.parse(_options);
const options = PublicKeyCredential.parseCreationOptionsFromJSON(decoded_options);
...

Appeler l'API WebAuthn pour créer une clé d'accès

Appelez navigator.credentials.create() pour créer une clé d'accès. L'API renvoie une promesse, attendant l'interaction de l'utilisateur pour afficher une boîte de dialogue modale.

Browser Support

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

Source

// Invoke WebAuthn to create a passkey.
const credential = await navigator.credentials.create({
  publicKey: options
});

Envoyer les identifiants de clé publique renvoyés au backend

Une fois l'utilisateur authentifié à l'aide du verrouillage de l'écran de l'appareil, une clé d'accès est créée et la promesse est résolue, renvoyant un objet PublicKeyCredential au frontend.

La promesse peut être refusée pour différentes raisons. Vous pouvez gérer ces erreurs en vérifiant la propriété name de l'objet Error:

  • InvalidStateError: une clé d'accès existe déjà sur l'appareil. Aucune boîte de dialogue d'erreur ne s'affiche à l'utilisateur. Le site ne doit pas traiter cela comme une erreur. L'utilisateur voulait que l'appareil local soit enregistré, et il l'est.
  • NotAllowedError: l'utilisateur a annulé l'opération.
  • AbortError: l'opération a été annulée.
  • Autres exceptions: une erreur inattendue s'est produite. Le navigateur affiche une boîte de dialogue d'erreur à l'utilisateur.

L'objet d'identifiant de clé publique contient les propriétés suivantes:

  • id : ID de la clé d'accès créée, encodé en Base64URL. Cet identifiant aide le navigateur à déterminer si une clé d'accès correspondante est enregistrée sur l'appareil au moment de l'authentification. Cette valeur doit être stockée dans la base de données sur le backend.
  • rawId : version ArrayBuffer de l'ID des identifiants.
  • response.clientDataJSON : données client encodées au format ArrayBuffer.
  • response.attestationObject : objet d'attestation encodé au format ArrayBuffer. Il contient des informations importantes telles qu'un ID de RP, des indicateurs et une clé publique.
  • authenticatorAttachment : renvoie "platform" lorsque cet identifiant est créé sur un appareil compatible avec les clés d'accès.
  • type: ce champ est toujours défini sur "public-key".

Encodez l'objet avec la méthode .toJSON(), sérialisez-le avec JSON.stringify(), puis envoyez-le au serveur.

...

// 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/registerResponse', {
  method: 'post',
  credentials: 'same-origin',
  body: result
});
...

Enregistrer l'identifiant

Après avoir reçu les identifiants de clé publique sur le backend, nous vous recommandons d'utiliser une bibliothèque ou une solution côté serveur au lieu d'écrire votre propre code pour traiter les identifiants de clé publique.

Vous pouvez ensuite stocker les informations récupérées à partir des identifiants dans la base de données pour une utilisation ultérieure.

La liste suivante inclut les propriétés recommandées à enregistrer:

  • ID d'identifiant : ID d'identifiant renvoyé avec les identifiants de clé publique.
  • Nom des identifiants: nom des identifiants. Nommez-la d'après le fournisseur de clés d'accès à l'origine de sa création, qui peut être identifié à l'aide de l'AAGUID.
  • ID utilisateur: ID utilisateur utilisé pour créer la clé d'accès.
  • Clé publique : clé publique renvoyée avec les identifiants de clé publique. Cela est nécessaire pour valider une assertion de clé d'accès.
  • Date et heure de création: enregistrez la date et l'heure de création de la clé d'accès. Cela permet d'identifier la clé d'accès.
  • Date et heure de la dernière utilisation: enregistre la date et l'heure de la dernière connexion de l'utilisateur à l'aide de la clé d'accès. Cela permet de déterminer la clé d'accès que l'utilisateur a utilisée (ou non).
  • AAGUID : identifiant unique du fournisseur de clés d'accès.
  • Indicateur d'éligibilité à la sauvegarde : vrai si l'appareil est éligible à la synchronisation des clés d'accès. Ces informations aident les utilisateurs à identifier les clés d'accès synchronisables et les clés d'accès liées à l'appareil (non synchronisables) sur la page de gestion des clés d'accès.

Pour obtenir des instructions plus détaillées, consultez la section Enregistrement de clés d'accès côté serveur.

Signaler l'échec de l'enregistrement

Si l'enregistrement d'une clé d'accès échoue, cela peut prêter à confusion pour l'utilisateur. Si une clé d'accès est disponible dans le fournisseur de clés d'accès et pour l'utilisateur, mais que la clé publique associée n'est pas stockée côté serveur, les tentatives de connexion à l'aide de la clé d'accès ne réussiront jamais et le dépannage sera difficile. Veillez à en informer l'utilisateur si c'est le cas.

Pour éviter ce problème, vous pouvez signaler une clé d'accès inconnue au fournisseur de clés d'accès à l'aide de l'API Signal. En appelant PublicKeyCredential.signalUnknownCredential() avec un ID de RP et un ID d'identifiant, le RP peut informer le fournisseur de clés d'accès que l'identifiant spécifié a été supprimé ou n'existe pas. C'est au fournisseur de clés d'accès de gérer ce signal, mais si cette fonctionnalité est prise en charge, la clé d'accès associée doit être supprimée.

// 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.
    ...
  }
}

Pour en savoir plus sur l'API Signal, consultez la section Assurer la cohérence des clés d'accès avec les identifiants sur votre serveur avec l'API Signal.

Envoyer une notification à l'utilisateur

L'envoi d'une notification (par exemple, un e-mail) lorsqu'une clé d'accès est enregistrée aide les utilisateurs à détecter tout accès non autorisé à leur compte. Si un pirate informatique crée une clé d'accès à l'insu de l'utilisateur, la clé d'accès reste disponible pour de futurs cas d'utilisation abusive, même après le changement du mot de passe. La notification avertit l'utilisateur et l'aide à éviter cela.

Checklist

  • Vérifiez l'utilisateur (de préférence par e-mail ou par une méthode sécurisée) avant de lui permettre de créer une clé d'accès.
  • Empêchez la création de clés d'accès en double pour le même fournisseur de clés d'accès à l'aide de excludeCredentials.
  • Enregistrez l'AAGUID pour identifier le fournisseur de clés d'accès et nommer les identifiants de l'utilisateur.
  • Signalez si une tentative d'enregistrement d'une clé d'accès échoue avec PublicKeyCredential.signalUnknownCredential().
  • Envoyez une notification à l'utilisateur après avoir créé et enregistré une clé d'accès pour son compte.

Ressources

Étape suivante : Se connecter avec une clé d'accès via le remplissage automatique de formulaire.