文件
hyb-oa/.trellis/tasks/05-26-oa-email-code-login/prd.md
T

7.5 KiB
原始文件 Blame 文件历史

邮箱验证码登录

Goal

在现有账号密码登录、企业微信登录、密码找回基础上,新增“邮箱验证码登录”。用户可通过企业邮箱接收 6 位验证码,在不输入密码的情况下登录 OA;同时保持验证码风控、审计、会话签发和现有登录体验一致。

Scope

  • 后端新增邮箱验证码登录接口、验证码持久化模型和安全校验。
  • 前端登录页新增“密码登录 / 邮箱验证码”方式切换。
  • 复用现有 SMTP 邮件服务,兼容企业微信里的企业邮箱。
  • 验证成功后复用现有 access token、refresh cookie、has_session 和设备指纹会话逻辑。
  • 增加单元测试、前端 E2E 和视觉截图证据。

What I Already Know

  • 用户已确认实现邮箱验证码登录,并要求前端设计优先使用 frontend-design skill。
  • frontend-design 已安装到 /home/shunli/.codex/skills/frontend-design,当前会话已读取其规则。
  • 当前登录页已有共享 AuthPanel、账号密码登录、企业微信入口、找回密码入口和 PWA 更新保护。
  • 后端已有 PasswordResetCode,仅用于密码找回,不能直接复用为登录验证码。
  • 后端已有 sendNotificationEmailSafe + SMTP 配置,企业邮箱可通过 SMTP 发验证码。
  • 现有 /api/auth/login 已实现设备指纹校验、登录失败锁定、session 数量上限、refresh cookie、has_session、审计日志和企业微信待绑定处理。

Design Direction

  • 采用 frontend-design 的“production-grade、精致、有明确审美方向”要求。
  • 本项目登录页方向定为“refined industrial minimal”:克制、干净、可信、精密,不做营销式大图和复杂装饰。
  • 前端视觉和界面设计以 frontend-design skill 为准;旧有前端设计文档不再作为审美裁决依据。
  • 旧有项目文档仅保留业务事实、品牌识别、验收命令和安全边界参考;与 frontend-design 的视觉建议冲突时,采用 frontend-design。
  • 安全、隐私、Trellis、生产部署和真实数据边界不属于前端审美规范,仍按项目强约束执行。

Requirements

  • 新增登录验证码数据模型,字段至少包括员工、验证码 hash、过期时间、使用时间、尝试次数、创建 IP、创建 UA。
  • 请求验证码接口:
    • POST /api/auth/email-login/code
    • 入参:email
    • 无论账号是否存在或是否可用,返回通用成功,避免枚举账号。
    • 对有效在职账号发送邮件验证码。
    • 对同一邮箱/IP 使用现有登录限流,并在模型层记录验证码请求上下文。
  • 校验登录接口:
    • POST /api/auth/email-login/verify
    • 入参:email、code
    • 请求头必须包含 x-device-fingerprint。
    • 验证码必须未过期、未使用、hash 匹配、尝试次数未超限。
    • 成功后标记验证码已使用,创建 auth session,写 refresh cookie 和 has_session,返回 LoginResponse。
    • 成功后清理员工登录失败锁定状态;如 must_change_password=true,仍返回该状态让前端跳转修改密码。
  • 前端登录页:
    • 普通浏览器下,在主登录区提供“密码登录 / 邮箱验证码”切换。
    • 邮箱验证码登录包含邮箱输入、发送验证码、验证码输入、登录按钮。
    • 发送验证码后保留邮箱和验证码输入,不自动刷新,不持久化验证码。
    • 企业微信入口仍位于次级登录区;企业微信 WebView 下企业微信一键进入仍为主 CTA。
  • 邮件内容:
    • 标题清楚标识 OA 登录验证码。
    • 正文只包含验证码、有效期和非本人操作提醒。
    • 不写入密码、token、完整敏感 payload。

Acceptance Criteria

  • 普通浏览器登录页可切换密码登录和邮箱验证码登录。
  • 邮箱验证码请求成功时显示通用提示,不暴露账号是否存在。
  • 正确验证码可完成登录并进入原 redirect 目标。
  • 错误、过期或已使用验证码不能登录,并有可理解错误提示。
  • 企业微信 WebView 登录层级不回退,企微一键进入仍为主入口。
  • 找回密码验证码和登录验证码数据、接口、审计语义隔离。
  • 前端不持久化验证码、密码或 token。
  • 后端单测、前端 typecheck/build、关键 E2E 和设计规则检查通过。

Definition of Done

  • Prisma schema/migration 可验证。
  • server 相关 auth 单测通过。
  • client typecheck 和 build 通过。
  • Playwright 覆盖密码登录、邮箱验证码登录、找回密码、企业微信入口。
  • 视觉截图覆盖 PC 登录页和移动/企微登录页。
  • Trellis validate、governance、git diff --check 通过。
  • 本地通过后再申请部署和公网 smoke;未部署前不告知用户可公网测试。

Out of Scope

  • 不接入企业微信通讯录邮箱 API 或专业版企业邮箱 API。
  • 不新增短信登录。
  • 不修改企业微信 OAuth/绑定后端逻辑。
  • 不修改真实企业微信后台配置。
  • 不执行真实账号重置、真实业务写入或公网部署。
  • 不改变密码找回流程。

Post-Approval Scope Extension

  • 用户后续明确批准部署和公网测试;已按 SYNC_ENV=0 执行公网部署,不覆盖远端 .env,并完成 live smoke、登录专项 E2E、邮箱验证码公网后端探测和视觉截图验证。

Proposed Implementation

  1. Backend schema:
    • 新增 EmailLoginCode 模型和迁移。
    • 员工表增加 relation。
  2. Backend route:
    • 在 server/src/routes/auth.js 新增请求验证码和校验登录接口。
    • 抽取或复用 session 创建逻辑,确保邮箱验证码登录和密码登录会话行为一致。
    • 增加审计 action:AUTH_EMAIL_LOGIN_CODE_REQUESTED、AUTH_EMAIL_LOGIN_SUCCESS、AUTH_EMAIL_LOGIN_FAILED。
  3. Config:
    • 新增邮箱登录验证码 TTL 和最大尝试次数配置,默认 10 分钟、5 次。
  4. Frontend API:
    • 在 client/src/api/auth.ts 增加请求验证码和校验登录函数。
  5. Frontend UI:
    • LoginPage 增加 segmented login mode。
    • 保持 AuthPanel,按 frontend-design 做更精致的单面板交互、状态提示和间距。
  6. Tests:
    • 后端单测覆盖请求验证码、账号枚举保护、正确验证码登录、错误验证码失败、重复使用失败。
    • 前端 E2E 覆盖邮箱验证码登录路径和现有登录入口不回退。

Test Plan

cd server && npm run prisma:validate
cd server && npm test -- --run tests/unit/routes/auth.login.test.js
cd client && npm run typecheck
cd client && npm run build
E2E_BASE_URL=http://127.0.0.1:5173 npx playwright test tests/e2e/login-portal-ux.spec.ts -g "邮箱验证码登录|登录页提供找回密码|找回密码流程" --workers=1 --reporter=list
E2E_BASE_URL=http://127.0.0.1:5173 npx playwright test tests/e2e/wecom-entry.spec.ts --workers=1 --reporter=list
node scripts/qa/check-ui-design-rules.mjs --strict
python3 ./.trellis/scripts/task.py validate 05-26-oa-email-code-login
node scripts/qa/check-trellis-governance.mjs
git diff --check

Technical Notes

  • Existing auth route: server/src/routes/auth.js
  • Existing email service: server/src/services/email/index.js
  • Existing password reset model: server/prisma/schema.prisma
  • Existing frontend login: client/src/pages/LoginPage.tsx
  • Existing auth API client: client/src/api/auth.ts
  • Existing auth E2E: client/tests/e2e/login-portal-ux.spec.ts
  • Frontend design skill: /home/shunli/.codex/skills/frontend-design/SKILL.md