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:
1 parent
5c6f483420
commit
b0e5d539ed
21 files changed
+1616
-80
No files matched your search
@@ -10,6 +10,17 @@ export const ConfigSchema = z.object({
|
||||
HOST: z.string().min(1).default("0.0.0.0"),
|
||||
DATABASE_URL: z.string().regex(/^postgres(ql)?:\/\//, "必须是 postgres:// 连接串"),
|
||||
SECRET_MASTER_KEY: z.string().min(16, "SECRET_MASTER_KEY 至少 16 个字符"),
|
||||
/** 主密钥轮换(--role rotate-master-key)的新密钥;只在轮换命令里读取,完成后换成 SECRET_MASTER_KEY 并删掉 */
|
||||
SECRET_MASTER_KEY_NEW: z.string().min(16, "SECRET_MASTER_KEY_NEW 至少 16 个字符").optional(),
|
||||
/**
|
||||
* Passkey 的 RP ID(浏览器地址栏里的主机名,例如 media.example.com;不含协议与端口)。
|
||||
* 不设时取请求的 Host(反代后面取 X-Forwarded-Host);换域名后已登记的 Passkey 全部失效。
|
||||
*/
|
||||
WEBAUTHN_RP_ID: z.string().min(1).optional(),
|
||||
/** Passkey 允许的来源,逗号分隔(例如 https://media.example.com);不设时接受主机名等于 / 属于 RP ID 的来源 */
|
||||
WEBAUTHN_ORIGINS: z.string().min(1).optional(),
|
||||
/** Passkey 注册时显示的站点名 */
|
||||
WEBAUTHN_RP_NAME: z.string().min(1).default("UHD Media Tool"),
|
||||
LOG_LEVEL: LogLevel.default("info"),
|
||||
/** 仅开发模式生效的管理员 Key */
|
||||
DEV_ADMIN_KEY: z.string().min(8).optional(),
|
||||
|
||||
Reference in new issue
Block a user