GitHub Auth Flow
完整设计依据见 openspec/changes/add-github-auth/design.md。本文是开发期的快速参考。
主流程时序
┌────────────────────┐ ┌────────────────────┐ ┌──────────────────┐
│ Browser │ │ GitHub OAuth │ │ NestJS (Fly.io) │
│ web.douglas-... │ │ github.com │ │ api.douglas-... │
└─────────┬──────────┘ └─────────┬──────────┘ └─────────┬────────┘
│ │ │
1. 点击「使用 GitHub 继续」 │ │
│ GET /auth/github/start ───────────────┼───────────────────────────────────▶│
│ │ │
│◀── 302 redirect to github.com/login/oauth/authorize?state=xxx&... ─────────│
│ (state 写 __Host-oauth_state cookie) │
│ │ │
2. 用户在 GitHub 同意 ────────────────────────▶│ │
│ │ │
│◀── 302 https://api.../auth/github/callback?code=...&state=... │
│ │ │
3. callback ────────────────────────────────────────────────────────────────────────▶│
│ │ │
│ │◀──── POST /login/oauth/access_token │
│ │ (client_id + secret + code) │
│ ├─── { access_token, ... } ─────────▶ │
│ │ │
│ │◀── GET /user, /user/emails ─────── │
│ │ │
│ │ ┌─ Postgres ─┐ │
│ │ │ users │ ◀── upsert │
│ │ │ sessions │ ◀── insert │
│ │ └────────────┘ │
│◀── 302 https://web.douglas.../projects │
│ Set-Cookie: __Secure-session=<sid>; Domain=.douglasdong.com; ... │
│ │
4. 浏览器后续 fetch('https://api.../auth/me', \{ credentials:'include' \}) │
自动带上 cookie │Cookie 速查
| Cookie 名 | 用途 | 属性 |
|---|---|---|
__Host-oauth_state | OAuth state CSRF + return_to | Path=/、HttpOnly、Secure、SameSite=Lax、Max-Age=600;禁 Domain(前缀要求) |
__Secure-session | 不透明 session id | Domain=.douglasdong.com、Path=/、HttpOnly、Secure、SameSite=Lax、Max-Age=2592000 |
__Secure- vs __Host-:前者要求 Secure,可带 Domain(跨子域 OK);后者更严格,禁 Domain,单 host 锁死。
错误码
?error= | 中文文案 | 触发条件 |
|---|---|---|
invalid_state | 授权请求已过期,请重试 | state cookie 缺失或不匹配 |
access_denied | 你取消了 GitHub 授权 | 用户在 GitHub 同意页点拒绝 |
session_revoked | 会话已失效,请重新登录 | 服务端组件检查 session 失败 |
unknown | 登录失败,请稍后再试 | 其他异常(code 交换失败、上游错误…) |
服务端组件 vs middleware
middleware.ts:只检查 cookie 存在性,跑在 Edge,不调 NestJS。getServerSession():在 RSC 中调 NestJS/auth/me,能真正知道用户是谁、session 是否被 revoke。- 两者协作:middleware 拦截「彻底未登录」的情况,避免一切对 NestJS 的浪费请求;RSC 守卫处理「cookie 在但 session 已撤销」的边角。