Files
2026-05-27 23:29:53 +01:00

11 KiB

Tasks — @suaveplan/webauthn (WebAuthn Level 3 client + parser + verifier)

Every checkbox MUST reflect work actually done in this change. Do not retroactively tick boxes for work that was already in main when this change opened. Do not tick a box for a test that doesn't exist or doesn't pin the requirement it claims to verify.

0. Blocking prerequisite

  • 0.1 enterprise-cbor change archived and @suaveplan/cbor published from genesis (COSE / CTAP CBOR profile available). No implementation task in §2 may start until this lands. Spec / proposal / fixture-prep tasks (§1, §3) are unblocked.

1. Spec authoring

  • 1.1 Read ../suaveplan/enterprise/webauthn.md end-to-end
  • 1.2 Read W3C WebAuthn L3 §5, §6, §7, §8, §9, §10
  • 1.3 Read CTAP 2.1 §6.5, RFC 9052 §7, IANA WebAuthn registries
  • 1.4 Read openspec/AGENTS.md §13 and §14
  • 1.5 Draft proposal.md from openspec/templates/proposal.md
  • 1.6 Fill in ## Source mapping table — every requirement cites enterprise-doc lines + a W3C / CTAP / RFC anchor
  • 1.7 Draft specs/webauthn/spec.md from openspec/templates/spec-delta.md
  • 1.8 Every requirement in the spec delta carries a **Verified by:** line naming concrete it(...) titles

2. Implementation

2.1 Adapter abstraction

  • 2.1.1 src/adapter/adapter.ts — WebAuthnAdapter interface (create, get, isSupported, isConditionalMediationAvailable)
  • 2.1.2 src/adapter/default-adapter.ts — defaultWebAuthnAdapter() binding globalThis.navigator.credentials; throws WEBAUTHN.NOT_SUPPORTED when missing
  • 2.1.3 src/adapter/dom-exception-mapper.ts — NotAllowedError → WEBAUTHN.USER_CANCELLED, InvalidStateError → WEBAUTHN.INVALID_STATE, SecurityError → WEBAUTHN.SECURITY_ERROR, AbortError → WEBAUTHN.ABORTED, NotSupportedError → WEBAUTHN.NOT_SUPPORTED

2.2 Errors + observability

  • 2.2.1 src/errors/errors.ts — WebAuthnError registry via defineCodes("WEBAUTHN", [...]) covering every code in the spec delta
  • 2.2.2 src/observability/metrics.ts — webauthn.register_duration_ms, webauthn.authenticate_duration_ms, webauthn.outcome_total, webauthn.attestation_format_total, webauthn.errors_total

2.3 Parser (universal-tier ./parser subpath)

  • 2.3.1 src/parser/client-data.ts — parseClientDataJSON; type pinning + origin + base64url challenge decode + Zod ClientDataJSONSchema
  • 2.3.2 src/parser/authenticator-data.ts — parseAuthenticatorData; 37-byte fixed-prefix split + flag-byte decoder + attested-credential-data + extensions block
  • 2.3.3 src/parser/cose-key.ts — RFC 9052 §7 COSE_Key decoder consuming @suaveplan/cbor; supports ES256 / ES384 / ES512 / EdDSA / RS256 alg labels
  • 2.3.4 src/parser/attestation-object.ts — parseAttestationObject per CTAP 2.1 §6.5; extracts fmt / authData / attStmt
  • 2.3.5 Zod boundary schemas at every export — MALFORMED_RESPONSE on mismatch

2.4 Verifier (universal-tier ./verifier subpath)

  • 2.4.1 src/verifier/verify-assertion.ts — signature verify over authData || sha256(clientDataJSON) via @suaveplan/crypto-curve, dispatched on COSE alg
  • 2.4.2 src/verifier/sign-counter.ts — sign-counter regression detection per W3C L3 §6.1.1
  • 2.4.3 src/verifier/verify-attestation.ts — verifyAttestation dispatcher
  • 2.4.4 src/verifier/registry.ts — defaultAttestationRegistry() + registerAttestationFormat(name, verifier)

2.5 Per-format attestation verifiers

  • 2.5.1 src/attestation/packed/packed.ts — W3C L3 §8.2
  • 2.5.2 src/attestation/fido-u2f/fido-u2f.ts — W3C L3 §8.6
  • 2.5.3 src/attestation/tpm/tpm.ts — W3C L3 §8.3
  • 2.5.4 src/attestation/android-key/android-key.ts — W3C L3 §8.4
  • 2.5.5 src/attestation/android-safetynet/android-safetynet.ts — W3C L3 §8.5
  • 2.5.6 src/attestation/apple/apple.ts — W3C L3 §8.8
  • 2.5.7 src/attestation/none/none.ts — W3C L3 §8.7

2.6 Registration (browser-tier root)

  • 2.6.1 src/register/register.ts — register(opts) building PublicKeyCredentialCreationOptions, calling adapter, parsing AuthenticatorAttestationResponse
  • 2.6.2 Client-data validator wired (type / origin / challenge constant-time-equals)
  • 2.6.3 expectedRpIdHash constant-time compare
  • 2.6.4 AbortSignal forwarded to navigator.credentials.create
  • 2.6.5 OTel webauthn.register_duration_ms recorded with outcome label

2.7 Authentication (browser-tier root)

  • 2.7.1 src/authenticate/authenticate.ts — authenticate(opts) building PublicKeyCredentialRequestOptions, calling adapter, parsing AuthenticatorAssertionResponse
  • 2.7.2 Client-data validator pinned to webauthn.get
  • 2.7.3 Conditional mediation passthrough; isConditionalMediationAvailable() helper
  • 2.7.4 AbortSignal forwarded to navigator.credentials.get
  • 2.7.5 OTel webauthn.authenticate_duration_ms recorded with outcome label

2.8 Extensions

  • 2.8.1 src/extensions/prf/prf.ts — PRF eval input on register + authenticate; result parsing; enabled === false fallback
  • 2.8.2 src/extensions/large-blob/large-blob.ts — support on register; read / write on authenticate; result shape

2.9 Root barrel + subpath exports

  • 2.9.1 src/index.ts — explicit named re-exports (no export *)
  • 2.9.2 package.json exports map covers every subpath listed in the proposal
  • 2.9.3 package.json browser field set; engines does not pin Node-only

3. Test-vector bundle

  • 3.1 src/__fixtures__/yubikey-packed/ — Yubikey 5 packed attestation good vector + corrupted variants
  • 3.2 src/__fixtures__/yubikey-fido-u2f/ — Yubikey 5 fido-u2f good vector
  • 3.3 src/__fixtures__/windows-hello-tpm/ — Windows Hello TPM attestation good vector
  • 3.4 src/__fixtures__/macos-touch-id-apple/ — Apple anonymous attestation good vector
  • 3.5 src/__fixtures__/chrome-software-none/ — Chrome software authenticator none format
  • 3.6 src/__fixtures__/android-key/ and android-safetynet/ — Pixel platform-authenticator vectors
  • 3.7 Each fixture set has at least one corrupted variant (signature flipped, rpIdHash zeroed, signCount frozen)
  • 3.8 Fixtures generated offline; CI does not depend on a real authenticator

4. Tests (runner-neutral imports per CLAUDE.md §11 — @suaveplan/testing/runner)

  • 4.1 src/register/register.test.ts — happy path, client-data mismatches, AbortSignal, DOMException mapping
  • 4.2 src/authenticate/authenticate.test.ts — happy path, conditional mediation, AbortSignal
  • 4.3 src/parser/authenticator-data.test.ts — every flag combination, attested-credential-data, extensions block
  • 4.4 src/parser/attestation-object.test.ts — fmt / authData / attStmt extraction
  • 4.5 src/parser/client-data.test.ts — type pinning, base64url decode
  • 4.6 src/parser/cose-key.test.ts — RFC 9052 §7 round-trip per alg
  • 4.7 src/verifier/verify-assertion.test.ts — every alg, signature-flip negative, sign-counter regression boundaries
  • 4.8 src/verifier/verify-attestation.test.ts — registry dispatch, unknown-format error
  • 4.9 Per-format src/attestation/<fmt>/<fmt>.test.ts — good + corrupted vectors for packed, fido-u2f, tpm, android-key, android-safetynet, apple, none
  • 4.10 src/extensions/prf/prf.test.ts — register + authenticate eval round-trip + length assertion + missing-PRF fallback
  • 4.11 src/extensions/large-blob/large-blob.test.ts — support flag, read, write
  • 4.12 src/adapter/adapter.test.ts — NOT_SUPPORTED when missing, conditional-mediation check
  • 4.13 src/errors/errors.test.ts — every code thrown by ≥1 production path; DOMException mapping
  • 4.14 src/observability/metrics.test.ts — every histogram + counter recorded against in-memory MeterProvider
  • 4.15 src/parser/authenticator-data.property.test.ts — fast-check fuzz, parser never panics
  • 4.16 src/parser/attestation-object.property.test.ts — fast-check fuzz of envelope decoder
  • 4.17 src/index.test.ts — every documented subpath resolves
  • 4.18 tests/dep-graph.test.ts — ./parser and ./verifier import zero DOM types
  • 4.19 Every it(...) carries { timeout: 5000 } (or 30000 for genuinely async)

5. Documentation

  • 5.1 Sibling .md for every implementation .ts (≥200 words, basic + advanced examples, full API reference)
  • 5.2 README.md with Modules table linking every co-located .md
  • 5.3 Update CLAUDE.md package listing under the Vault section
  • 5.4 README cites W3C WebAuthn L3 / CTAP 2.1 / RFC 9052 anchors

6. Benchmarks (mitata)

  • 6.1 src/__benchmarks__/parse_authdata.bench.ts
  • 6.2 src/__benchmarks__/verify_assertion_es256.bench.ts
  • 6.3 src/__benchmarks__/verify_attestation_packed.bench.ts
  • 6.4 src/__benchmarks__/verify_attestation_tpm.bench.ts
  • 6.5 Baseline numbers committed in README

7. Pre-archive verification (the 5-gate triple)

  • 7.1 bun run check — Biome 0 errors / 0 warnings / 0 info across the whole monorepo
  • 7.2 bunx turbo lint typecheck test --filter=@suaveplan/webauthn — green
  • 7.3 bunx turbo test:coverage --filter=@suaveplan/webauthn — 100/100/100/100
  • 7.4 bun scripts/openspec-conformance.ts @suaveplan/webauthn — exit 0, no error findings
  • 7.5 bunx turbo build --filter=@suaveplan/webauthn — green

8. Pre-archive 7-row checklist (AGENTS.md §13)

  • 8.1 Generics threaded; expectTypeOf assertions on every literal-type claim (AttestationType, WebAuthnErrorCode, CoseAlg)
  • 8.2 Zod schemas wired at every declared boundary (register / authenticate options, parsed structures, COSE_Key)
  • 8.3 Every error code in errors.ts thrown by at least one production path
  • 8.4 Strict-mode behaviors tested both on and off (UV required vs preferred; attestation direct vs none)
  • 8.5 OTel instruments emitted from production paths and asserted with a real in-memory MeterProvider
  • 8.6 Lifecycle dedup tested where claimed (AbortSignal mid-flight, conditional-mediation availability cache)
  • 8.7 No retroactive tick — every box above reflects work done in this change

9. Banned-pattern + barrel + runner-import scans

  • 9.1 grep -rn "@deprecated\|TODO\|FIXME\|ts-ignore\|ts-expect-error\|\.skip\|\.only" packages/vault/webauthn/src/ --include="*.ts" — zero output
  • 9.2 grep -rn "^\s*export\s\+\(type\s\+\)\?\*\s\+from" packages/vault/webauthn/src/ --include="index.ts" — zero output (root src/index.ts permitted; feature barrels must use named exports)
  • 9.3 grep -rnE "from ['\"](vitest|bun:test)['\"]" packages/vault/webauthn/src/ --include="*.ts" --include="*.tsx" — zero output (package is not on the testing-infra exempt allowlist)

10. Archive

  • 10.1 Fold spec delta into openspec/specs/webauthn/spec.md
  • 10.2 Move openspec/changes/enterprise-webauthn/ → openspec/archive/<yyyy-mm-dd>-enterprise-webauthn/
  • 10.3 Refresh MANIFEST.md via bun scripts/folder-reorg/generate-manifest.ts
  • 10.4 Refresh MISSING.yml via bun scripts/generate-missing.ts
  • 10.5 Move @suaveplan/webauthn row in STATUS.md from Pending → Completed
  • 10.6 Update SUMMARY.md with per-package paragraph
  • 10.7 bunx markdownlint-cli2 --fix and stage results