diff --git a/docs/superpowers/specs/2026-06-11-better-auth-performance-design.md b/docs/superpowers/specs/2026-06-11-better-auth-performance-design.md new file mode 100644 index 0000000..abdf7bf --- /dev/null +++ b/docs/superpowers/specs/2026-06-11-better-auth-performance-design.md @@ -0,0 +1,289 @@ +# Better Auth 接口响应时间优化设计 + +## 背景 + +`cfw-auth` 运行在 Cloudflare Worker 上,使用 Better Auth 和 Cloudflare D1 提供认证能力。当前项目已经接入账号中心相关插件,包括 username、phone-number、organization、api-key、expo、email OTP、two-factor、passkey、captcha 等。 + +本设计关注 Better Auth 接口响应时间优化,目标是在不重写 Better Auth adapter、不更换数据库、不改变客户端体验的前提下,先降低认证热路径延迟,并建立可观测的性能闭环。 + +## 范围 + +纳入范围: + +- `/api/auth/get-session` +- 邮箱、用户名、手机号登录注册路径 +- `/phone-number/send-otp` +- `/phone-number/verify` +- organization 邀请和接受邀请相关路径 +- `/api-key/verify` +- Worker 层认证接口耗时观测 +- Better Auth session 和 API key 插件的低风险性能配置 + +不纳入范围: + +- 更换 D1 或重写 Better Auth database adapter +- 引入全新的缓存基础设施 +- 改客户端 UI 或 Expo 客户端实现 +- 改 Better Auth 认证语义 +- 将 OTP 发送异步化 +- 生产环境强制启用 Smart Placement 或 D1 read replication + +## 成功标准 + +- 支持 session cookie cache,以减少 session 读取路径对 D1 的重复访问。 +- 支持 API key 非关键更新延迟执行,以减少 API key 验证路径的同步写入成本。 +- `/api/auth/*` 请求有结构化耗时日志,且不记录 token、cookie、API key、邮箱、手机号或请求体。 +- 性能配置都能通过配置关闭或调低。 +- 本地 `pnpm db:check`、`pnpm typecheck`、`pnpm test` 通过。 +- 纯性能配置不应产生无关 D1 migration SQL 变化。 + +## 方案选择 + +### 推荐方案:低风险热路径优化 + 观测闭环 + +第一阶段只做三件事: + +- 启用 Better Auth `session.cookieCache`。 +- 给 `/api/auth/*` 增加轻量结构化耗时日志。 +- 给 `@better-auth/api-key` 配置 `deferUpdates`,复用现有 Better Auth `advanced.backgroundTasks.handler` 和 Worker `executionCtx.waitUntil`。 + +这个方案改动小,能直接覆盖最可能的热路径瓶颈,也能为后续更重的 Cloudflare 平台优化提供证据。 + +### 备选方案:平台定位优化 + +开启 Cloudflare `observability` 和生产环境 `placement.mode = "smart"`,通过线上 p50、p95、p99 对比验证 Worker 与 D1、外部 provider 的距离是否是主要瓶颈。 + +该方案保留为第二阶段,因为如果当前瓶颈是 session 查询、API key 写入或外部短信/邮件,仍需要第一阶段先拆清楚路径。 + +### 暂缓方案:存储层重构 + +引入 secondary storage、D1 read replication/Sessions API 或专门缓存层。该方向潜在收益高,但会增加一致性、安全和 adapter 适配复杂度。本次只记录决策门槛,不实施。 + +## 架构设计 + +保留现有三层架构。 + +第一层是 Worker/Hono 入口,由 `src/index.ts` 负责: + +- CORS +- `/api/auth/*` 路由挂载 +- 把 `executionCtx.waitUntil` 传给 Better Auth +- 追加认证接口观测包装 + +第二层是 Better Auth 配置,由 `src/auth.ts` 负责: + +- `database: env.DB` +- `secret` +- `baseURL` +- `trustedOrigins` +- email/password 配置 +- session 性能配置 +- `advanced.backgroundTasks.handler` +- 插件列表 + +第三层是插件和外部服务适配,由 `src/plugins.ts`、`src/email.ts`、`src/sms.ts` 负责: + +- Better Auth plugin 配置 +- 邮件 provider +- SMS provider +- API key 性能配置 + +新增一个小型观测边界,例如 `src/observability.ts`。它只负责记录请求耗时,不参与认证语义,不改写 Better Auth 响应。 + +## 数据流 + +### Session 读取路径 + +请求进入 Worker 后仍交给 Better Auth。启用 `session.cookieCache` 后,短 TTL 内的 session 数据可以从签名 cookie cache 读取,从而减少 D1 session 查询。 + +建议默认: + +```ts +session: { + cookieCache: { + enabled: true, + maxAge: 300, + }, +} +``` + +TTL 可以通过 env 配置。若安全策略要求更高,可以降到 `60` 秒或关闭。 + +### OTP、邮件和短信路径 + +短信 OTP、验证邮件、密码重置邮件默认保持同步 `await`。这些路径的外部发送动作是用户感知成功的一部分,不能为了接口更快而提前返回成功。 + +后续可以异步化的仅限: + +- 非关键通知 +- 审计日志 +- 分析事件 +- 不影响用户下一步操作的后台同步 + +### API key 验证路径 + +API key 的验证结果必须同步返回。请求计数、`lastRequest`、`remaining` 等非关键更新可以延迟执行。 + +`@better-auth/api-key` 提供 `deferUpdates`,并要求主 Better Auth 配置存在 `advanced.backgroundTasks.handler`。当前项目已经通过 Worker `waitUntil` 提供该 handler,因此可以将 `deferUpdates` 作为默认开启的性能配置。 + +## 一致性边界 + +### 可接受的短暂陈旧 + +`session.cookieCache` 可能导致 session 撤销、封禁、角色变化等状态在 TTL 内不立即反映。默认 TTL 不能过长,建议 `300` 秒起步,并允许配置为更短。 + +### 可接受的 eventual consistency + +API key 计数和时间戳更新可以接受 eventual consistency。API key 是否有效、是否匹配、是否过期等核心判断仍必须同步完成。 + +### 不接受的提前成功 + +OTP 发送、验证邮件、密码重置邮件不应在 provider 未完成时提前返回成功。否则用户可能收到成功响应却拿不到验证码或链接。 + +## 配置策略 + +建议新增或使用以下配置: + +- `BETTER_AUTH_SESSION_COOKIE_CACHE_MAX_AGE` + - 默认:`300` + - 用途:控制 Better Auth session cookie cache TTL。 + +- `API_KEY_DEFER_UPDATES` + - 默认:`true` + - 用途:控制 API key 非关键更新是否延迟执行。 + +- Cloudflare `observability` + - 建议写入 `wrangler.jsonc` + - 初始 `head_sampling_rate` 使用低采样,例如 `0.1` + +- Cloudflare `placement.mode = "smart"` + - 不作为本次强制配置 + - 作为生产验证选项记录 + +非法配置处理: + +- session cache TTL 非数字或小于等于 0 时使用默认值。 +- `API_KEY_DEFER_UPDATES=false` 时关闭延迟更新;其他值按默认开启处理。 + +## 观测设计 + +结构化日志字段: + +- `event`: 固定为 `auth_request` +- `method` +- `path` 或归一化后的 endpoint +- `status` +- `durationMs` +- `colo`,来自 `request.cf?.colo` +- `cfRay`,来自请求头 `cf-ray` + +不得记录: + +- `Authorization` +- `Cookie` +- API key +- session token +- 请求体 +- 邮箱 +- 手机号 +- OTP code + +观测层失败时必须静默降级,不影响 Better Auth 响应。 + +## 错误处理 + +- Better Auth 业务错误保持原样返回。 +- 观测层不能改写响应体、状态码或 header。 +- 后台任务失败只记录错误,不回滚已经返回给客户端的响应。 +- `waitUntil` 接收的 promise 必须捕获并记录错误,避免隐藏失败。 + +## 测试策略 + +### 配置单元测试 + +扩展现有 `tests/auth-config.test.ts`: + +- 默认 session cookie cache TTL 是 `300`。 +- env 可覆盖 TTL。 +- 非法 TTL 使用默认值。 +- API key defer 默认开启。 +- `API_KEY_DEFER_UPDATES=false` 可以关闭。 + +### Worker 行为测试 + +扩展现有 `tests/auth-worker.test.ts`: + +- `/api/auth/*` 请求仍能正常返回。 +- 观测包装不改写 Better Auth 响应。 +- 日志包含 `durationMs`、`method`、`status`。 +- 日志不包含 `cookie`、`authorization`、手机号、邮箱、API key 等敏感信息。 + +### 本地验收 + +必须通过: + +```bash +pnpm db:check +pnpm typecheck +pnpm test +``` + +如果本地 Worker 可运行,补充烟测: + +```bash +curl -i http://localhost:8788/api/auth/get-session +curl -i http://localhost:8788/api/auth/reference +``` + +## 生产验证策略 + +本次不要求完成生产验证,但部署后应对比: + +- `get-session` p50、p95、p99 +- `api-key/verify` p50、p95、p99 +- OTP 发送路径 p95 +- organization invitation 路径 p95 +- Worker colo 分布 +- D1 或外部 provider 是否主导尾延迟 + +只有当观测显示主要延迟来自远端等待时,再评估开启 Smart Placement。只有当 D1 读延迟明确成为瓶颈时,再评估 D1 read replication、Sessions API 或 secondary storage。 + +## 后续决策门槛 + +进入平台定位优化的门槛: + +- 第一阶段上线后,p95 仍不能接受; +- 日志显示延迟主要来自 D1 或外部 provider; +- 多地域请求有明显延迟差异。 + +进入存储层重构的门槛: + +- `get-session` 或 organization 读路径被证明确实受 D1 读延迟限制; +- session cookie cache 无法满足安全或一致性要求; +- API key rate counter 写入仍是高频瓶颈; +- 有明确的强一致、弱一致、撤销语义设计。 + +## 风险 + +- session cookie cache 会引入短 TTL 内的撤销延迟。 +- API key `deferUpdates` 会让计数类字段 eventual consistent。 +- 观测日志若设计不当可能泄露敏感信息。 +- 过早引入 secondary storage 会增加一致性复杂度。 +- Smart Placement 可能改善后端等待,也可能对当前瓶颈帮助有限,必须用数据验证。 + +## 实施顺序 + +1. 抽出性能配置解析函数。 +2. 在 `src/auth.ts` 启用 session cookie cache。 +3. 在 `src/plugins.ts` 为 API key 配置 `deferUpdates`。 +4. 增加 `/api/auth/*` 观测包装。 +5. 增加配置和 Worker 行为测试。 +6. 更新 operations 文档。 +7. 运行本地验证。 + +## 自检结论 + +- 无未完成标记或空白段落。 +- 范围限定在第一阶段低风险优化,没有混入存储层重构。 +- 架构、数据流、一致性、配置和测试互相一致。 +- 每个可能引入安全或一致性变化的优化都有关闭或调低路径。