Mensaje de error
InvalidStateError: The user attempted to register an authenticator that contains one of the credentials already registered with the relying party.
Arriba está el texto de Chromium; otros navegadores lo formulan de otra manera. El nombre de la DOMException, `InvalidStateError`, es la parte estable.
WebAuthn navigator.credentials.create() with excludeCredentials — DOMException InvalidStateError (Chromium)
Qué significa
Se rechazó el registro porque el autenticador ya tiene una de las credenciales enumeradas en `excludeCredentials`. La relying party (RP), es decir, la parte que confía en la autenticación, envía esa lista precisamente para impedir que el mismo autenticador se registre dos veces para una cuenta, así que este suele ser el resultado previsto: el usuario ya tiene una llave de acceso que funciona en este dispositivo. Los navegadores solo lo revelan después de que el usuario haya interactuado con el autenticador.
Por qué ocurre
El usuario volvió a hacer clic en «Añadir una llave de acceso» en el mismo dispositivo
frecuenteEl caso más habitual. La cuenta ya tiene una credencial en este autenticador; la respuesta correcta es ofrecer iniciar sesión con ella.
Llegó a este dispositivo una llave de acceso sincronizada
frecuenteEl Llavero de iCloud, el Gestor de contraseñas de Google y otros proveedores de llaves de acceso sincronizan las credenciales. Una llave de acceso registrada en el teléfono también está en el portátil, así que registrar «el portátil» queda excluido.
La lista de exclusión contiene credenciales de otros usuarios
ocasionalUn fallo que enumera todas las credenciales (o las de otra cuenta que comparte el dispositivo) bloquea registros que deberían completarse.
Cómo solucionarlo
- 1.
Decir al usuario que ya tiene todo configurado
Trata InvalidStateError como «aquí ya existe una llave de acceso» y guía al usuario para que inicie sesión con ella.
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.
Excluir solo las credenciales del usuario actual
Construye `excludeCredentials` a partir de los ID de credencial almacenados del usuario que ha iniciado sesión, y usa un `user.id` aleatorio y estable para cada cuenta.
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 })), });