Pular para o conteúdo
ecdsa.com
WebAuthn navigator.credentials.create() with excludeCredentialsWebAuthn e chaves de acesso

Mensagem de erro

InvalidStateError: The user attempted to register an authenticator that contains one of the credentials already registered with the relying party.

O texto acima é o do Chromium; outros navegadores usam outra redação. A parte estável é o nome da DOMException, `InvalidStateError`.

WebAuthn navigator.credentials.create() with excludeCredentials — DOMException InvalidStateError (Chromium)

O que significa

O registro foi recusado porque o autenticador já guarda uma das credenciais que você listou em `excludeCredentials`. As partes confiáveis (relying parties) enviam essa lista justamente para impedir que o mesmo autenticador seja registrado duas vezes na mesma conta, então em geral esse é o resultado pretendido — o usuário já tem uma chave de acesso (passkey) funcionando neste dispositivo. Os navegadores só revelam isso depois que o usuário interage com o autenticador.

Por que acontece

Como corrigir

  1. 1.

    Avisar o usuário de que tudo já está configurado

    Trate InvalidStateError como “já existe uma chave de acesso aqui” e conduza o usuário a fazer login com ela.

    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 apenas as credenciais do usuário atual

    Monte `excludeCredentials` a partir dos IDs de credencial armazenados do usuário conectado e use um `user.id` aleatório e estável por conta.

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

Erros relacionados

Ver toda a base de erros de assinatura