一个OAuth 2.1授权服务器,可以为任何MCP(模型上下文协议)服务器添加身份验证,而无需修改代码。网关作为OAuth授权服务器运行,并使用GitHub作为用户身份验证的身份提供商(IdP)。
这是一个MCP协议的参考实现和测试平台。
MCP OAuth网关是一个零修改认证层,适用于MCP服务器。它实现了OAuth 2.1,包括动态客户端注册(RFC 7591/7592),并利用GitHub作为用户身份验证的身份提供商。架构遵循以下核心原则:
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ 外部客户端 │
│ (Claude.ai, MCP CLI工具, IDE扩展, 自定义集成) │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
HTTPS │ :443
↓
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ TRAEFIK反向代理 │
│ (第1层:路由与TLS终止) │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ • 使用Let's Encrypt自动为所有子域生成HTTPS证书 │
│ • 基于优先级的路由规则 (OAuth > Verify > MCP > 全部捕获) │
│ • 使用ForwardAuth中间件对MCP端点进行认证 → 认证服务 /verify │
│ • 根据子域和路径进行请求路由: │
│ - auth.domain.com/* → 认证服务 (无需认证) │
│ - *.domain.com/.well-known/* → 认证服务 (OAuth发现) │
│ - *.domain.com/mcp → MCP服务 (通过ForwardAuth认证) │
│ • 通过标签进行Docker服务发现 │
└─────────────────────────────────────────────────────────────────────────────────────┘
│ │
│ OAuth/认证请求 │ MCP请求
│ (未认证) │ (已认证)
↓ ↓
┌───────────────────────────────────────────┐ ┌─────────────────────────────────────┐
│ 认证服务 │ │ MCP服务 │
│ (第2层:OAuth授权服务器) │ │ (第3层:协议处理器) │
├───────────────────────────────────────────┤ ├─────────────────────────────────────┤
│ 容器: auth:8000 │ │ 容器: │
│ 包: mcp-oauth-dynamicclient │ │ • mcp-echo-stateful:3000 │
│ │ │ • mcp-echo-stateless:3000 │
│ │ │ • mcp-fetch:3000 │
│ OAuth端点: │ │ • mcp-memory:3000 │
│ • POST /register (RFC 7591) │ │ • mcp-time:3000 │
│ • GET /authorize + /callback │ │ • ... (动态启用) │
│ • POST /token │ │ │
│ • GET /.well-known/* (RFC 8414) │ │ 架构: │
│ • POST /revoke, /introspect │ │ • mcp-streamablehttp-proxy包装 │
│ │ │ • 启动官方MCP stdio服务器 │
│ 管理端点 (RFC 7592): │ │ • 桥接stdio ↔ HTTP/SSE │
│ • GET/PUT/DELETE /register/{client_id} │ │ • 无OAuth知识 │
│ │ │ • 在头部接收用户身份 │
│ 内部端点: │ │ │
│ • GET/POST /verify (ForwardAuth) │ │ 协议端点: │
│ │ │ • POST /mcp (HTTP上的JSON-RPC) │
│ 外部集成: │←---│ • GET /mcp (异步消息的SSE) │
│ • GitHub OAuth (用户身份验证) │ │ • 在/health上进行健康检查 │
└───────────────────────────────────────────┘ └─────────────────────────────────────┘
│ ↑
│ │
└──────────────┬───────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ Redis存储层 │
│ (持久状态管理) │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ 容器: redis:6379 │
│ 持久化: AOF + RDB快照 │
│ │
│ 数据结构: │
│ • oauth:client:{client_id} → OAuth客户端注册 (90天 / 永久) │
│ • oauth:state:{state} → 授权流程状态 (5分钟) │
│ • oauth:code:{code} → 授权码 + 用户信息 (1年) │
│ • oauth:token:{jti} → JWT令牌跟踪以撤销 (30天) │
│ • oauth:refresh:{token} → 刷新令牌数据 (1年) │
│ • oauth:user_tokens:{username} → 用户活动令牌索引 │
│ • redis:session:{id}:state → MCP会话状态 (由代理管理) │
│ • redis:session:{id}:messages → MCP消息队列 │
└─────────────────────────────────────────────────────────────────────────────────────┘
网络拓扑:
网关实现了一个多层OAuth 2.1授权系统,结合了客户端凭据认证和GitHub OAuth身份联合:
网关实现了一个复杂的OAuth 2.1系统,具有三个不同的认证流程:
需要认证吗?
├─> 对于网关自身的GitHub访问?
│ └─> 使用设备流:`just generate-github-token`
│ - 显示代码:“访问github.com/login/device”
│ - 不需要浏览器重定向
│ - 将GITHUB_PAT存储在.env中
│
├─> 对于MCP客户端令牌?
│ └─> 使用设备流:`just mcp-client-token`
│ - 客户端使用设备流进行无浏览器认证
│ - 将MCP_CLIENT_ACCESS_TOKEN存储在.env中
│
└─> 对于最终用户访问(浏览器)?
└─> 使用标准OAuth流
- 用户访问受保护资源
- 被重定向到GitHub进行登录
- 返回到网关
- 发布包含用户+客户端身份的JWT
网关实现结合了客户端凭据认证和GitHub用户认证:
╔═══════════════════════════════════════════════════════════════════════════════════╗
║ OAUTH客户端注册 (RFC 7591/7592) ║
╠═══════════════════════════════════════════════════════════════════════════════════╣
║ ║
║ 📝 步骤1:客户端注册(无需认证) ║
║ ┌─────────────────────────────────────────────────────────────────────────────┐ ║
║ │ POST /register │ ║
║ │ • 公开端点 - 任何MCP客户端都可以注册 │ ║
║ │ • 创建OAuth客户端应用程序凭据 │ ║
║ │ │ ║
║ │ 请求正文: │ ║
║ │ { │ ║
║ │ "redirect_uris": ["https://example.com/callback"], │ ║
║ │ "client_name": "我的MCP客户端" │ ║
║ │ } │ ║
║ │ │ ║
║ │ 响应: │ ║
║ │ • client_id: "client_abc123..." ← OAuth客户端凭据 │ ║
║ │ • client_secret: "secret_xyz789..." ← 在/token端点使用 │ ║
║ │ • registration_access_token: "reg_tok..."← 仅用于客户端管理 │ ║
║ │ • registration_client_uri: "https://auth.../register/client_abc123" │ ║
║ └─────────────────────────────────────────────────────────────────────────────┘ ║
║ ║
║ 🔧 可选:客户端管理(需要registration_access_token) ║
║ ┌─────────────────────────────────────────────────────────────────────────────┐ ║
║ │ Authorization: Bearer <registration_access_token> │ ║
║ │ │ ║
║ │ • GET /register/{client_id} - 查看客户端配置 │ ║
║ │ • PUT /register/{client_id} - 更新重定向URI等 │ ║
║ │ • DELETE /register/{client_id} - 删除客户端注册 │ ║
║ │ │ ║
║ │ 注意:此令牌仅用于管理客户端注册, │ ║
║ │ 不用于访问MCP资源! │ ║
║ └─────────────────────────────────────────────────────────────────────────────┘ ║
║ ║
╚═══════════════════════════════════════════════════════════════════════════════════╝
↓
客户端拥有凭据,现在需要用户授权
↓
╔═══════════════════════════════════════════════════════════════════════════════════╗
║ 用户认证流程 (GitHub OAuth) ║
╠═══════════════════════════════════════════════════════════════════════════════════╣
║ ║
║ 👤 步骤2:用户授权(人类通过GitHub进行认证) ║
║ ┌─────────────────────────────────────────────────────────────────────────────┐ ║
║ │ GET /authorize?client_id=client_abc123&redirect_uri=...&code_challenge=... │ ║
║ │ │ ║
║ │ 1. 网关验证client_id存在 │ ║
║ │ 2. 将用户重定向到GitHub OAuth: │ ║
║ │ → 用户登录GitHub │ ║
║ │ → GitHub认证人类用户 │ ║
║ │ → 返回到网关/callback,带有GitHub用户信息 │ ║
║ │ 3. 网关检查ALLOWED_GITHUB_USERS白名单 │ ║
║ │ 4. 创建与以下内容绑定的授权码: │ ║
║ │ • OAuth客户端 (client_id) │ ║
║ │ • GitHub用户 (用户名、电子邮件等) │ ║
║ │ 5. 将授权码重定向回客户端 │ ║
║ └─────────────────────────────────────────────────────────────────────────────┘ ║
║ ║
║ 🎫 步骤3:令牌交换(客户端凭据 + 授权码) ║
║ ┌─────────────────────────────────────────────────────────────────────────────┐ ║
║ │ POST /token │ ║
║ │ Content-Type: application/x-www-form-urlencoded │ ║
║ │ │ ║
║ │ 请求: │ ║
║ │ • client_id=client_abc123 ← 验证OAuth客户端 │ ║
║ │ • client_secret=secret_xyz789 ← 证明客户端身份 │ ║
║ │ • code=auth_code_from_step_2 ← 包含GitHub用户信息 │ ║
║ │ • code_verifier=pkce_verifier ← PKCE验证 │ ║
║ │ │ ║
║ │ 响应: │ ║
║ │ • access_token: 包含以下内容的JWT: │ ║
║ │ - sub: GitHub用户ID │ ║
║ │ - username: GitHub用户名 │ ║
║ │ - email: GitHub电子邮件 │ ║
║ │ - client_id: client_abc123 │ ║
║ │ • refresh_token: 用于更新访问权限 │ ║
║ └─────────────────────────────────────────────────────────────────────────────┘ ║
║ ║
║ 🛡️ 步骤4:资源访问(使用访问令牌) ║
║ ┌─────────────────────────────────────────────────────────────────────────────┐ ║
║ │ Authorization: Bearer <access_token> │ ║
║ │ │ ║
║ │ • 令牌包含BOTH client_id AND用户身份 │ ║
║ │ • Traefik ForwardAuth通过/verify验证令牌 │ ║
║ │ • 用户身份作为头部传递给MCP服务: │ ║
║ │ - X-User-Id: GitHub