Files
Felis/docs/changes/2026-07-01-passkey-login.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

37 lines
2.5 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) 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:**
- `e035142` feat(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)
- `ec468ba` feat(auth): discoverable (usernameless) passkey login — the from-zero door the username-first assertion couldn't key on
- `0dbd557` fix(store): renumber the discoverable-login migration 0013 → 0014
- `9e1df12` feat(passkey): advance `sign_count`, reject clone-warned assertions
- `4f59d51` feat(auth): owner-tier passkey-unbind remediation endpoint
- `a63f49d` feat(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, an `ua_ack` cookie 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 at `9911b8c`.