feat(auth): TOTP setup endpoint, recovery codes, passkeys and master key rotation

- GET /auth/totp/setup returns the pending secret and otpauth URL (created
  on first call, reused on refresh) for the console to render as a QR code.
- Enabling TOTP issues 8 one-time recovery codes (argon2id hashes in
  admin_recovery_code); login accepts recoveryCode in place of totpCode,
  disable accepts either, and recovery_codes regenerates with the password.
- Passkeys via @simplewebauthn/server: register/list/rename/delete under
  the console session, public login options/verify that reuse the login
  rate limit and session cookies. Challenges are one-time rows in
  webauthn_challenge; RP ID and origins come from WEBAUTHN_RP_ID /
  WEBAUTHN_ORIGINS, falling back to the request host. Signature counter
  regressions are rejected. SessionInfo gains passkeyEnabled.
- --role rotate-master-key re-encrypts the secret table from
  SECRET_MASTER_KEY to SECRET_MASTER_KEY_NEW one row per transaction,
  skips rows already on the new key (resumable, swap keys to roll back),
  logs progress and audits secret.master_key_rotate. Secrets now record
  the master key fingerprint in kdf_params.kid.
- Runbook: recovery codes, passkey configuration, rotation steps and
  rollback.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-28 20:22:07 +08:00
1 parent 5c6f483420
commit b0e5d539ed
21 files changed
+1616 -80

No files matched your search

+2
View File
@@ -16,6 +16,7 @@
"@fontsource-variable/inter-tight": "^5.2.7",
"@fontsource-variable/space-grotesk": "^5.2.10",
"@react-router/node": "7.12.0",
"@simplewebauthn/browser": "^13.3.0",
"@tanstack/react-query": "^5.90.21",
"@umt/contracts": "workspace:*",
"class-variance-authority": "^0.7.1",
@@ -24,6 +25,7 @@
"isbot": "^5.1.31",
"lucide-react": "^0.576.0",
"next-themes": "^0.4.6",
"qr-code-styling": "^1.9.2",
"radix-ui": "^1.4.3",
"react": "^19.2.4",
"react-day-picker": "^10.0.1",