You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
9.7 KiB
9.7 KiB
登录、注册与用户信息 — 设计规格
状态:已定稿(2026-04-12)
范围:第一版邮箱密码 + Redis Session;未验证邮箱分级门禁;忘记密码闭环(无生产邮件);最小 Nuxt 页面;为 OAuth 预留数据与流程边界。
1. 目标与非目标
1.1 目标(第一版交付)
- 注册:邮箱 + 密码;持久化密码哈希;可创建邮箱验证类 challenge(与是否发信解耦)。
- 登录 / 登出:校验密码后创建 Redis Session,通过 HttpOnly Cookie 下发 opaque session id;登出删除会话并清 Cookie;登录成功 轮换 session id。
- 当前用户:从 Cookie → Redis →
user_id解析会话上下文。 - 用户信息:
GET/PATCH个人资料;第一版不允许修改email;可更新字段如name、age(与现有users_table对齐)。 - 未验证账号:不阻止登录;对指定操作通过 集中配置 要求
email_verified_at;新增能力时优先改配置而非复制校验逻辑。 - 忘记密码:
forgot-password与reset-passwordAPI 闭环;无生产邮件时 token 通过开发日志或运维/DB 获取;对外响应防枚举。 - OAuth:不在第一版实现;数据与流程边界见第 8 节。
1.2 非目标(第一版不做)
- 第三方 OAuth 回调与 UI。
- 2FA、设备管理、复杂风控。
- 生产级「发验证邮件 / 发重置邮件」投递(通过
Mailer抽象预留,第一版NoopMailer)。 - 完整运营后台;管理员手工重置仅作为运维流程文档化。
2. 数据模型与迁移
2.1 users_table 扩展
在现有表上 增量迁移(保持表名 users_table 与 Drizzle 映射,减少改名风险):
| 列 | 说明 |
|---|---|
password_hash |
非空(已有行需迁移策略:开发环境可清空后重建;生产需单独评估) |
email_verified_at |
timestamptz,可空 |
created_at / updated_at |
建议新增,便于审计 |
保留:id、name、age、email(唯一)。
邮箱变更:第一版 禁止 PATCH 修改 email。
2.2 auth_challenges
单表承载多种「挑战」:
| 列 | 说明 |
|---|---|
id |
主键 |
user_id |
外键 → users_table |
type |
枚举:email_verify | password_reset |
token_hash |
仅存储哈希,不存明文 token |
expires_at |
过期时间 |
consumed_at |
可空;消费后置位 |
| 可选 | created_at、created_ip |
索引:按 token_hash 查询(实现时注意与过期清理策略配合)。
2.3 Session
- 仅存 Redis,不建 Postgres session 表。
- Key 建议:
sess:{sessionId};Value JSON 至少含userId,可选createdAt等。 - TTL 与 Cookie
Max-Age一致;续期策略在实现计划中写死默认(如固定过期或滑动窗口)。
2.4 linked_accounts(OAuth 预留)
第二版可落库,第一版可在迁移中 创建空表 或 仅文档约定(二选一在实现计划中固定):
user_id、provider(如github)、provider_user_id- 唯一约束:
(provider, provider_user_id)
OAuth 绑定不写入 users 宽表。
2.5 迁移注意
- 使用 Drizzle 生成并版本化迁移。
- 示例接口
server/api/hello若返回用户列表,须在实现阶段改为 鉴权后可用 或 删除,禁止公开泄露。
3. Cookie、Redis 与安全
3.1 Cookie
- HttpOnly:必选。
- Secure:
NODE_ENV=production且 HTTPS 时必选;本地 HTTP 可关闭。 - SameSite:默认
Lax。 - Path:
/;Domain由部署环境可选配置。 - Cookie 名:实现中用常量(如
SESSION_COOKIE_NAME),全仓统一。
3.2 Redis
- 环境变量
REDIS_URL。 - Nitro 进程内 单例连接(插件或惰性初始化),禁止每请求新建连接。
3.3 密码与 token
- 密码哈希:argon2id 或 bcrypt 二选一,全仓单一封装;实现计划选定并锁定依赖。
- 挑战 token:生成随机明文 → 仅存
token_hash;校验时使用恒定时间比较。
3.4 滥用防护
- 对登录、注册等接口做 限流(建议 Redis 计数器 + 时间窗口)。
forgot-password:始终 HTTP 200 + 统一文案,不泄露邮箱是否注册。
3.5 CSRF
- 第一版假设 同站 Nuxt 调用 API,
SameSite=Lax。 - 若未来开放跨站写操作,须引入 CSRF token 或等价机制(单独规格)。
4. API 约定
4.1 路由一览
| 方法与路径 | 行为 |
|---|---|
POST /api/auth/register |
注册;建议成功后直接建立会话(与登录一致) |
POST /api/auth/login |
登录;轮换 session;写 Cookie |
POST /api/auth/logout |
登出;幂等 |
POST /api/auth/forgot-password |
body:{ email };统一 200 响应 |
POST /api/auth/reset-password |
body:{ token, new_password };成功后消费 challenge、更新 password_hash、吊销该用户所有 Redis session |
POST /api/auth/verify-email |
body:{ token };成功则写 email_verified_at、消费 challenge |
GET /api/me |
当前用户摘要;未登录 401 |
PATCH /api/me |
更新资料;未登录 401;若配置要求已验证而未满足 403 |
全仓路径命名保持一致;若调整仅允许整仓重命名并更新本文档。
4.2 错误响应
统一 JSON,例如:
{
"error": {
"code": "EMAIL_IN_USE",
"message": "该邮箱已注册"
}
}
建议状态码:
- 400:校验失败
- 401:未登录或 session 无效
- 403:已登录但不满足策略(如未验证邮箱)
- 409:资源冲突(邮箱已存在)
- 429:限流
- 500:不暴露内部细节;服务端结构化日志记录
响应体 不得 包含 password_hash 或明文 session id。
5. 「未验证」可配置门禁
5.1 原则
requireUser:解析会话,无则 401。requireVerifiedFor(handlerId)或等价:根据 集中配置 判断是否要求email_verified_at非空。- 默认策略:未在配置中声明的操作 仅需登录(避免默认过严导致全站不可用);敏感写操作 显式 声明需验证。
5.2 配置形态
- 使用「路由/能力 id → 是否需要已验证」的映射(对象、模块导出或 JSON)。
- 第一版至少示例:
PATCH /api/me需已验证(可配置关闭以联调);GET /api/me仅需登录。
5.3 前端
- 401:跳转
/login,可带redirect。 - 403(未验证):在
/me使用提示条或轻量引导;不强制整页拦截只读,除非产品后续变更。
6. 邮件与占位行为
6.1 Mailer 抽象
sendVerificationEmail、sendPasswordResetEmail(签名随实现细化)。- 第一版:
NoopMailer,不发起网络投递。
6.2 开发调试
- 环境变量
AUTH_DEBUG_LOG_TOKENS=true时,将验证/重置用 明文 token 写入结构化日志一次;生产默认关闭。
6.3 无邮件生产
reset-password/verify-email仍可用;token 获取走 运维/DB 应急流程(文档化,非产品功能)。
7. 最小 Nuxt 前端
- 页面:
/login、/register、/me(资料 + 登出);登出可在/me上以按钮完成。 - 路由中间件:
auth(保护/me);可选guest(已登录访问登录/注册页时重定向/me)。 - 数据请求:浏览器与 SSR 须保证 携带 Cookie(
credentials: 'include'或同源默认行为以实现时验证为准)。 - 客户端可做基础格式校验;服务端校验为权威。
8. OAuth 第二阶段(边界)
- 登录成功仍使用 同一套 Redis Session + Cookie。
- 用户主体在
users_table;外部身份在linked_accounts。 - 禁止 OAuth 邮箱与已有账号 静默合并;须显式「连接账号」或拒绝策略(实现计划细化)。
- 回调路由占位:
/api/auth/oauth/{provider}/start、/api/auth/oauth/{provider}/callback(路径以实现计划为准)。 email_verified_at是否因 OAuth 声明而自动写入:第二阶段按 provider 策略在实现计划中规定。
9. 测试、环境变量与上线检查
9.1 测试
- 单元:哈希、token 校验、门禁配置解析。
- 集成:Postgres + Redis;覆盖注册→登录→me、未验证 403、验证后通过、登出 401、重置密码吊销会话等(具体用例实现计划列出)。
- 可复用/扩展
scripts/migrate-test.sh等现有脚本启动测试库。
9.2 环境变量(最小集)
| 变量 | 说明 |
|---|---|
DATABASE_URL |
已有 |
REDIS_URL |
新增 |
NODE_ENV |
production 行为见上文 |
AUTH_DEBUG_LOG_TOKENS |
可选;开发用 |
SESSION_TTL_SECONDS |
可选;缺省用代码默认 |
部署可选:COOKIE_DOMAIN、限流相关前缀变量(实现计划定义)。
9.3 上线检查单
- 迁移已执行;Redis 可用;HTTPS +
SecureCookie。 - 关闭
AUTH_DEBUG_LOG_TOKENS;限流与公开 API 审查完成。 - 日志不含密码与生产 session 明文。
9.4 日志
- 复用项目 logger;认证失败、限流、Redis 错误结构化记录。
10. 与现有代码库的关系
- 栈:Nuxt 4、Nitro、Bun、Drizzle、Postgres;Session Redis。
- 现有
users_table与drizzle-pkg迁移流程延续;新表纳入同一包或约定目录。 - 本文档为实现的 唯一需求来源之一;冲突以本文档与后续已批准的变更记录为准。
11. 修订记录
| 日期 | 说明 |
|---|---|
| 2026-04-12 | 初版定稿(brainstorming 各节确认合并) |