Pular para o conteúdo
ecdsa.com
Node.js · WebCrypto

SimpleWebAuthn

O SimpleWebAuthn, de Matthew Miller, a biblioteca de chaves de acesso mais usada em TypeScript: geradores de opções e verificadores de respostas que exigem a verificação do usuário por padrão, aceitam apenas assinaturas de asserção DER e recusam retrocessos do contador.
Licença
MIT
Versão verificada
14.0.3 ·
Código-fonte
github.com/MasterKale/SimpleWebAuthn
Documentação
simplewebauthn.dev/docs/

Visão geral da compatibilidade

WebAuthn

O SimpleWebAuthn divide o WebAuthn em dois pacotes: @simplewebauthn/browser encapsula navigator.credentials, e @simplewebauthn/server gera as opções que uma parte confiável envia e verifica o que volta — formatos de atestado, chaves COSE, contadores e assinaturas. O teste usou o pacote de servidor com um autenticador em software que produziu JSON padrão de registro e de asserção, os mesmos bytes usados nas páginas de Python, Go e Java.

O bug de integração mais comum é a divergência entre o que as opções pedem e o que a verificação exige. Por padrão, generateRegistrationOptions() usa userVerification: "preferred", mas os dois verificadores usam requireUserVerification: true: uma asserção de um autenticador que só comprovou a presença do usuário — uma chave de segurança sem PIN — foi recusada com User verification required, but user could not be verified. Decida uma vez e faça os dois lados dizerem a mesma coisa, como faz o exemplo.

As assinaturas de asserção com ES256 precisam estar em ASN.1 DER, como exige a especificação WebAuthn. Uma assinatura r‖s bruta de 64 bytes é passada ao parser ASN.1 mesmo assim e falha com uma mensagem que depende dos bytes da assinatura — Too big integer, End of input reached before message was fully decoded ou Data does not match to ECDSASigValue ASN1 schema. foram as mais frequentes em 60 credenciais novas —, e nenhuma delas menciona a codificação. Autenticadores e provedores de chaves de acesso produzem DER, então assinaturas brutas costumam indicar um cliente escrito à mão ou um código de teste que chamou o sign() do WebCrypto diretamente; converta-as com o conversor DER ⇄ bruto. Uma assinatura DER high-S foi verificada.

Um contador de assinaturas menor que o armazenado é um erro, não um aviso: a verificação lançou Response counter value 3 was lower than expected 5, e um contador 0 depois de um 5 armazenado falhou da mesma forma. Só uma credencial que sempre informou 0 — como fazem as chaves de acesso sincronizadas — passa com 0. Armazene o contador recebido e decida você mesmo se um retrocesso bloqueia a credencial ou só gera um alerta. A versão 14 coloca o ML-DSA-44 (COSE -48) em primeiro lugar no pubKeyCredParams padrão, à frente de Ed25519 (-8), ES256 (-7) e RS256 (-257), e a credencial Ed25519 foi verificada de ponta a ponta.

Decodifique o que um navegador realmente enviou com o inspetor de WebAuthn, experimente fluxos sem um autenticador real no ambiente de testes de chaves de acesso e identifique modelos de autenticador pelo AAGUID com a consulta de AAGUID.

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/simplewebauthn/run.sh executou SimpleWebAuthn 14.0.3 em Node.js 24.22.0 na imagem de contêiner node:24.22.0-bookworm-slim em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Algoritmos COSE padrão

    -48-8-7-257

    generateRegistrationOptions() lista os algoritmos COSE -48 (ML-DSA-44), -8 (Ed25519), -7 (ES256) e -257 (RS256), nessa ordem.

    Registrado pela verificação
    fact webauthn-default-algorithms = -48, -8, -7, -257
  • Atestado padrão

    none

    A preferência de transmissão do atestado (attestation conveyance) é none por padrão.

    Registrado pela verificação
    fact webauthn-attestation = none
  • Nível padrão de verificação do usuário

    preferred

    authenticatorSelection.userVerification é preferred por padrão.

    Registrado pela verificação
    fact webauthn-user-verification = preferred
  • Configuração padrão de chave residente

    preferred

    authenticatorSelection.residentKey é preferred por padrão.

    Registrado pela verificação
    fact webauthn-resident-key = preferred
  • Verificação do usuário exigida por padrão

    yes

    Os dois verificadores exigem a flag UV, a menos que se passe requireUserVerification: false: um registro e uma asserção só com a flag UP foram recusados com User verification was required, but user could not be verified e User verification required, but user could not be verified.

    Registrado pela verificação
    fact webauthn-uv-enforced-by-default = yes
    evidence webauthn-uv-enforced-by-default: registration with the UV flag clear: throws User verification was required, but user could not be verified
  • Codificação da assinatura de asserção

    der

    Uma asserção ES256 correta com assinatura ASN.1 DER foi verificada.

    Registrado pela verificação
    fact webauthn-assertion-encoding = der
  • Assinatura de asserção r‖s bruta

    rejected

    A mesma asserção com uma assinatura r‖s bruta de 64 bytes lançou um erro do parser ASN.1 cuja mensagem depende dos bytes da assinatura.

    Registrado pela verificação
    fact webauthn-raw-signature = rejected
  • Assinatura de asserção high-S

    accepted

    Uma assinatura DER com s substituído por n − s foi verificada.

    Registrado pela verificação
    fact webauthn-high-s = accepted
  • Contador de assinaturas que retrocede

    rejected

    Um contador abaixo do valor armazenado lançou Response counter value 3 was lower than expected 5; um contador 0 depois de um 5 armazenado também foi recusado, enquanto 0 depois de um 0 armazenado foi verificado.

    Registrado pela verificação
    fact webauthn-counter-regression = rejected
    evidence webauthn-counter-regression: counter 0 after a stored 5: throws Response counter value 0 was lower than expected 5; counter 0 after a stored 0: verified
  • Credenciais Ed25519

    supported

    Uma credencial Ed25519 (COSE -8) foi registrada e autenticada com sucesso.

    Registrado pela verificação
    fact webauthn-ed25519 = supported
    evidence webauthn-ed25519: verified

Uso

Gere opções de registro e de autenticação que peçam a mesma política de verificação do usuário que os verificadores exigem por padrão.
import { generateAuthenticationOptions, generateRegistrationOptions } from "@simplewebauthn/server";

const rpID = "example.com";

// Registration: the options ask for UV only as "preferred"...
const registration = await generateRegistrationOptions({
  rpName: "Example",
  rpID,
  userName: "user@example.com",
  authenticatorSelection: { residentKey: "required", userVerification: "required" },
});

// ...while verifyRegistrationResponse() and verifyAuthenticationResponse()
// require the UV flag by default. Ask for "required" up front (as here), or pass
// requireUserVerification: false when a security key without a PIN is acceptable.
const authentication = await generateAuthenticationOptions({ rpID, userVerification: "required" });

console.log("algorithms:", registration.pubKeyCredParams.map((param) => param.alg).join(", "));
console.log("uv:", registration.authenticatorSelection?.userVerification, authentication.userVerification);
console.log("challenge bytes:", Buffer.from(authentication.challenge, "base64url").length);

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner node:24.22.0-bookworm-slim, e a execução falha se a saída não corresponder.

Configuração
npm init -y >/dev/null && npm install --no-audit --no-fund @simplewebauthn/server@14.0.3
Comando
node usage.mts
A saída deve corresponder a (expressão regular)
algorithms: -48, -8, -7, -257\nuv: required required\nchallenge bytes: 32

Bibliotecas