---
url: /docs/auth/email-otp.md
---
# 邮箱与验证码

## 服务端配置

密码开关是 `websiteConfig.auth.password`，验证码开关是 `websiteConfig.auth.emailOtp`。配置位于 `apps/api/lib/auth.ts`，邮件发送位于 `apps/api/lib/email.ts`。

密码注册在开发环境不强制邮箱验证；预发布和生产环境需要验证邮件。找回密码通过一次性重置链接完成。验证码插件使用六位验证码、五分钟有效期和最多三次错误尝试。

## 验证码流程

1. 用户选择邮箱验证码，输入邮箱。
2. 客户端请求 `auth.emailOtp.sendVerificationOtp()`，使用 `sign-in` 类型。
3. 用户输入邮件中的验证码，调用 `auth.signIn.emailOtp()`。
4. 登录成功后刷新统一会话查询，再进入工作台。

登录和注册页的验证码流程都可能为新邮箱创建账号，上线前应提供真实服务条款与隐私政策。客户端重发倒计时为 30 秒，只是界面反馈，不替代服务端限流。

## 本地排查

完整预览使用邮件捕获器，邮件记录写入 `.local/outbox.jsonl`。只在本机查看，不提交、不分享完整记录。`bun dev` 则会使用配置的 Resend 真实发送。

线上核对 `RESEND_API_KEY`、`RESEND_EMAIL_FROM` 和已验证的发信域。修改环境后重启接口；模板改动后重新构建邮件包和接口。

## 常见错误

| 现象               | 排查方向                                        |
| ------------------ | ----------------------------------------------- |
| 邮件没有收到       | 区分本地捕获与真实发送；查看发送错误及垃圾邮件  |
| 验证码已过期       | 重新发送并使用最新验证码                        |
| 尝试次数超限       | 返回邮箱步骤，重新获取验证码                    |
| 验证成功但仍未登录 | 查看会话刷新、请求来源和 cookie                 |
| 重置链接打不开     | 检查 `APP_ORIGIN` 和 `/reset-password` 路由分流 |

不要记录生产验证码、重置 token 或密码。验收要覆盖重复注册、错误密码、过期验证码、重复使用重置链接和退出后的接口访问。
