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

+7 -1
View File
@@ -110,7 +110,13 @@ export type AuthEnv = {
};
/** 免鉴权路径(相对 /api/v1):登录前就要能访问;带有效 cookie 时仍会挂上 actor */
export const PUBLIC_AUTH_PATHS = ["/auth/setup", "/auth/login", "/auth/session"] as const;
export const PUBLIC_AUTH_PATHS = [
"/auth/setup",
"/auth/login",
"/auth/session",
"/auth/passkeys/login/options",
"/auth/passkeys/login/verify",
] as const;
const WRITE_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"]);
/** last_seen_at 节流写库间隔 */
const TOUCH_INTERVAL_MS = 60_000;