Créer une clé d'accès pour se connecter 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 plutôt que de mots de passe est un excellent moyen pour les sites Web de rendre leurs comptes utilisateur plus sûrs, plus simples, plus faciles à utiliser et sans mot de passe. Avec une clé d'accès, un utilisateur peut se connecter à un site Web ou à une application en utilisant simplement son empreinte digitale, son visage ou le code de son appareil.

Vous devez créer une clé d'accès, l'associer à un compte utilisateur et stocker sa clé publique sur votre serveur pour qu'un utilisateur puisse s'en servir pour se connecter.

Fonctionnement

Un utilisateur peut être invité à créer une clé d'accès dans l'une des situations suivantes:

  • Lorsqu'un utilisateur se connecte à l'aide d'un mot de passe.
  • Lorsqu'un utilisateur se connecte à l'aide d'une clé d'accès depuis un autre appareil (c'est-à-dire que authenticatorAttachment est défini sur cross-platform).
  • sur une page dédiée sur laquelle les utilisateurs peuvent gérer leurs clés d'accès ;

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

Les quatre composants du flux d'enregistrement d'une clé d'accès sont les suivants:

  • Backend: votre serveur backend contenant la base de données des comptes, qui stocke la clé publique et d'autres métadonnées associées à la clé d'accès.
  • Interface: interface qui communique avec le navigateur et envoie des requêtes de récupération au backend.
  • Navigateur: le navigateur de l'utilisateur qui exécute votre code JavaScript.
  • Authenticator: authentificateur de l'utilisateur qui crée et stocke la clé d'accès. Il peut se trouver sur le même appareil que le navigateur (par exemple, lorsque vous utilisez Windows Hello) ou sur un autre appareil, comme un téléphone.
Schéma d'enregistrement d'une clé d'accès

Pour ajouter une clé d'accès à un compte utilisateur existant, procédez comme suit:

  1. Un utilisateur se connecte au site Web.
  2. Une fois l'utilisateur connecté, il demande à créer une clé d'accès sur l'interface, par exemple en appuyant sur le bouton "Créer une clé d'accès".
  3. Pour créer une clé d'accès, l'interface demande au backend des informations telles que des informations sur l'utilisateur, une question d'authentification et les ID d'identification à exclure.
  4. L'interface appelle navigator.credentials.create() pour créer une clé d'accès. Cet appel renvoie une promesse.
  5. Une clé d'accès est créée une fois que l'utilisateur a donné son accord pour utiliser le verrouillage de l'écran de l'appareil. La promesse est résolue et des identifiants de clé publique sont renvoyés à l'interface.
  6. L'interface envoie les identifiants de la clé publique au backend et stocke l'identifiant ainsi que la clé publique associée au compte utilisateur pour les futures authentifications.

Compatibilités

WebAuthn est compatible avec la plupart des navigateurs, à quelques exceptions près. Consultez Device Support - passkeys.dev pour savoir quelle combinaison de navigateurs et de systèmes d'exploitation est compatible avec la création d'une clé d'accès.

Créer une clé d'accès

Voici comment une interface doit fonctionner à la suite d'une requête de création de clé d'accès.

Détection de fonctionnalités

Avant d'afficher le bouton "Créer une clé d'accès", vérifiez les points suivants:

  • Le navigateur est compatible avec WebAuthn.
  • L'appareil est compatible avec un authentificateur de plate-forme (peut créer une clé d'accès et s'authentifier à l'aide de celle-ci).
  • Le navigateur est compatible avec l'interface utilisateur conditionnelle WebAuthn.
// 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  
    }  
  });  
}  

Tant que toutes les conditions ne sont pas remplies, les clés d'accès ne sont pas compatibles avec ce navigateur. Le bouton "Créer une clé d'accès" ne devrait pas s'afficher avant cette date.

Récupérer les informations importantes du backend

Lorsque l'utilisateur clique sur le bouton, récupérez les informations importantes pour appeler navigator.credentials.create() à partir du backend:

  • challenge : question d'authentification générée par le serveur dans ArrayBuffer pour cet enregistrement. Ce paramètre est obligatoire, mais n'est pas utilisé lors de l'enregistrement, sauf si vous effectuez une attestation (sujet avancé non abordé ici).
  • user.id : identifiant unique d'un utilisateur. Cette valeur doit être un ArrayBuffer qui n'inclut pas d'informations permettant d'identifier personnellement l'utilisateur, comme des adresses e-mail ou des noms d'utilisateur. Une valeur aléatoire de 16 octets générée par compte fonctionnera bien.
  • user.name : ce champ doit contenir un identifiant unique du compte que l'utilisateur reconnaît, comme son adresse e-mail ou son nom d'utilisateur. Il s'affichera dans le sélecteur de compte. (Si vous utilisez un nom d'utilisateur, utilisez la même valeur que pour l'authentification par mot de passe.)
  • user.displayName : ce champ est un nom obligatoire et plus convivial pour le compte. Il n'a pas besoin d'être unique et il peut s'agir du nom choisi par l'utilisateur. Si votre site ne dispose pas d'une valeur appropriée à inclure ici, transmettez une chaîne vide. Il peut s'afficher dans le sélecteur de compte selon le navigateur.
  • excludeCredentials : empêche l'enregistrement du même appareil en fournissant une liste des ID déjà enregistrés. Le membre transports, s'il est fourni, doit contenir le résultat de l'appel de getTransports() lors de l'enregistrement de chaque identifiant.

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, en attendant l'interaction de l'utilisateur qui affiche une boîte de dialogue modale.

const publicKeyCredentialCreationOptions = {
  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,
  }
};

const credential = await navigator.credentials.create({
  publicKey: publicKeyCredentialCreationOptions
});

// Encode and send the credential to the server for verification.  

Les paramètres qui ne sont pas expliqués ci-dessus sont les suivants:

  • rp.id : un ID de RP est un domaine et un site Web peut spécifier son 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 spécifié en tant que example.com, l'utilisateur peut s'authentifier sur login.example.com ou sur n'importe quel sous-domaine sur example.com.

  • rp.name : nom du tiers assujetti à des restrictions.

  • pubKeyCredParams : ce champ spécifie les algorithmes de clé publique acceptés par la RP. Nous vous recommandons de la définir sur [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]. Il indique la compatibilité de l'ECDSA avec P-256 et RSA PKCS#1. Leur prise en charge vous permet de bénéficier d'une couverture complète.

  • authenticatorSelection.authenticatorAttachment : définissez ce paramètre sur "platform" si la création de la clé d'accès est une mise à niveau d'un mot de passe (par exemple, dans une promotion après une connexion). "platform" indique que le tiers assujetti à des restrictions souhaite un authentificateur de plate-forme (un authentificateur intégré à l'appareil de la plate-forme) qui n'invite pas l'insertion, par exemple 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 cette valeur sur une valeur booléenne "true". Un identifiant détectable (clé résidente) stocke les informations utilisateur dans la clé d'accès et permet aux utilisateurs de sélectionner le compte lors de l'authentification.

  • authenticatorSelection.userVerification : indique si la validation d'un 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 vérification de l'utilisateur. Définissez cette valeur sur "preferred" ou omettez la propriété.

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

Une fois que l'utilisateur a autorisé l'utilisation du verrouillage de l'écran de l'appareil, une clé d'accès est créée et la promesse est résolue en renvoyant un objet PublicKeyCredential à l'interface.

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 sera présentée à l'utilisateur, et le site ne doit pas considérer cela comme une erreur (l'utilisateur voulait que l'appareil local soit enregistré, et c'est le cas).
  • NotAllowedError: l'utilisateur a annulé l'opération.
  • Autres exceptions: un événement inattendu s'est produit. Le navigateur affiche une boîte de dialogue d'erreur à l'utilisateur.

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

  • id: ID de la clé d'accès créée, encodé en Base64URL. Cet ID aide le navigateur à déterminer si une clé d'accès correspondante se trouve 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 d'identification.
  • response.clientDataJSON : données client encodées dans ArrayBuffer.
  • response.attestationObject : objet d'attestation encodé dans ArrayBuffer. Il contient des informations importantes telles qu'un ID de RP, des options 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".

Si vous utilisez une bibliothèque pour gérer l'objet d'identifiants de clé publique sur le backend, nous vous recommandons d'envoyer l'objet entier au backend après l'avoir partiellement encodé en base64url.

Enregistrer l'identifiant

À la réception des identifiants de la clé publique sur le backend, transmettez-les à la bibliothèque FIDO pour traiter l'objet.

Vous pouvez ensuite stocker les informations récupérées de l'identifiant dans la base de données pour une utilisation ultérieure. La liste suivante inclut des propriétés classiques à enregistrer:

  • Identifiant d'identification (clé primaire)
  • ID utilisateur
  • Clé publique

Les identifiants de clé publique incluent également les informations suivantes que vous pouvez enregistrer dans la base de données:

Pour authentifier l'utilisateur, consultez Se connecter avec une clé d'accès via la saisie automatique des formulaires.

Ressources