本文汇总当前分支未提交的 OTP 邮箱验证码登录相关改动,便于后续维护或复查。内容仅包含实现思路和代码位置,不包含任何敏感信息。
核心改动概览
- 客户端切换到 better-auth 的 email-otp 流程,放弃 magic link。
- 前端登录表单支持 Google 登录 + 邮箱验证码两步流程,带倒计时、重发与错误提示。
- 服务端启用 emailOTP 插件,配置发送验证码邮件、过期时间与速率限制。
- 新增验证码邮件模板(中文),支持登录/邮箱验证/重置密码场景。
客户端(登录表单)
文件:src/client/components/auth/login-form.tsx
- 状态机:
step: "request" | "verify";倒计时、错误提示、邮箱/验证码输入。 - 持久化:邮箱、步骤、redirect、倒计时写入 localStorage + sessionStorage;同时同步到 URL 查询参数
otpEmail/otpStep/redirect,即使刷新或被重定向回/login也能继续输入验证码。 - 错误提示:捕获
Invalid OTP/OTP expired等返回,转为中文提示,并保留在验证步骤。 - 安全:
redirect仅允许同源相对路径(防止开放跳转)。 - 手动切回邮箱输入时清理本地 OTP 状态。
辅助方法:requestEmailOtp 与 signIn.emailOtp 来自 src/client/lib/auth.ts,该文件使用 emailOTPClient 插件创建 better-auth 客户端。
服务端(better-auth 配置)
文件:src/server/lib/auth.ts
- 插件:
emailOTP({ otpLength: 6, expiresIn: 10*60, sendVerificationOTP }) - 发送逻辑:
sendVerificationOTP调用邮件工具emailSender.sendOtpCode,并记录日志。 - 速率限制:
/email-otp/send-verification-otp5 分钟内最多 3 次;/sign-in/*5 分钟 5 次。 - 开放路由:
src/server/config/routes.ts将/api/auth/email-otp/*列入公开名单(Better Auth 内部校验)。
邮件发送与模板
文件:
-
src/server/lib/email/sender.ts:新增sendOtpCode,复用 Resend 客户端。 -
src/server/lib/email/templates/otp-code.ts:中文模板,区分场景文案(登录/验证邮箱/重置密码),附带有效期提示与安全提示。 -
src/server/lib/email/types.ts:补充 OTP 类型定义。 -
src/server/lib/email/index.ts:导出新模板。
关键路由配置
文件:src/server/config/routes.ts
-
PUBLIC_API_ROUTES添加/api/auth/email-otp/*,便于前端无会话请求验证码、验证登录。
使用步骤(端到端)
- 客户端输入邮箱,点击“发送验证码” → 调用
requestEmailOtp(email)→/api/auth/email-otp/send-verification-otp。 - 收到邮件中的 6 位验证码,在同一或新页面输入;URL 若含
otpEmail/otpStep=verify会自动恢复状态。 - 提交验证:
signIn.emailOtp({ email, otp })→/api/auth/sign-in/email-otp;成功后按安全处理过的redirect跳转。 - 错误/过期:前端展示中文提示,保留验证步骤,允许重试或重发。
参考文件列表
- 客户端逻辑:
src/client/components/auth/login-form.tsx - Auth 客户端创建:
src/client/lib/auth.ts - Auth 服务端配置:
src/server/lib/auth.ts - 公共路由白名单:
src/server/config/routes.ts - 邮件发送与模板:
src/server/lib/email/sender.ts、src/server/lib/email/templates/otp-code.ts、src/server/lib/email/types.ts - 其它文档:
docs/项目全览-zh.md(仅有一处新增公开路由描述)
验证点
- 前端:错误验证码提示为中文;刷新/重定向后仍停留在验证码步骤;重发冷却与倒计时正常。
- 服务端:验证码邮件可发送,10 分钟内有效;速率限制生效;
redirect同源校验通过。 - 安全:外部跳转被拒绝,所有 OTP 请求均为公开路由但由 better-auth 校验邮箱/验证码。