Перейти к содержимому
ecdsa.com
Node.js · WebCrypto

SimpleWebAuthn

SimpleWebAuthn от Matthew Miller — самая распространённая библиотека ключей доступа для TypeScript: генераторы параметров и функции проверки ответов, которые по умолчанию требуют проверки пользователя, принимают подписи assertion только в DER и отклоняют откат счётчика.
Лицензия
MIT
Проверенная версия
14.0.3 ·
Исходный код
github.com/MasterKale/SimpleWebAuthn
Документация
simplewebauthn.dev/docs/

Совместимость вкратце

WebAuthn

SimpleWebAuthn разделяет WebAuthn на два пакета: @simplewebauthn/browser служит обёрткой над navigator.credentials, а @simplewebauthn/server формирует параметры, которые отправляет доверяющая сторона, и проверяет то, что приходит в ответ: форматы аттестации, ключи COSE, счётчики и подписи. В ходе проверки серверный пакет получал данные от программного аутентификатора: стандартный JSON регистрации и assertion, те же байты, что и для страниц о библиотеках для Python, Go и Java.

Самая частая ошибка интеграции — расхождение между тем, что запрашивают параметры, и тем, что требует проверка. По умолчанию generateRegistrationOptions() задаёт userVerification: "preferred", а обе функции проверки по умолчанию используют requireUserVerification: true: assertion от аутентификатора, который подтвердил только присутствие пользователя (ключ безопасности без PIN), был отклонён с ошибкой User verification required, but user could not be verified. Примите решение один раз и задайте одну и ту же политику с обеих сторон, как в примере.

Подписи assertion для ES256 должны быть в ASN.1 DER, как того требует спецификация WebAuthn. 64-байтовая подпись raw r‖s всё равно попадает в парсер ASN.1, и разбор завершается ошибкой, текст которой зависит от байтов подписи: для 60 новых учётных данных чаще всего встречались Too big integer, End of input reached before message was fully decoded и Data does not match to ECDSASigValue ASN1 schema., и ни одно из этих сообщений не упоминает кодировку. Аутентификаторы и поставщики ключей доступа создают DER, поэтому подпись raw обычно означает самописный клиент или тестовую обвязку, которая вызвала sign() из WebCrypto напрямую; преобразуйте такие подписи в конвертере DER ⇄ raw. Подпись DER с high-S прошла проверку.

Счётчик подписей меньше сохранённого — это ошибка, а не предупреждение: проверка выбросила Response counter value 3 was lower than expected 5, а счётчик 0 при сохранённом 5 привёл к такой же ошибке. Значение 0 проходит только у учётных данных, которые всегда сообщали 0, как синхронизируемые ключи доступа. Сохраняйте полученный счётчик и сами решайте, блокировать ли учётные данные при откате счётчика или только оповещать о нём. Версия 14 ставит ML-DSA-44 (COSE -48) первым в списке pubKeyCredParams по умолчанию, перед Ed25519 (-8), ES256 (-7) и RS256 (-257), а учётные данные Ed25519 прошли сквозную проверку.

Декодируйте то, что браузер действительно отправил, в инспекторе WebAuthn, пробуйте сценарии без настоящего аутентификатора в песочнице ключей доступа и определяйте модели аутентификаторов по AAGUID через поиск по AAGUID.

Факты и проверки, на которых они основаны

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/simplewebauthn/run.sh запустила SimpleWebAuthn 14.0.3 в среде Node.js 24.22.0 в образе контейнера node:24.22.0-bookworm-slim; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

  • Алгоритмы COSE по умолчанию

    -48-8-7-257

    generateRegistrationOptions() перечисляет алгоритмы COSE -48 (ML-DSA-44), -8 (Ed25519), -7 (ES256) и -257 (RS256) именно в таком порядке.

    Что записала проверка
    fact webauthn-default-algorithms = -48, -8, -7, -257
  • Аттестация по умолчанию

    none

    Параметр передачи аттестации по умолчанию равен none.

    Что записала проверка
    fact webauthn-attestation = none
  • Проверка пользователя по умолчанию

    preferred

    authenticatorSelection.userVerification по умолчанию равен preferred.

    Что записала проверка
    fact webauthn-user-verification = preferred
  • Резидентный ключ по умолчанию

    preferred

    authenticatorSelection.residentKey по умолчанию равен preferred.

    Что записала проверка
    fact webauthn-resident-key = preferred
  • Обязательная проверка пользователя по умолчанию

    yes

    Обе функции проверки требуют флаг UV, если не передать requireUserVerification: false: регистрация и assertion, в которых установлен только флаг UP, отклонены с ошибками User verification was required, but user could not be verified и User verification required, but user could not be verified.

    Что записала проверка
    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
  • Кодировка подписи assertion

    der

    Корректный assertion ES256 с подписью в ASN.1 DER прошёл проверку.

    Что записала проверка
    fact webauthn-assertion-encoding = der
  • Подпись assertion в формате raw r‖s

    rejected

    Тот же assertion с 64-байтовой подписью raw r‖s вызвал ошибку разбора ASN.1, текст которой зависит от байтов подписи.

    Что записала проверка
    fact webauthn-raw-signature = rejected
  • Подпись assertion с high-S

    accepted

    Подпись в DER, в которой s заменили на n − s, прошла проверку.

    Что записала проверка
    fact webauthn-high-s = accepted
  • Уменьшение счётчика подписей

    rejected

    Счётчик меньше сохранённого значения вызвал ошибку Response counter value 3 was lower than expected 5; значение 0 при сохранённом 5 тоже отклонено, а 0 при сохранённом 0 прошло проверку.

    Что записала проверка
    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
  • Учётные данные Ed25519

    supported

    Учётные данные Ed25519 (COSE -8) успешно прошли регистрацию и аутентификацию.

    Что записала проверка
    fact webauthn-ed25519 = supported
    evidence webauthn-ed25519: verified

Использование

Сформируйте параметры регистрации и аутентификации, которые запрашивают ту же политику проверки пользователя, что функции проверки применяют по умолчанию.
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);

Тестовый стенд для фрагментов кода запускает каждый пример без изменений в образе контейнера node:24.22.0-bookworm-slim, и запуск завершается ошибкой, если вывод не совпадает.

Подготовка
npm init -y >/dev/null && npm install --no-audit --no-fund @simplewebauthn/server@14.0.3
Команда
node usage.mts
Вывод должен совпадать (регулярное выражение)
algorithms: -48, -8, -7, -257\nuv: required required\nchallenge bytes: 32

Библиотеки