返回市场
MCP授权网关

MCP授权网关

作者:atrawog44 星标更新:2025-08-16

项目介绍

MCP OAuth Gateway

一个OAuth 2.1授权服务器,可以为任何MCP(模型上下文协议)服务器添加身份验证,而无需修改代码。网关作为OAuth授权服务器运行,并使用GitHub作为用户身份验证的身份提供商(IdP)。

📖 查看文档 | 🔧 安装指南 | 🏗️ 架构概述

⚠️ 重要通知

这是一个MCP协议的参考实现和测试平台。

  • 主要目的:MCP协议开发和测试的参考实现
  • 实验性质:用作未来MCP协议迭代的测试平台
  • 安全免责声明:虽然此实现力求遵循最佳安全实践,但可能仍存在漏洞和安全风险
  • 生产警告:不建议在未经彻底安全审查的情况下用于生产环境
  • 自行承担风险:这是一个实验性软件,仅适用于开发和测试

🏗️ 架构

概述

MCP OAuth网关是一个零修改认证层,适用于MCP服务器。它实现了OAuth 2.1,包括动态客户端注册(RFC 7591/7592),并利用GitHub作为用户身份验证的身份提供商。架构遵循以下核心原则:

  • 完全职责分离:认证、路由和MCP协议处理严格隔离
  • 无MCP服务器修改:官方MCP服务器未修改,仅用于HTTP传输
  • 标准合规性:全面遵守OAuth 2.1、RFC 7591/7592和MCP协议
  • 生产就绪安全性:HTTPS处处可用,强制执行PKCE,JWT令牌,安全会话管理
  • 动态服务发现:通过配置启用或禁用服务

系统组件

┌─────────────────────────────────────────────────────────────────────────────────────┐
│                                   外部客户端                                      │
│         (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消息队列                                  │
└─────────────────────────────────────────────────────────────────────────────────────┘

网络拓扑:

  • 所有服务通过“公共”Docker网络连接
  • 仅内部服务通信(除了Traefik入口)
  • Redis仅在本地主机6379端口暴露用于调试
  • 每个MCP服务都在隔离容器中运行,没有共享状态

安全架构

认证层

  1. TLS/HTTPS:由Traefik强制执行所有外部通信
  2. OAuth客户端认证:在令牌端点使用client_id + client_secret
  3. 用户认证:GitHub OAuth,带有ALLOWED_GITHUB_USERS白名单
  4. 令牌认证:JWT承载令牌用于API访问
  5. PKCE保护:强制执行S256代码挑战

安全边界

  • 公开访问:仅限/register和/.well-known/*端点
  • 客户端认证:/token端点需要客户端凭证
  • 用户认证:/authorize需要GitHub登录
  • 承载认证:所有/mcp端点需要有效的JWT
  • 注册令牌认证:客户端管理端点(RFC 7592)

令牌类型和范围

  • registration_access_token:仅用于客户端管理的承载令牌(RFC 7592)
  • access_token:包含用户身份和client_id的JWT,用于MCP访问
  • refresh_token:用于获取新访问令牌的不透明令牌
  • authorization_code:一次性代码绑定用户到客户端

架构决策

为什么三层?

  1. Traefik(路由):集中TLS、路由和认证执行
  2. 认证服务(OAuth):隔离的OAuth实现,无MCP知识
  3. MCP服务(协议):纯MCP协议处理器,无认证知识

为什么使用mcp-streamablehttp-proxy?

  • 包装未修改的基于stdio的MCP服务器
  • 提供Web客户端所需的HTTP传输
  • 管理子进程生命周期和会话状态
  • 使水平扩展成为可能

为什么使用Redis?

  • 快速、可靠的OAuth流状态存储
  • 支持原子操作以确保安全
  • 内置TTL以自动清理
  • 如果需要,支持分布式部署

为什么使用GitHub OAuth?

  • 开发者的可信身份提供商
  • 不需要密码管理
  • 强大的安全性和2FA支持
  • 丰富的用户资料信息

OAuth架构:客户端凭据 + 用户认证

网关实现了一个多层OAuth 2.1授权系统,结合了客户端凭据认证GitHub OAuth身份联合

网关实现了一个复杂的OAuth 2.1系统,具有三个不同的认证流程

  1. GitHub设备流(RFC 8628) - 用于命令行/无浏览器场景
  2. GitHub OAuth Web流 - 用于基于浏览器的最终用户认证
  3. 动态客户端注册(RFC 7591) - 用于MCP客户端注册

认证流程决策树

需要认证吗?
├─> 对于网关自身的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