Saltar al contenido
ecdsa.com
WebAuthn navigator.credentials.create() with excludeCredentialsWebAuthn y llaves de acceso

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

Cómo solucionarlo

  1. 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. 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 })),
    });

Errores relacionados

Explorar toda la base de datos de errores de firma