11 KiB
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-cborchange archived and@suaveplan/cborpublished 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.mdend-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.mdfromopenspec/templates/proposal.md - 1.6 Fill in
## Source mappingtable — every requirement cites enterprise-doc lines + a W3C / CTAP / RFC anchor - 1.7 Draft
specs/webauthn/spec.mdfromopenspec/templates/spec-delta.md - 1.8 Every requirement in the spec delta carries a
**Verified by:**line naming concreteit(...)titles
2. Implementation
2.1 Adapter abstraction
- 2.1.1
src/adapter/adapter.ts—WebAuthnAdapterinterface (create,get,isSupported,isConditionalMediationAvailable) - 2.1.2
src/adapter/default-adapter.ts—defaultWebAuthnAdapter()bindingglobalThis.navigator.credentials; throwsWEBAUTHN.NOT_SUPPORTEDwhen 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—WebAuthnErrorregistry viadefineCodes("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 + ZodClientDataJSONSchema - 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—parseAttestationObjectper CTAP 2.1 §6.5; extractsfmt/authData/attStmt - 2.3.5 Zod boundary schemas at every export —
MALFORMED_RESPONSEon mismatch
2.4 Verifier (universal-tier ./verifier subpath)
- 2.4.1
src/verifier/verify-assertion.ts— signature verify overauthData || 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—verifyAttestationdispatcher - 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)buildingPublicKeyCredentialCreationOptions, calling adapter, parsingAuthenticatorAttestationResponse - 2.6.2 Client-data validator wired (type / origin / challenge constant-time-equals)
- 2.6.3
expectedRpIdHashconstant-time compare - 2.6.4 AbortSignal forwarded to
navigator.credentials.create - 2.6.5 OTel
webauthn.register_duration_msrecorded withoutcomelabel
2.7 Authentication (browser-tier root)
- 2.7.1
src/authenticate/authenticate.ts—authenticate(opts)buildingPublicKeyCredentialRequestOptions, calling adapter, parsingAuthenticatorAssertionResponse - 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_msrecorded withoutcomelabel
2.8 Extensions
- 2.8.1
src/extensions/prf/prf.ts— PRF eval input on register + authenticate; result parsing;enabled === falsefallback - 2.8.2
src/extensions/large-blob/large-blob.ts—supporton register;read/writeon authenticate; result shape
2.9 Root barrel + subpath exports
- 2.9.1
src/index.ts— explicit named re-exports (noexport *) - 2.9.2
package.jsonexportsmap covers every subpath listed in the proposal - 2.9.3
package.jsonbrowserfield set;enginesdoes 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 authenticatornoneformat - 3.6
src/__fixtures__/android-key/andandroid-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/attStmtextraction - 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 forpacked,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_SUPPORTEDwhen 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-memoryMeterProvider - 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—./parserand./verifierimport zero DOM types - 4.19 Every
it(...)carries{ timeout: 5000 }(or 30000 for genuinely async)
5. Documentation
- 5.1 Sibling
.mdfor 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.mdpackage 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, noerrorfindings - 7.5
bunx turbo build --filter=@suaveplan/webauthn— green
8. Pre-archive 7-row checklist (AGENTS.md §13)
- 8.1 Generics threaded;
expectTypeOfassertions 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.tsthrown by at least one production path - 8.4 Strict-mode behaviors tested both on and off (UV required vs preferred; attestation
directvsnone) - 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 (rootsrc/index.tspermitted; 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.mdviabun scripts/folder-reorg/generate-manifest.ts - 10.4 Refresh
MISSING.ymlviabun scripts/generate-missing.ts - 10.5 Move
@suaveplan/webauthnrow inSTATUS.mdfromPending→Completed - 10.6 Update
SUMMARY.mdwith per-package paragraph - 10.7
bunx markdownlint-cli2 --fixand stage results