修复认证验证码投递与登录入口体验 05-27-oa-auth-delivery-wecom-entry-fix

这个提交包含在:
2026-05-27 10:14:49 +08:00
父节点 347d337c0c
当前提交 511efa0b2d
共修改 17 个文件,包含 746 行新增和 63 行删除
@@ -0,0 +1,4 @@
{"file": ".trellis/spec/hyb-oa/backend-fastify-prisma.md", "reason": "Check backend syntax, focused route tests and Prisma validation."}
{"file": ".trellis/spec/hyb-oa/frontend-react-vite-design.md", "reason": "Check frontend typecheck, build, Playwright auth flows and screenshot evidence."}
{"file": ".trellis/spec/hyb-oa/security-privacy.md", "reason": "Check test output and task notes do not expose SMTP secrets, real verification codes or sensitive accounts."}
{"file": ".trellis/spec/hyb-oa/wecom-boundary.md", "reason": "Check ordinary browser WeCom entry is non-clickable while WebView CTA remains available."}
@@ -0,0 +1,4 @@
{"file": ".trellis/spec/hyb-oa/backend-fastify-prisma.md", "reason": "Auth route and email service changes must follow backend route/service validation rules."}
{"file": ".trellis/spec/hyb-oa/frontend-react-vite-design.md", "reason": "Login and forgot-password UI changes must follow auth-page layout, PWA and frontend design contracts."}
{"file": ".trellis/spec/hyb-oa/security-privacy.md", "reason": "Auth delivery work must avoid leaking credentials, verification codes, tokens or account-existence signals."}
{"file": ".trellis/spec/hyb-oa/wecom-boundary.md", "reason": "Enterprise WeChat login entry behavior must preserve OA/WeCom boundary and avoid unapproved backend OAuth changes."}
@@ -0,0 +1,15 @@
# Implementation Notes
## Approach
- Backend exposes `/api/auth/capabilities` with non-secret email delivery readiness.
- Email-login code and forgot-password code requests fail before account lookup when SMTP delivery is not configured, preserving account enumeration protection.
- Verification email copy uses Chinese Beijing-time expiry wording instead of ISO UTC timestamps.
- Normal browsers show Enterprise WeChat as an informational entry only; Enterprise WeChat WebView keeps one-click login as the primary CTA.
- Frontend email-code login and forgot-password pages disable code sending when delivery is not ready.
## Verification Focus
- Backend unit coverage for delivery capability, delivery-unavailable behavior and Beijing-time email wording.
- Frontend E2E coverage for password visibility, PWA update prompt, email-code login, disabled email delivery state, forgot-password flow and Enterprise WeChat entry hierarchy.
- Deployment checks report public auth capability status without printing SMTP values.
@@ -0,0 +1,124 @@
# 认证验证码投递与企业微信入口修复
## Goal
修复部署后认证入口的两个真实问题:邮箱登录验证码和密码重置验证码无法收到;普通浏览器点击企业微信登录会跳转到只能在企业微信内使用的 OAuth 页面并报错。目标是让认证入口行为和真实能力一致,避免用户误判“验证码已发送”或点击错误入口。
## Scope
- 本任务覆盖后端认证接口、邮件投递能力探测、认证验证码邮件文案、登录页、找回密码页、普通浏览器企业微信入口提示、部署检查和对应自动化测试。
- 本任务不接入 PC 企业微信扫码登录,不修改企业微信后台配置,不在未获批准时继续修改远端 `.env` 或部署代码。
## What I Already Know
- 用户反馈:
- 邮箱收不到登录验证码和密码重置验证码。
- 点击企业微信登录报错,实际需要在企业微信里点击。
- 登录验证码和密码重置验证码都通过 `sendNotificationEmailSafe` + SMTP 邮件服务发送。
- 公网远端运行时配置显示:
- `emailEnabled=false`
- `smtpHost=false`
- `smtpUser=false`
- `smtpPass=false`
- `emailFrom=true`
- `smtpPort=587`
- `smtpSecure=false`
- 本地 `~/.hyb-oa/live-smoke.env` 也没有 SMTP 配置可直接同步。
- 当前邮箱验证码接口会在账号存在且 active 时创建验证码和审计记录,然后异步调用邮件发送;当邮件服务关闭时,用户仍看到通用成功提示。
- 当前普通浏览器下只要 `wecomEntry` ready,就显示“企业微信登录”按钮;点击后进入 `/api/auth/wecom/login` 并跳转企业微信 OAuth。
- 当前企业微信入口的 OAuth 设计是企业微信工作台/WebView 入口,不是 PC 浏览器扫码登录。
## Diagnosis
- 邮箱问题的直接原因是公网试运行环境未启用或配置 SMTP,两个验证码功能没有可用邮件通道。
- 代码体验问题是认证页没有把“邮件服务未启用”和“普通浏览器不支持企业微信 OAuth”转化为清晰交互。
- 安全约束:验证码请求仍必须避免账号枚举。不能因为邮箱是否存在而返回不同提示。
## Discussion Notes
- 企业邮箱 SMTP 配置复杂度低,前提是企业邮箱已允许客户端 SMTP,且能为专用发件邮箱生成客户端专用密码或授权码。
- 推荐使用独立发件邮箱,例如 `oa` 或 `no-reply` 类账号;不要使用个人员工邮箱或管理员真实登录密码。
- 试运行环境当前缺 SMTP 配置,因此必须先补远端 `.env` 并重启 server,才能验证真实收信。
- PC 企业微信扫码登录是独立能力,不等同于当前“企业微信内 OAuth 免登”。它适合 PC 常用用户,但需要单独配置企业微信授权登录回调域、前端二维码入口和后端扫码回调处理。
- 当前修复建议先做 SMTP 投递和普通浏览器企业微信入口防误点;PC 企业微信扫码登录可列为后续 P1,不放进本次故障修复 MVP。
## Requirements
- 邮件服务全局未启用或 SMTP 未配置时:
- 登录页邮箱验证码发送应提示“邮件服务暂未启用,请联系管理员或使用密码登录”,而不是“验证码将发送”。
- 忘记密码页应给出同类提示。
- 不暴露输入邮箱是否存在。
- 公网/部署检查应能发现邮件验证码依赖的 SMTP 未启用状态,避免再次“功能上线但无法收信”。
- 普通浏览器下:
- 不提供可点击的企业微信 OAuth 登录按钮。
- 显示清晰提示:企业微信一键进入需从企业微信工作台或企业微信内打开 OA。
- 不影响企业微信 WebView 下主 CTA。
- 企业微信 WebView 下:
- 企微一键进入仍为主入口。
- 账号密码/邮箱验证码兜底仍可按现有逻辑出现。
- 不打印、提交或写入 SMTP 密码、真实邮箱、验证码、token、Secret。
## Acceptance Criteria
- [ ] 公网或本地配置缺 SMTP 时,邮箱验证码和忘记密码不再误提示“验证码已发送”。
- [ ] 配置完整时,验证码请求仍返回通用成功,不暴露账号是否存在。
- [ ] 普通浏览器不再点击进入企业微信 OAuth 报错页。
- [ ] 企业微信 WebView 下企业微信主入口不回退。
- [ ] 后端/前端测试覆盖邮件服务未启用提示和普通浏览器企微入口提示。
- [ ] 部署/公网检查能报告邮件投递配置是否 ready。
- [ ] 需要真实收信验证时,仅在获得 SMTP 凭据和远端配置修改批准后执行。
## Proposed Implementation
1. Backend:
- 增加一个安全的公开认证能力配置接口,或扩展现有公开 auth config,暴露 `emailDeliveryReady` 这类非敏感布尔值。
- 在邮箱验证码和忘记密码请求中,若邮件服务全局未配置,返回统一错误(不依赖邮箱是否存在),让前端能提示服务未启用。
2. Frontend:
- 登录页邮箱验证码模式根据 `emailDeliveryReady` 禁用发送按钮并显示说明。
- 忘记密码页根据 `emailDeliveryReady` 禁用发送或显示服务未启用说明。
- 普通浏览器下企业微信区域改为说明性入口,不触发 OAuth;企业微信 WebView 才显示可点击 CTA。
3. QA:
- 增加/更新 auth 单测覆盖邮件未配置分支。
- 更新登录页、忘记密码、企微入口 E2E。
- 更新 live smoke/env 检查,输出邮件投递 ready 状态但不打印 SMTP 值。
4. Operations:
- 若用户提供并批准 SMTP 配置,先备份远端 `.env`,再写入 SMTP 相关键并重启 server。
- 配置后执行一次受控真实收信验证,不在日志和记录中保存验证码。
## Test Plan
```bash
cd server && node --check src/routes/auth.js src/routes/wecomAuth.js src/config.js
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 tests/e2e/wecom-entry.spec.ts --workers=1 --reporter=list
node deploy/trialrun/validate-env-contract.js
node scripts/qa/check-live-smoke-env.mjs
python3 ./.trellis/scripts/task.py validate 05-27-oa-auth-delivery-wecom-entry-fix
node scripts/qa/check-trellis-governance.mjs
git diff --check
```
## Out of Scope
- 不在本任务接入 PC 企业微信扫码登录。
- 不在未获批准时修改远端 `.env`。
- 不在聊天、Trellis、docs 或日志中记录 SMTP 密码、验证码或完整账号。
- 不修改企业微信后台配置。
## Technical Notes
- Email service: `server/src/services/email/index.js`
- Auth routes: `server/src/routes/auth.js`
- WeCom auth routes: `server/src/routes/wecomAuth.js`
- Login page: `client/src/pages/LoginPage.tsx`
- WeCom entry helpers: `client/src/lib/wecomEntry.ts`
- Runtime context: `client/src/lib/runtimeContext.ts`
- Live env check: `scripts/qa/check-live-smoke-env.mjs`
## Progress Notes
- 公网 SMTP 配置已完成一次受控验证:有效企业邮箱可以收到登录验证码和密码重置验证码;邮件被企业邮箱归入免提醒邮件。
- 本地代码已补齐认证能力接口、北京时间有效期文案、邮箱验证码入口禁用态和普通浏览器企业微信入口提示。
- 截止本记录,代码改动尚未部署到公网;公网 `/api/auth/capabilities` 仍需部署后验证。
@@ -0,0 +1,34 @@
{
"id": "oa-auth-delivery-wecom-entry-fix",
"name": "oa-auth-delivery-wecom-entry-fix",
"title": "认证验证码投递与企业微信入口修复",
"description": "修复邮箱验证码真实投递能力、验证码有效期展示和普通浏览器企业微信入口误点击问题,并补齐登录页交互验证。",
"status": "in_progress",
"dev_type": null,
"scope": "server/client/auth",
"package": null,
"priority": "P2",
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-05-27",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": null,
"relatedFiles": [
"server/src/routes/auth.js",
"server/src/services/email/index.js",
"client/src/pages/LoginPage.tsx",
"client/src/pages/ForgotPasswordPage.tsx",
"client/src/api/auth.ts",
"client/tests/e2e/login-portal-ux.spec.ts",
"client/tests/e2e/wecom-entry.spec.ts"
],
"notes": "",
"meta": {}
}