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 at9911b8c). 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 missing9911b8crow. 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.
2.5 KiB
Passkey (WebAuthn) login: assertion, discoverable, clone-detection (ledger backfill)
- Type: feature — retroactive ledger entry
- Date: 2026-07-01 – 2026-07-05
- Area:
internal/passkey(assertion crypto),internal/api(login/assertion, unbind, UA-guard),internal/store(migrations 0013/0014) - Commits:
e035142feat(passkey): WebAuthn login/assertion crypto adapter (BeginLogin/FinishLogin over go-webauthn, Oracle-verified against a virtual authenticator; surfaces the signature counter as a ceremony fact)ec468bafeat(auth): discoverable (usernameless) passkey login — the from-zero door the username-first assertion couldn't key on0dbd557fix(store): renumber the discoverable-login migration 0013 → 00149e1df12feat(passkey): advancesign_count, reject clone-warned assertions4f59d51feat(auth): owner-tier passkey-unbind remediation endpointa63f49dfeat(panel): steer WeChat/QQ in-app browsers to the system browser for passkey — a backend-only UA interstitial (the SPA is untouched); asset/API/health requests pass through, anua_ackcookie lets a determined user continue
- Tasks: #40 (from-zero discoverable login), #67 (WeChat/QQ UA-guard in
internal/panel)
What it did
Built the assertion (login) half of the ceremony: the crypto adapter, then discoverable
credentials so a user with no typed identifier can still log in (the enrollment
identifier problem the earlier deferral doc named), clone detection via the advancing
signature counter, and the owner-tier unbind remediation. a63f49d guards the flow at the
transport edge — WebAuthn is unusable inside the WeChat/QQ WebViews, so those UAs get a
bilingual "open in your system browser" page instead of the passkey SPA.
Why
Enrollment without a login path is half a feature. Discoverable credentials resolve the
blocker recorded in the earlier deferral (users.email is nullable/non-unique and a
player's username is their Minecraft UUID, so username-first assertion had nothing to key
on). The UA-guard stops the most common real-world dead end: a passkey prompt that can
never succeed inside an in-app browser.
Backfill note. Reconstructed 2026-07-07 from the commit history. The assertion crypto was verified against a virtual authenticator (enrollment→assertion chain, origin-mismatch and unbound-credential rejection);
9e1df12's clone policy and the UA-guard pass-through were unit-tested at their commits. Not independently re-verified for this doc; current tree green at9911b8c.