oauth-integrations

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

OAuth Integrations for Edge Environments

边缘环境下的OAuth集成

Implement GitHub and Microsoft OAuth in Cloudflare Workers and other edge runtimes.
在Cloudflare Workers及其他边缘运行时中实现GitHub和Microsoft OAuth。

GitHub OAuth

GitHub OAuth

Required Headers

所需标头

GitHub API has strict requirements that differ from other providers.
HeaderRequirement
User-Agent
REQUIRED - Returns 403 without it
Accept
application/vnd.github+json
recommended
typescript
const resp = await fetch('https://api.github.com/user', {
  headers: {
    Authorization: `Bearer ${accessToken}`,
    'User-Agent': 'MyApp/1.0',  // Required!
    'Accept': 'application/vnd.github+json',
  },
});
GitHub API有别于其他提供商的严格要求。
标头要求
User-Agent
必填 - 缺少时返回403
Accept
推荐使用
application/vnd.github+json
typescript
const resp = await fetch('https://api.github.com/user', {
  headers: {
    Authorization: `Bearer ${accessToken}`,
    'User-Agent': 'MyApp/1.0',  // Required!
    'Accept': 'application/vnd.github+json',
  },
});

Private Email Handling

私人邮箱处理

GitHub users can set email to private (
/user
returns
email: null
).
typescript
if (!userData.email) {
  const emails = await fetch('https://api.github.com/user/emails', { headers })
    .then(r => r.json());
  userData.email = emails.find(e => e.primary && e.verified)?.email;
}
Requires
user:email
scope.
GitHub用户可将邮箱设置为私人(
/user
接口返回
email: null
)。
typescript
if (!userData.email) {
  const emails = await fetch('https://api.github.com/user/emails', { headers })
    .then(r => r.json());
  userData.email = emails.find(e => e.primary && e.verified)?.email;
}
需要
user:email
权限范围。

Token Exchange

令牌交换

Token exchange returns form-encoded by default. Add Accept header for JSON:
typescript
const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Accept': 'application/json',  // Get JSON response
  },
  body: new URLSearchParams({ code, client_id, client_secret, redirect_uri }),
});
令牌交换默认返回表单编码格式。添加Accept标头以获取JSON格式:
typescript
const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Accept': 'application/json',  // Get JSON response
  },
  body: new URLSearchParams({ code, client_id, client_secret, redirect_uri }),
});

GitHub OAuth Notes

GitHub OAuth注意事项

IssueSolution
Callback URLMust be EXACT - no wildcards, no subdirectory matching
Token exchange returns form-encodedAdd
'Accept': 'application/json'
header
Tokens don't expireNo refresh flow needed, but revoked = full re-auth
问题解决方案
回调URL必须完全匹配 - 不支持通配符,不匹配子目录
令牌交换返回表单编码添加
'Accept': 'application/json'
标头
令牌不会过期无需刷新流程,但令牌被吊销后需重新完整认证

Microsoft Entra (Azure AD) OAuth

Microsoft Entra(Azure AD)OAuth

Why MSAL Doesn't Work in Workers

为何MSAL在Workers中无法使用

MSAL.js depends on:
  • Browser APIs (localStorage, sessionStorage, DOM)
  • Node.js crypto module
Cloudflare's V8 isolate runtime has neither. Use manual OAuth instead:
  1. Manual OAuth URL construction
  2. Direct token exchange via fetch
  3. JWT validation with
    jose
    library
MSAL.js依赖于:
  • 浏览器API(localStorage、sessionStorage、DOM)
  • Node.js加密模块
Cloudflare的V8隔离运行时不具备上述两者。请使用手动OAuth方式:
  1. 手动构建OAuth URL
  2. 通过fetch直接进行令牌交换
  3. 使用
    jose
    库验证JWT

Required Scopes

所需权限范围

typescript
// For user identity (email, name, profile picture)
const scope = 'openid email profile User.Read';

// For refresh tokens (long-lived sessions)
const scope = 'openid email profile User.Read offline_access';
Critical:
User.Read
is required for Microsoft Graph
/me
endpoint. Without it, token exchange succeeds but user info fetch returns 403.
typescript
// 用于用户身份信息(邮箱、姓名、头像)
const scope = 'openid email profile User.Read';

// 用于刷新令牌(长会话)
const scope = 'openid email profile User.Read offline_access';
关键提示
User.Read
是调用Microsoft Graph
/me
端点的必填项。缺少该权限时,令牌交换会成功,但获取用户信息会返回403。

User Info Endpoint

用户信息端点

typescript
// Microsoft Graph /me endpoint
const resp = await fetch('https://graph.microsoft.com/v1.0/me', {
  headers: { Authorization: `Bearer ${accessToken}` },
});

// Email may be in different fields
const email = data.mail || data.userPrincipalName;
typescript
// Microsoft Graph /me端点
const resp = await fetch('https://graph.microsoft.com/v1.0/me', {
  headers: { Authorization: `Bearer ${accessToken}` },
});

// 邮箱可能存在于不同字段中
const email = data.mail || data.userPrincipalName;

Tenant Configuration

租户配置

Tenant ValueWho Can Sign In
common
Any Microsoft account (personal + work)
organizations
Work/school accounts only
consumers
Personal Microsoft accounts only
{tenant-id}
Specific organization only
租户值可登录用户
common
任何Microsoft账户(个人+工作)
organizations
仅工作/学校账户
consumers
仅个人Microsoft账户
{tenant-id}
仅特定组织

Azure Portal Setup

Azure门户设置

  1. App Registration → New registration
  2. Platform: Web (not SPA) for server-side OAuth
  3. Redirect URIs: Add both
    /callback
    and
    /admin/callback
  4. Certificates & secrets → New client secret
  1. 应用注册 → 新注册
  2. 平台:选择Web(而非SPA)以适配服务器端OAuth
  3. 重定向URI:添加
    /callback
    /admin/callback
  4. 证书和密码 → 新建客户端密码

Token Lifetimes

令牌生命周期

Token TypeDefault LifetimeNotes
Access token60-90 minutesConfigurable via token lifetime policies
Refresh token90 daysRevoked on password change
ID token60 minutesSame as access token
Best Practice: Always request
offline_access
scope and implement refresh token flow for sessions longer than 1 hour.
令牌类型默认生命周期说明
访问令牌60-90分钟可通过令牌生命周期策略配置
刷新令牌90天用户修改密码后会被吊销
ID令牌60分钟与访问令牌生命周期相同
最佳实践:始终请求
offline_access
权限范围,并为超过1小时的会话实现刷新令牌流程。

Common Corrections

常见修正

If Claude suggests...Use instead...
GitHub fetch without User-AgentAdd
'User-Agent': 'AppName/1.0'
(REQUIRED)
Using MSAL.js in WorkersManual OAuth + jose for JWT validation
Microsoft scope without User.ReadAdd
User.Read
scope
Fetching email from token claims onlyUse Graph
/me
endpoint
如果Claude建议...请改用...
不带User-Agent的GitHub请求添加
'User-Agent': 'AppName/1.0'
(必填)
在Workers中使用MSAL.js手动OAuth + jose库进行JWT验证
不带User.Read的Microsoft权限范围添加
User.Read
权限范围
仅从令牌声明中获取邮箱使用Graph
/me
端点

Error Reference

错误参考

GitHub Errors

GitHub错误

ErrorCauseFix
403 ForbiddenMissing User-Agent headerAdd User-Agent header
email: null
User has private emailFetch
/user/emails
with
user:email
scope
错误原因修复方案
403 Forbidden缺少User-Agent标头添加User-Agent标头
email: null
用户设置了私人邮箱使用
user:email
权限范围请求
/user/emails

Microsoft Errors

Microsoft错误

ErrorCauseFix
AADSTS50058Silent auth failedUse interactive flow
AADSTS700084Refresh token expiredRe-authenticate user
403 on Graph /meMissing User.Read scopeAdd User.Read to scopes
错误原因修复方案
AADSTS50058静默认证失败使用交互式流程
AADSTS700084刷新令牌过期重新认证用户
Graph /me端点返回403缺少User.Read权限范围为权限范围添加User.Read

Reference

参考链接