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
O usuário clicou de novo em “Adicionar chave de acesso” no mesmo dispositivo
comumO caso mais comum. A conta já tem uma credencial neste autenticador; a resposta certa é oferecer o login com ela.
Uma chave de acesso sincronizada chegou a este dispositivo
comumAs Chaves do iCloud, o Gerenciador de Senhas do Google e outros provedores de chaves de acesso sincronizam credenciais. Uma chave de acesso registrada no celular também está presente no notebook, então o registro “do notebook” é excluído.
A lista de exclusão contém credenciais de outros usuários
ocasionalUm bug que lista todas as credenciais (ou as de outra conta que compartilha o dispositivo) bloqueia registros que deveriam dar certo.
Como corrigir
- 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.
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 })), });