MCP OAuth 2.1 授权
为 MCP 客户端构建 OAuth 2.1 授权服务器,支持 PKCE、refresh 令牌轮换和基于范围的访问控制。
AACWorkflow 通过 MCP (Model Context Protocol) 使用 OAuth 2.1 授权暴露其 API。外部 MCP 客户端 — 包括 Claude、ChatGPT 连接器和自定义工具 — 通过 OAuth 服务器认证以获得绑定到 (user, workspace) 对的范围受限、可刷新的访问令牌。
概述
OAuth 服务器实现 Authorization Code grant 配合 PKCE (Proof Key for Code Exchange)、refresh 令牌轮换和动态客户端注册。这确保:
- 用户控制 — 用户通过同意屏幕明确授予工作区访问权
- 基于范围的访问 — 客户端声明需要的资源(issue、评论、智能体等)
- 令牌生命周期 — 短期有效访问令牌(~15 分钟)和可刷新的长期令牌
- 重放检测 — 重放刷新令牌会撤销整个令牌族,检测令牌泄漏
OAuth 流程
1. 客户端注册
客户端一次性通过以下方式注册:
POST /oauth/register
Content-Type: application/json
{
"client_name": "My MCP Tool",
"redirect_uris": ["http://localhost:5000/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "none"
}返回 client_id 和 client_secret(公共客户端无密钥)。
2. 授权代码流程
客户端启动登录:
GET /oauth/authorize?
client_id=<id>&
redirect_uri=http://localhost:5000/callback&
response_type=code&
scope=issues:read+issues:write+comments:read&
code_challenge=<sha256_hash>&
code_challenge_method=S256用户看到列出请求范围和要授予的工作区的同意屏幕。批准后,他们收到授权代码(有效期 10 分钟):
HTTP 302
Location: http://localhost:5000/callback?code=<auth_code>&state=<state>3. 交换代码获取令牌
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=<auth_code>&
client_id=<id>&
code_verifier=<original_plain_challenge>&
redirect_uri=http://localhost:5000/callback返回:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "<refresh_token>",
"scope": "issues:read issues:write comments:read"
}访问令牌是包含以下内容的有签名 JWT:
sub— 用户 IDwsid— 工作区 IDscope— 空格分隔的范围exp— 过期时间(900 秒)
4. 刷新令牌轮换
当访问令牌过期时,使用刷新令牌:
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&
refresh_token=<refresh_token>&
client_id=<id>返回新的访问令牌和新的刷新令牌。旧刷新令牌自动失效。如果重放旧刷新令牌,服务器会撤销整个令牌族,表示可能的令牌泄漏。
范围
范围令牌定义客户端可以访问的资源:
| 范围 | 访问权限 |
|---|---|
issues:read | 读取 issue、状态、时间线 |
issues:write | 创建、更新 issue;更改状态 |
comments:read | 读取 issue 评论和讨论线程 |
comments:write | 创建和编辑评论 |
agents:read | 列出智能体、查看详情 |
agents:write | 创建智能体、分配给 issue |
runtimes:read | 查询运行时状态和日志 |
autopilot:run | 触发自动驾驶 |
dashboard:read | 查询使用情况和质量仪表板 |
客户端在授权期间请求范围子集。令牌仅以请求的范围颁发。如果刷新缩小范围,新令牌的范围少于原始令牌。
端点
| 方法 | 路径 | 目的 |
|---|---|---|
GET | /.well-known/oauth-authorization-server | RFC 8414 元数据发现 |
GET | /oauth/authorize | 同意 UI;颁发授权代码 |
POST | /oauth/token | 代码-令牌和刷新流程 |
POST | /oauth/register | 动态客户端注册 (RFC 7591) |
POST | /oauth/revoke | RFC 7009 令牌撤销 |
发现
客户端可以发现授权服务器元数据:
GET /.well-known/oauth-authorization-server返回:
{
"issuer": "https://aacworkflow.example.com",
"authorization_endpoint": "https://aacworkflow.example.com/oauth/authorize",
"token_endpoint": "https://aacworkflow.example.com/oauth/token",
"registration_endpoint": "https://aacworkflow.example.com/oauth/register",
"revocation_endpoint": "https://aacworkflow.example.com/oauth/revoke",
"scopes_supported": [
"issues:read", "issues:write", "comments:read", "comments:write",
"agents:read", "agents:write", "runtimes:read", "autopilot:run", "dashboard:read"
],
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"token_endpoint_auth_methods_supported": ["none"],
"code_challenge_methods_supported": ["S256"]
}安全考虑
PKCE 强制 — code_challenge_method=S256 必需;不接受 plain。
- 准确的重定向 URI 匹配 — 重定向 URI 与注册值精确验证(无前缀匹配)
- 授权代码一次性 — 授权代码只能交换一次;第二次尝试失败
- Refresh 令牌轮换 — 每次刷新使先前的刷新令牌失效,检测和应对令牌泄漏
- 短期有效访问令牌 — 访问令牌在 15 分钟后过期;客户端必须刷新以续期
- 安全令牌存储 — 令牌在静态时哈希化(与个人访问令牌相同的 KDF);明文令牌不保存
撤销访问
随时撤销令牌(访问或刷新):
POST /oauth/revoke
Content-Type: application/x-www-form-urlencoded
token=<access_or_refresh_token>&
client_id=<id>撤销刷新令牌会级联:从该刷新颁发的所有访问令牌也被失效。
与 MCP 的集成
外部 MCP 服务器使用 OAuth 令牌调用 AACWorkflow 工具。令牌在 Authorization 头中传递:
GET /api/issues
Authorization: Bearer <access_token>服务器验证令牌签名、检查过期并强制范围。如果工具需要 issues:write 但令牌仅有 issues:read,调用被拒绝并返回 403 Forbidden。
审计日志
每个 OAuth 事件(登录、令牌颁发、刷新、撤销、重放检测)记录在审计日志中以用于合规和调查。