Files
Felis/docs/changes/2026-07-01-passkey-enrollment.md
T
flyemoji 096d59716e docs(changes): backfill detail docs for pre-ledger functional commits
Retroactively author 15 grouped detail docs covering the backend
functional (feat/fix) commits made before the change ledger was
established (fad48ff), closing the ledger's detail-doc axis for the
pre-convention history. Each doc groups a feature's constituent commits,
lists their SHAs with subjects, and carries a backfill note stating it
was reconstructed from git history on 2026-07-07 and not independently
re-verified (current tree green at 9911b8c).

Add a Detail docs section to INDEX.md linking every detail doc (the 6
existing + 15 backfill) to the commit(s) it covers, so a doc is findable
from the index without a column on the auto-generated ledger table. Catch
the table up with the missing 9911b8c row.

Scope: backend (Go/Java/K8s) only, per the ledger's stated convention
that frontend/panel commits are the collaborator's UI work; non-functional
commits (docs/style/chore/refactor) keep their table row without a
dedicated detail doc.
2026-07-07 20:55:51 +09:00

38 lines
2.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Passkey (WebAuthn) enrollment subsystem + hardening (ledger backfill)
- **Type:** feature + fix — retroactive ledger entry
- **Date:** 2026-07-01 – 2026-07-02
- **Area:** `internal/passkey` (go-webauthn adapter), `internal/api` (enrollment handlers/audit), `internal/store` (migrations 0007–0009)
- **Commits:**
- `f2c916d` feat(api): passkey enrollment persistence layer
- `742f15f` feat(api): passkey enrollment endpoints
- `0261204` feat(passkey): go-webauthn enrollment verifier adapter (Oracle-verified against a virtual authenticator)
- `fce0fce` feat(passkey): wire the enrollment verifier into felis-api
- `7278cd7` feat(passkey): require + record user verification at enrollment (`UserVerification=required`; capture `user_verified`/`backup_eligible`/`backup_state` — migration 0009) — *fix (d)*
- `cdbb5ab` fix(api): record credential id in the passkey-register audit event so bind/unbind are symmetric — *fix (a)*
- `9953275` fix(api): bound `webauthn_challenges` growth by superseding *all* prior rows per (user, purpose) — *fix (b)*
- `20e31fb` fix(store): cascade-delete passkeys + challenges on user removal (recreate both FKs `ON DELETE CASCADE`, scoped to the passkey tables only) — *fix (c)*
- `54bc6ef` fix(api): clear bound passkeys on password change to close a takeover foothold — *fix (e)*
- **Tasks:** #36 (passkey bind with email-OTP fallback), #48–#52 (fixes a–e)
## What it did
Built the WebAuthn *enrollment* half — persistence, the go-webauthn crypto adapter, and
the register-begin/finish endpoints — then hardened it through the five-fix batch (a–e):
symmetric audit, a bounded challenge table, cascade cleanup, enforced+recorded user
verification, and unbinding every passkey on a password reset so a passkey planted through
a transiently-hijacked session cannot survive as a standing login foothold.
## Why
Passkeys are the phishing-resistant factor with email-OTP as the fallback. The hardening
batch closes the seams that make enrollment safe to *rely on*: without UV enforcement a
passkey proves possession but not user; without the password-reset clear, a planted
passkey outlives the very remediation meant to evict an attacker.
> **Backfill note.** Reconstructed 2026-07-07 from the commit history. The adapter crypto
> was verified against a virtual authenticator (virtualwebauthn), and each fix shipped
> with a targeted test (UV-negative rejection, challenge-growth bound, cascade, symmetric
> audit) at its commit. Not independently re-verified for this doc; current tree green at
> `9911b8c`. The assertion/login half is a separate doc ([passkey-login](2026-07-01-passkey-login.md)).