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.

Eiji Kitamura
Eiji Kitamura

L'utilisation de clés d'accès au lieu de mots de passe est un excellent moyen pour les sites Web de renforcer la sécurité de leurs comptes utilisateur, tout en les rendant plus simples et plus faciles à utiliser, et sans mot de passe. Avec une clé d'accès, l'utilisateur peut se connecter à un site Web ou à une application simplement en utilisant son empreinte digitale, son visage ou le code PIN 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 afin 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 sur un autre appareil (c'est-à-dire que l'authenticatorAttachment 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: serveur backend qui contient la base de données des comptes qui stocke la clé publique et d'autres métadonnées sur la clé d'accès.
  • Interface: votre interface qui communique avec le navigateur et envoie des requêtes de récupération au backend.
  • Navigateur: 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 s'agir du gestionnaire de mots de passe 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 de clés 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 le frontend, par exemple en appuyant sur un bouton "Créer une clé d'accès".
  3. L'interface utilisateur demande des informations au backend pour créer une clé d'accès, telles que des informations utilisateur, un défi et les ID d'identifiants à 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 consentement à l'aide du verrouillage de l'écran de l'appareil. La promesse est résolue et des identifiants de clé publique sont renvoyés au frontend.
  6. Le frontend envoie les identifiants de clé publique au backend et stocke l'ID d'identifiant et la clé publique associés au compte utilisateur pour les futures authentifications.

Compatibilités

WebAuthn est compatible avec la plupart des navigateurs, mais il existe de légères différences. Consultez la section Compatibilité des appareils – passkeys.dev pour savoir quelle combinaison de navigateurs et de systèmes d'exploitation permet de créer une clé d'accès.

Créer une clé d'accès

Voici comment un frontend doit fonctionner en cas de demande de création d'une clé d'accès.

Détection de fonctionnalités

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

  • Le navigateur est compatible avec WebAuthn avec PublicKeyCredential.

Navigateurs pris en charge

  • 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().

Navigateurs pris en charge

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

Source

Navigateurs pris en charge

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

Source

// 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 "Create a passkey" (Créer une clé d'accès) ne doit pas s'afficher avant.

Récupérer des informations importantes du backend

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

  • challenge : défi généré 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 traité ici).
  • user.id : ID unique de l'utilisateur. Cette valeur doit être un ArrayBuffer qui n'inclut pas d'informations permettant d'identifier personnellement l'utilisateur, comme une adresse e-mail ou un nom d'utilisateur. Nous recommandons de générer une valeur aléatoire de 16 octets pour chaque compte.
  • user.name : ce champ doit contenir un identifiant unique pour le compte, reconnaissable par l'utilisateur, comme son adresse e-mail ou son nom d'utilisateur. Cet identifiant apparaîtra dans le sélecteur de comptes. (S'il s'agit d'un nom d'utilisateur, spécifiez 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 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.
  • excludeCredentials: empêche l'enregistrement du même appareil 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.

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.

Navigateurs pris en charge

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

Source

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 non expliqués ci-dessus sont les suivants:

  • rp.id : un ID de RP est un domaine, et un site Web peut spécifier son 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.

  • rp.name : nom de l'RP.

  • pubKeyCredParams : ce champ spécifie les algorithmes de clé publique acceptés par la RP. 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.

  • authenticatorSelection.authenticatorAttachment : définissez cette valeur sur "platform" 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'invitera pas l'utilisateur à insérer, 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-le sur une valeur booléenne "true". 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. Pour en savoir plus sur les identifiants visibles, consultez Présentation détaillée des identifiants visibles.

  • 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é.

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

Une fois que l'utilisateur a donné son consentement à l'aide de l'écran de verrouillage 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, et 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.
  • 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 d'identifiant.
  • 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".

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

Enregistrer les identifiants

Lorsque vous recevez les identifiants de 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 à partir des identifiants dans la base de données pour une utilisation ultérieure. La liste suivante inclut quelques propriétés typiques à enregistrer:

  • ID d'identifiant (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 obtenir des instructions plus détaillées, consultez Enregistrement de clés d'accès côté serveur.

Pour authentifier l'utilisateur, consultez Se connecter avec une clé d'accès via le remplissage automatique de formulaire.

Ressources