Message d’erreur
InvalidStateError: The user attempted to register an authenticator that contains one of the credentials already registered with the relying party.
Le texte ci-dessus est celui de Chromium ; les autres navigateurs le formulent différemment. Le nom de la DOMException, `InvalidStateError`, est la partie stable.
WebAuthn navigator.credentials.create() with excludeCredentials — DOMException InvalidStateError (Chromium)
Ce que cela signifie
L’enregistrement a été refusé, car l’authentificateur détient déjà l’un des identifiants que vous avez listés dans `excludeCredentials`. Les parties de confiance (relying parties, RP) envoient cette liste précisément pour empêcher qu’un même authentificateur soit enregistré deux fois pour un compte. C’est donc généralement le résultat voulu : l’utilisateur a déjà une clé d’accès fonctionnelle sur cet appareil. Les navigateurs ne le révèlent qu’après une interaction de l’utilisateur avec l’authentificateur.
Pourquoi cela se produit
L’utilisateur a de nouveau cliqué sur « Ajouter une clé d’accès » sur le même appareil
fréquenteLe cas le plus fréquent. Le compte possède déjà un identifiant sur cet authentificateur ; la bonne réponse consiste à proposer de se connecter avec celui-ci.
Une clé d’accès synchronisée est arrivée sur cet appareil
fréquenteLe trousseau iCloud, le Gestionnaire de mots de passe de Google et les autres fournisseurs de clés d’accès synchronisent les identifiants. Une clé d’accès enregistrée sur le téléphone est aussi présente sur l’ordinateur portable : enregistrer « l’ordinateur portable » est donc exclu.
La liste d’exclusion contient des identifiants d’autres utilisateurs
occasionnelleUn bug qui liste tous les identifiants (ou ceux d’un autre compte qui partage l’appareil) bloque des enregistrements qui devraient réussir.
Comment corriger l’erreur
- 1.
Indiquer à l’utilisateur que tout est déjà configuré
Traitez InvalidStateError comme « une clé d’accès existe déjà ici » et guidez l’utilisateur pour qu’il se connecte avec celle-ci.
js try { const credential = await navigator.credentials.create({ publicKey: options }); await saveCredential(credential); } catch (e) { if (e.name === "InvalidStateError") { showMessage("This device already has a passkey for your account. You can use it to sign in."); } else if (e.name !== "NotAllowedError") { throw e; } } - 2.
N’exclure que les identifiants de l’utilisateur actuel
Construisez `excludeCredentials` à partir des ID d’identifiants stockés de l’utilisateur connecté, et utilisez un `user.id` aléatoire et stable pour chaque compte.
js // Server: exclude only the CURRENT user's credentials (base64url ids from your database). const options = await generateRegistrationOptions({ rpName: "Example", rpID, userName: user.email, userID: user.webauthnUserId, // stable per user, never the email excludeCredentials: user.passkeys.map((p) => ({ id: p.credentialId, transports: p.transports })), });