Création conditionnelle de clé d'accès (promotion après connexion)

Ce guide explique comment enregistrer automatiquement et de manière silencieuse une clé d'accès pour un utilisateur immédiatement après une connexion réussie par mot de passe, ce qui réduit les frictions et favorise l'adoption des clés d'accès.

Le bon moment pour déclencher l'action

La création automatique de clés d'accès (également appelée "création conditionnelle" ou "promotion silencieuse après connexion") NE DOIT être déclenchée qu'immédiatement après une connexion complète réussie impliquant un mot de passe.

  • N'essayez pas d'utiliser la création conditionnelle pour les flux sans mot de passe (par exemple, les liens magiques, les mots de passe à usage unique par SMS ou la fédération d'identité).
  • Si l'authentification multifacteur est requise, vous DEVEZ attendre que tous les facteurs aient réussi avant de lancer la création conditionnelle.
  • Assurez-vous qu'une session utilisateur valide et authentifiée est active avant d'envoyer des requêtes aux points de terminaison de création.

Mise en œuvre, étape par étape

1. Annuler les actions de saisie automatique précédentes

Si la page de connexion utilise la saisie automatique de formulaire (UI/Get conditionnelle), l'appel d'obtention d'identifiants actif doit être annulé pour éviter les conflits avec le navigateur.

  • Appelez abortController.abort() sur le AbortController associé à la requête de saisie automatique navigator.credentials.get() en attente avant d'appeler navigator.credentials.create().

2. Détection de caractéristiques

Déterminez si la création conditionnelle est disponible en vérifiant conditionalCreate avec PublicKeyCredential.getClientCapabilities().

const capabilities = await PublicKeyCredential.getClientCapabilities();
if (capabilities.conditionalCreate) {
  // Conditional create is available
}

3. Créer une clé d'accès avec la création conditionnelle

  • Transmettez mediation: 'conditional' dans les options navigator.credentials.create(). Cela indique au navigateur de gérer le flux de création de la clé d'accès de manière silencieuse en arrière-plan ou de manière contextuelle, sans afficher de boîtes de dialogue modales intrusives.
  • Renseignez excludeCredentials avec les ID d'identifiants de clé d'accès existants de l'utilisateur pour éviter d'enregistrer des clés en double.

4. Gestion des erreurs silencieuses

  • Encapsulez l'invite de création de clé d'accès (navigator.credentials.create) dans un bloc try/catch. Vous DEVEZ intercepter et ignorer silencieusement les exceptions courantes visibles par l'utilisateur (InvalidStateError, NotAllowedError, AbortError) sans afficher d'UI d'erreur à l'utilisateur.

5. Validation de la présence de l'utilisateur côté serveur

  • Le point de terminaison de validation côté serveur DOIT assouplir l'exigence de présence de l'utilisateur (UP) (requireUserPresence: false) UNIQUEMENT lors de la validation des identifiants produits par un déclencheur de création conditionnelle. La validation stricte de la présence doit rester active pour les créations explicites standards.

6. Gérer correctement l'échec de la validation du serveur

  • Si navigator.credentials.create() aboutit, mais que la récupération de la validation du serveur renvoie une réponse incorrecte (par exemple, échec de la validation de la signature), appelez PublicKeyCredential.signalUnknownCredential() pour éviter que des identifiants orphelins ne restent dans le fournisseur de clés d'accès.

Exemple de code

// optionsFetch and registerVerifyFetch are app-defined server endpoint requests
import { optionsFetch, registerVerifyFetch } from './api.js';

async function triggerConditionalCreate(loginAbortController) {
  const capabilities = await PublicKeyCredential.getClientCapabilities();
  if (capabilities.conditionalCreate !== true) {
    return; // Platform does not support conditional creation
  }

  // 1. Abort any active autofill conditional-get controllers to clear the WebAuthn pipeline
  loginAbortController.abort();

  // 2. Fetch creation options signaling the backend that this is a conditional request
  const creationOptionsJSON = await optionsFetch({ conditional: true });
  const publicKey = PublicKeyCredential.parseCreationOptionsFromJSON(creationOptionsJSON);

  let credential;
  try {
    // 3. Invoke silent credentials creation prompt
    credential = await navigator.credentials.create({ 
      publicKey,
      mediation: 'conditional' // Silent background creation mediation
    });
  } catch (e) {
    // 4. Silently swallow common WebAuthn browser exceptions
    if (['InvalidStateError', 'NotAllowedError', 'AbortError'].includes(e.name)) {
      return; 
    }
    console.error('Unexpected conditional create error:', e);
    return;
  }

  // 5. Server verification step using dedicated Try/Catch block
  let encodedResponse = credential.toJSON();
  try {
    const response = await registerVerifyFetch(encodedResponse);
    if (!response.ok) {
      // If the server verification fails, clean up using Signal API
      if (PublicKeyCredential.signalUnknownCredential) {
        await PublicKeyCredential.signalUnknownCredential({
          rpId, // RP ID must match the one defined on the server
          credentialId: encodedResponse.id
        });
      }
    }
  } catch (serverErr) {
    console.error('Verification network failure:', serverErr);
    if (PublicKeyCredential.signalUnknownCredential) {
      await PublicKeyCredential.signalUnknownCredential({
        rpId, // RP ID must match the one defined on the server
        credentialId: encodedResponse.id
      });
    }
  }
}

Stratégies de remplacement

Solution de secours pour la détection des fonctionnalités de clé d'accès

Installez toujours "webauthn-polyfills" et importez-le dans le contexte. Tant que PublicKeyCredential est compatible, PublicKeyCredential.getClientCapabilities l'est également.

import 'webauthn-polyfills';

Solution de secours pour la synchronisation de l'API Signal

L'API WebAuthn Signal (webauthn-signals) est une optimisation progressive utilisée pour synchroniser les gestionnaires de mots de passe avec l'état des identifiants du serveur.

  • Expérience de remplacement : accès limité via if (PublicKeyCredential.signalUnknownCredential). Si elle n'est pas compatible, la synchronisation de la validation en arrière-plan est ignorée sans générer d'exceptions de navigateur.

Easy JSON Serialization Fallback

Installez toujours "webauthn-polyfills" et importez-le dans le contexte. Tant que PublicKeyCredential est compatible, PublicKeyCredential.parseCreationOptionsFromJSON et PublicKeyCredential.prototype.toJSON le sont également.

import 'webauthn-polyfills';