Skip to Content
流程Auth Flow

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 名用途属性
__Host-oauth_stateOAuth state CSRF + return_toPath=/、HttpOnly、Secure、SameSite=LaxMax-Age=600禁 Domain(前缀要求)
__Secure-session不透明 session idDomain=.douglasdong.comPath=/、HttpOnly、Secure、SameSite=LaxMax-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 已撤销」的边角。