feat(server): web 用户中心后端(3/3) — 用户 TOTP 2FA + 登录二段式
- 用户 TOTP(auth/totp_user.go,复用 internal/totp + AES-256-GCM 加密存密钥):
POST /v1/me/totp/setup(生成密钥+otpauth_uri)、/verify(校验码→启用)、
/disable(校验码→清空)。仅在 USER_TOTP_ENC_KEY(32B/64hex) 配置时挂载。
- 登录二段式:Login 在 totp_enabled 时不发 token,改发短期 pending token(Redis
5min)+ 返回 {totp_required, pending_token};POST /v1/auth/login/totp 消费
pending + 校验码 → 发 token。非 TOTP 用户仍走扁平 TokenPair,app 不受影响。
- User 结构 + GetUserByEmail 补 totp_enabled。
- 单测覆盖 AES seal/open 往返 + 篡改/错误密钥检测 + pending token 唯一性。
- 全量 server 23 包测试通过。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -13,12 +13,13 @@ import (
|
||||
|
||||
// User is the subset of the users row the auth module needs.
|
||||
type User struct {
|
||||
ID int64
|
||||
UUID string
|
||||
Email string
|
||||
PwHash string
|
||||
DpUUID string
|
||||
Status string // "active" | "banned"
|
||||
ID int64
|
||||
UUID string
|
||||
Email string
|
||||
PwHash string
|
||||
DpUUID string
|
||||
Status string // "active" | "banned"
|
||||
TOTPEnabled bool // two-factor enabled → login requires a second TOTP step
|
||||
}
|
||||
|
||||
// Sentinel store errors. Service maps these to API errors.
|
||||
@@ -112,8 +113,8 @@ func (s *SQLStore) CreateUserWithTrial(ctx context.Context, email, pwHash string
|
||||
func (s *SQLStore) GetUserByEmail(ctx context.Context, email string) (*User, error) {
|
||||
var u User
|
||||
err := s.db.QueryRowContext(ctx,
|
||||
`SELECT id, uuid, email, pw_hash, dp_uuid, status FROM users WHERE email = ?`,
|
||||
email).Scan(&u.ID, &u.UUID, &u.Email, &u.PwHash, &u.DpUUID, &u.Status)
|
||||
`SELECT id, uuid, email, pw_hash, dp_uuid, status, totp_enabled FROM users WHERE email = ?`,
|
||||
email).Scan(&u.ID, &u.UUID, &u.Email, &u.PwHash, &u.DpUUID, &u.Status, &u.TOTPEnabled)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user