OAuth 登录
以 Github OAuth 为例。
项目通过 GitHub OAuth 完成第三方身份认证。GitHub 负责确认用户身份,认证成功后由本项目创建或更新本地用户,并签发项目 JWT。后续访问 /api/* 接口时携带该 JWT 机械能认证。
项目下 src/endpoints/auth/github-login 提供 默认实现、@hono/oauth-providers 两个方案。
登录流程
mermaid
sequenceDiagram
participant Browser as 浏览器
participant Worker as Hono Worker
participant GitHub as GitHub OAuth/API
participant D1 as D1
Browser->>Worker: GET /auth/github/login
Worker-->>Browser: 写入 state Cookie,302 跳转 GitHub
Browser->>GitHub: 用户授权
GitHub-->>Browser: 回调 /auth/github/login?code&state
Browser->>Worker: 携带 code、state 和 state Cookie
Worker->>Worker: 校验 Cookie 中的 state
Worker->>GitHub: 使用 code 换取 Access Token
Worker->>GitHub: 获取用户资料和邮箱
Worker->>D1: 查询或创建本地用户及 OAuth 账户
Worker-->>Browser: 302,将 JWT 写入 Cookie,重定向到前端登录完成页或者登录失败页面环境配置
需要去 Github Settings 创建 OAuth App,获取 GITHUB_CLIENT_ID 和 GITHUB_CLIENT_SECRET。
bash
JWT_SECRET=replace_with_a_long_random_secret
GITHUB_CLIENT_ID=replace_with_github_client_id
GITHUB_CLIENT_SECRET=replace_with_github_client_secret线上环境使用 Wrangler Secret 保存凭据:
bash
bunx wrangler secret put JWT_SECRET
bunx wrangler secret put GITHUB_CLIENT_ID
bunx wrangler secret put GITHUB_CLIENT_SECRET在 GitHub OAuth App 中,将 Authorization callback URL 配置为:
text
https://<你的 Worker 域名>/auth/github/loginHono Oauth Providers 实现
typescript
openapi.use("/auth/github/login", GithubAuthMiddlewares);
openapi.get("/auth/github/login", GithubHonoAuthLogin);首次请求与 GitHub 回调共用 GET /auth/github/login。
具体步骤如下:
- 浏览器访问
GET /auth/github/login。 GithubAuthMiddlewares生成随机state,将其写入名为state的 Cookie,并返回302跳转到 GitHub 授权页。- 中间件请求
read:user和user:email权限。当前配置的oauthApp: true表示使用 GitHub OAuth App。 - GitHub 授权完成后,将浏览器重定向回同一路径,并附带
code和state查询参数。 - 中间件比较查询参数中的
state与 Cookie 中保存的值;不匹配或缺失时返回401。 - 中间件使用
code、GITHUB_CLIENT_ID和GITHUB_CLIENT_SECRET换取 GitHub Access Token,然后请求 GitHub 用户资料与邮箱。 GithubHonoAuthLogin从 Hono Context 中读取user-github,并调用AuthQueries.loginWithGithub()关联本地身份。- 项目签发自己的 JWT,并以
201 Created返回登录结果。
中间件写入的 state Cookie 有效期为 10 分钟,并设置了 HttpOnly、Secure、SameSite=Lax 和 Path=/。OAuth 登录依赖浏览器在回调时带回该 Cookie。
自定义 Oauth 实现
GithubAuthLogin 是保留的自定义 Oauth实现。同样以 GitHub 为例,流程如下:
- 生成
state、PKCEcodeVerifier和codeChallenge。 - 将 OAuth 事务写入 KV 的
oauth_transactions:<state>,过期时间设为 5 分钟。 - 将
redirect_uri、state、code_challenge和code_challenge_method=S256发送给 GitHub。 - 回调时从 KV 读取事务并检查过期时间。
- 使用授权码与
codeVerifier换取 GitHub Access Token。 - 获取 GitHub 用户资料以及 primary、verified 邮箱,再执行本地身份映射和 JWT 签发。
OAuth 事务结构由 OAuthTransactionsSchema 校验,包含:
stateHash、provider、codeVerifierintent、initiatorUserIdredirectTo、expiresAtexchangeCodeHash、exchangedAtconsumedAt、resolvedUserIdcreatedAt、updatedAt
在 wrangler.jsonc 中声明了以下普通变量:
diff
{
"vars": {
+ "API_ORIGIN": "https://<你的 Worker 域名>",
},
}API_ORIGIN:生成回调地址。
前端接入
前端通过页面跳转发起 OAuth:
typescript
window.location.assign(`${apiOrigin}/auth/github/login`);在授权完成后的重定向回调界面中, 从 Cookie 中解析 auth:
typescript
const authCookie = document.cookie
.split("; ")
.find((row) => row.startsWith("auth="))
?.split("=")[1];
const auth = authCookie ? JSON.parse(decodeURIComponent(authCookie)) : null;
const token = auth?.token;本地身份映射
GitHub 身份与本地用户分别存储在 users_table 和 oauth_accounts_table:
| 数据 | 存储位置 | 用途 |
|---|---|---|
| 本地用户资料 | users_table | 保存名称、邮箱和头像等业务用户信息 |
| GitHub 身份 | oauth_accounts_table | 保存 GitHub 用户 ID、登录名、邮箱以及关联的本地 userId |