Files
suaveplan/openspec/changes/complete-webauthn-mds3/tasks.md
2026-05-27 23:29:53 +01:00

8.7 KiB

Tasks — complete-webauthn-mds3

0. Pre-flight (non-negotiable)

  • 0.1 Confirm clean working tree (git status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in each)
  • 0.2 If any worktrees exist under .claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
  • 0.3 Confirm the package set in MANIFEST.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Package scaffolding

  • 1.1 Create packages/auth/webauthn-mds3/ per genesis package layout.
  • 1.2 package.json at version: 0.0.0, sideEffects: false, workspace:^ for internal deps, catalog:* for external.
  • 1.3 tsconfig.json extending @suaveplan/typescript-config universal preset.
  • 1.4 vitest.config.ts via createTestConfig() from @suaveplan/testing/config; coverage 100/100/100/100; provider: 'istanbul'.
  • 1.5 Subpath exports: ., ./fetch, ./verify, ./cache, ./bootstrap, ./refresher, ./resolver, ./status-filter, ./events, ./errors.
  • 1.6 Root README.md with one-paragraph description, install, quick-start, Modules section.

2. Constants + FIDO root pinning

  • 2.1 src/constants/fido-root.ts + .md — pin FIDO Alliance MDS3 root CA DER bytes + SHA-256 fingerprint; export FIDO_MDS3_DEFAULT_URL, DEFAULT_REFRESH_INTERVAL_MS, DEFAULT_DENY_STATUSES, JITTER_WINDOW_MS.

3. Error codes (≥12; current: 14)

  • 3.1 src/errors/error-codes.ts + .md — defineCodes("WEBAUTHN_MDS3", [BLOB_FETCH_FAILED, BLOB_NOT_MODIFIED, BLOB_MALFORMED, BLOB_SIGNATURE_INVALID, BLOB_CHAIN_UNTRUSTED, BLOB_EXPIRED, BLOB_ALG_DISALLOWED, BLOB_SCHEMA_MISMATCH, CACHE_READ_FAILED, CACHE_WRITE_FAILED, CACHE_ROLLBACK_REFUSED, RESOLVER_LOOKUP_FAILED, REFRESHER_ALREADY_RUNNING, REFRESHER_ABORTED]).
  • 3.2 src/errors/errors.test.ts — assert every code is reachable from a production path.

4. Schemas

  • 4.1 src/schemas/metadata-blob.schemas.ts + .md — MetadataBlobPayloadSchema, MetadataStatementSchema, AuthenticatorStatusSchema, StatusReportSchema, BiometricStatusReportSchema, EcdaaTrustAnchorSchema, MetadataBlobEntrySchema tracking FIDO MDS3 §3.1.

5. Fetch (conditional)

  • 5.1 src/fetch/fetch-blob.ts + .md — fetchMetadataBlob({ fetch, url?, signal?, cache? }) returning FetchOutcome; sends If-None-Match / If-Modified-Since when validators present; maps 304 → { kind: "not-modified" }; non-2xx (excluding 304) → BLOB_FETCH_FAILED.
  • 5.2 src/fetch/fetch-blob.test.ts — happy, 304, non-2xx, abort, conditional headers asserted.

6. Verify

  • 6.1 src/verify/verify-blob.ts + .md — JWS via @suaveplan/jws; alg allow-list {RS256, ES256, ES384}; chain to pinned FIDO root via @suaveplan/x509; decode + Zod-validate payload; reject expired.
  • 6.2 src/verify/verify-blob.test.ts — happy, signature-flipped, chain-broken, expired, alg-disallowed, schema-mismatch.

7. Cache (rollback-protected)

  • 7.1 src/cache/metadata-cache.ts + .md — MetadataCache over StorageLike<CachedMetadataEnvelope>; namespace webauthn-mds3:blob:v1; rejects writes whose payload.no < cached.payload.no with CACHE_ROLLBACK_REFUSED.
  • 7.2 src/cache/metadata-cache.test.ts — round-trip, rollback refusal, read/write failure mapping.

8. Bootstrap blob

  • 8.1 src/bootstrap/blob.ts + .md — export BOOTSTRAP_BLOB: string (base64 JWS) plus a verified BOOTSTRAP_PAYLOAD: MetadataBlobPayload derived at module load.
  • 8.2 src/bootstrap/blob.test.ts — verifyMetadataBlob(BOOTSTRAP_BLOB) resolves; bootstrap is overridden by first successful refresh.

9. Refresher

  • 9.1 src/refresher/metadata-refresher.ts + .md — createMetadataRefresher({ fetch, storage, clock, logger, eventBus?, intervalMs?, signal? }); reentrancy guard; jittered schedule at nextUpdate - jitter; conditional fetch; EventBus + OTel.
  • 9.2 src/refresher/metadata-refresher.test.ts — schedule, fallback intervalMs, reentrancy, abort, 304-skips-verify, verify-fail keeps prior envelope, EventBus + OTel emitted.

10. Resolver

  • 10.1 src/resolver/types.ts + .md — MetadataResolver, MetadataLookup.
  • 10.2 src/resolver/memory.ts + .md + .test.ts — createMemoryResolver({ payload }); O(1) primary map by AAGUID; secondary O(1) map by SKI hex; constant-time AAGUID equality.
  • 10.3 src/resolver/cached.ts + .md + .test.ts — createCachedResolver({ cache, refresher?, bootstrap? }); lazy refresh on first lookup; falls back to bootstrap when cache empty AND fetch fails.
  • 10.4 src/resolver/manual.ts + .md + .test.ts — createManualResolver({ jws }); runs verifyMetadataBlob then delegates to memory resolver.

11. Status filter

  • 11.1 src/status-filter/status-filter.ts + .md — createStatusFilter({ allow?, deny? }); deny wins; defaults DEFAULT_DENY_STATUSES.
  • 11.2 src/status-filter/status-filter.test.ts — allow-only, deny-only, both, default deny set, deny-wins precedence.

12. Events

  • 12.1 src/events/topics.ts + .md — typed topics webauthn-mds3.refresh.success, webauthn-mds3.refresh.failure, webauthn-mds3.lookup.hit, webauthn-mds3.lookup.miss via @suaveplan/events.
  • 12.2 src/events/topics.test.ts — every topic fires from its production call site.

13. Observability

  • 13.1 src/observability/metrics.ts + .md — counters refresh_total{outcome}, lookup_total{outcome}; histogram blob_age_ms. No-op when no exporter.
  • 13.2 src/observability/spans.ts + .md — spans fetch, verify, refresh, lookup with attribute set + status mapping.
  • 13.3 src/observability/{metrics,spans}.test.ts — every metric and span records on its production path.

14. Fixtures

  • 14.1 src/__fixtures__/mds3-sample-blob.jwt — captured FIDO Alliance conformance sample.
  • 14.2 src/__fixtures__/mds3-corrupted-blob.jwt — signature-flipped.
  • 14.3 src/__fixtures__/mds3-rolled-back-blob.jwt — same payload with no decremented.
  • 14.4 src/__fixtures__/index.ts — typed exports.

15. Benchmarks

  • 15.1 src/__benchmarks__/verify-blob.bench.ts — JWS verify throughput.
  • 15.2 src/__benchmarks__/resolve-aaguid.bench.ts — AAGUID lookup throughput.

16. Tier-purity + subpath harnesses

  • 16.1 src/dep-graph.test.ts — zero DOM / Node-only imports across the public surface.
  • 16.2 src/index.test.ts — every documented subpath resolves.

17. Integration into @suaveplan/webauthn

  • 17.1 Extend VerifyAttestationInput with optional metadata?: MetadataResolver and statusFilter?: StatusFilter (typed via import type — no runtime dep).
  • 17.2 Extend AttestationVerifyResult with optional metadata?: MetadataLookup.
  • 17.3 Wire post-verifier resolution: AAGUID for non-U2F; SKI for fido-u2f / all-zero AAGUID; downgrade trusted=false when filter rejects status.
  • 17.4 Update packages/auth/webauthn/src/verifier/verify-attestation.test.ts — add six scenarios per the modified delta.
  • 17.5 Refresh verify-attestation.md and the packed.md / fido-u2f.md / none.md anchors to point at MetadataResolver as the canonical seam.
  • 17.6 Add @suaveplan/webauthn-mds3 to packages/auth/webauthn/README.md Modules section.

18. Pre-publish gate

  • 18.1 bunx turbo lint typecheck test --filter=@suaveplan/webauthn-mds3 green.
  • 18.2 bunx turbo test:coverage --filter=@suaveplan/webauthn-mds3 100/100/100/100.
  • 18.3 bunx turbo lint typecheck test --filter=@suaveplan/webauthn green (dependent sweep).
  • 18.4 Banned-pattern scan: zero matches (§13 step 4).
  • 18.5 Barrel-shape scan: zero matches (§13 step 4a).
  • 18.6 Test-runner import scan: zero matches (§13 step 4b).
  • 18.7 Missing-.md scan: zero misses.
  • 18.8 bunx markdownlint-cli2 --fix then no-fix pass exits 0.

19. Repo-root manifest refresh (§13b)

  • 19.1 bun scripts/folder-reorg/generate-manifest.ts → MANIFEST.md.
  • 19.2 bun scripts/generate-missing.ts → MISSING.yml.
  • 19.3 STATUS.md add @suaveplan/webauthn-mds3 row.
  • 19.4 SUMMARY.md paragraph added.

20. Fold + archive

  • 20.1 Fold specs/webauthn-mds3/spec.md into openspec/specs/webauthn-mds3/spec.md.
  • 20.2 Fold specs/webauthn/spec.md ## MODIFIED Requirements into openspec/specs/webauthn/spec.md.
  • 20.3 Move openspec/changes/complete-webauthn-mds3/ → openspec/archive/<yyyy-mm-dd>-complete-webauthn-mds3/.