带Auth0身份验证的Apollo MCP服务器+谷歌ADK代理
该项目演示了如何集成 阿波罗MCP服务器, 身份验证0 OAuth2,以及 Google ADK(代理开发工具包) 创建一个经过身份验证的AI代理,可以访问外部API。
概述
该系统由三个主要部分组成:
- 阿波罗MCP服务器:提供由GraphQL API支持的MCP(模型上下文协议)工具,受Auth0身份验证保护
- Auth0 OAuth2服务器:使用OAuth 2.0流处理身份验证和授权
- 谷歌ADK代理:使用具有双OAuth身份验证流的MCP工具的AI代理
建筑
┌──────────────────────────────────────────────────────────────────┐
│ User / Developer │
└────────────┬─────────────────────────────────────────────────────┘
│
│ Chat / API Calls
│
┌────────────▼──────────────────────────────────────────────────────┐
│ Google ADK Agent │
│ (space_explorer) │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ LlmAgent (Gemini 2.0 Flash) │ │
│ │ • Natural language understanding │ │
│ │ • Tool selection and execution │ │
│ │ • Response generation │ │
│ └───────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────▼────────────────────────────────────┐ │
│ │ McpToolset │ │
│ │ • header_provider: Client credentials (M2M auth) │ │
│ │ • auth_scheme: Authorization code grant (user auth) │ │
│ │ • auth_credential: OAuth2 configuration │ │
│ └───────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────▼────────────────────────────────────┐ │
│ │ oauth_helper.py │ │
│ │ • Credential lifecycle management │ │
│ │ • Token caching (session state) │ │
│ │ • Automatic token refresh │ │
│ │ • Authorization code exchange │ │
│ └────────────────────────────────────────────────────────────┘ │
└───────────────────────┬──────────────────┬───────────────────────┘
│ │
MCP Protocol │ │ OAuth 2.0
(HTTP+JSON-RPC) │ │ (Authorization Code)
│ │
▼ ▼
┌───────────────────────┐ ┌──────────────────────┐
│ Apollo MCP Server │ │ Auth0 OAuth Server │
│ (Port 8000) │ │ │
│ │ │ Grant Types: │
│ Endpoints: │ │ • Client Credentials│
│ • /mcp (MCP tools) │ │ • Authorization Code│
│ • /.well-known/... │◄─┤ • Refresh Token │
│ (OAuth metadata) │ │ │
│ │ │ Endpoints: │
│ Features: │ │ • /authorize │
│ • Bearer token auth │ │ • /oauth/token │
│ • Tool discovery │ │ • /.well-known/... │
│ • Tool execution │ │ │
│ • GraphQL proxy │ └──────────────────────┘
└───────────┬───────────┘
│
│ GraphQL
│
▼
┌───────────────────────┐
│ The Space Devs API │
│ (thespacedevs.com) │
│ │
│ Data: │
│ • Rocket launches │
│ • Astronauts │
│ • Celestial bodies │
│ • Space missions │
└───────────────────────┘主要特点
双OAuth 2.0身份验证流程
此实现使用 两种不同的OAuth流程 用于不同目的:
1.客户端凭证流(M2M)
- 目的:初始MCP服务器连接以发现可用工具
- 当:代理初始化(
listToolsMCP方法) - 为什么:工具发现不需要用户交互
- 实施:
get_mcp_headers()功能在agent.py
2.授权码授权流程
- 目的:用户身份验证工具执行
- 当:第一次实际调用工具
- 为什么:工具可能会访问用户特定的数据或需要用户同意
- 实施:
oauth_helper.py使用ADK的凭证管理 - 特性:
- 用户同意屏幕(仅限首次使用) - 会话状态下的令牌缓存 - 自动令牌刷新 offline_access 范围 - 安全授权码交换
自动令牌管理
ADK代理处理完整的OAuth2令牌生命周期:
- 代币交换:将授权码转换为访问+刷新令牌
- 令牌缓存:使用以下命令将令牌存储在会话状态中
credential_cache_key - 令牌刷新:使用刷新令牌自动刷新过期令牌
- 错误处理:如果刷新失败,则重新启动身份验证流
ADK如何确定身份验证流
ADK使用 header_provider 和 auth_scheme 为了 不同的目的 在 不同阶段:
当 header_provider 被使用
目的:MCP协议级通信(服务器连接)
触发器:
initialize-初始MCP握手listTools-发现可用工具callTool-执行工具- 任何MCP协议方法
这 header_provider 是 总是被召唤 对于MCP服务器的每个HTTP请求。
def get_mcp_headers(context: ReadonlyContext) -> dict[str, str]:
# Uses client credentials to get token
# Called for every MCP server request
return {"Authorization": f"Bearer {token}"}当 auth_scheme/auth_credential 被使用
目的:工具级用户身份验证
触发器:
- 仅当执行工具时 (
callTool) - 只有
auth_scheme已配置 - ADK检查是否存在有效的缓存凭据
流动:
- 用户请求调用工具
- ADK检查:“此McpToolset是否配置了auth_scheme?”
- 如果是:“我们是否有有效的缓存凭据?”
- 如果没有缓存凭据:启动OAuth流(显示同意屏幕)
- 获取凭据后:使用用户令牌执行工具
ADK决策树
MCP Server Request (initialize, listTools):
├─ ADK calls header_provider()
└─ Uses returned headers for HTTP request
Tool Execution (callTool):
├─ ADK calls header_provider() for MCP communication
└─ IF auth_scheme is configured:
├─ Check for cached credentials (via oauth_helper)
├─ If no valid credentials:
│ └─ Initiate OAuth flow (authorization code grant)
└─ Include user token in tool execution context实践中的身份验证阶段
第一阶段:代理初始化
User runs: adk web space_agent
↓
ADK → listTools (MCP method)
├─ header_provider() called
│ └─ Returns: Client Credentials Token
└─ MCP server authenticates request
└─ Returns: Available tools list第二阶段:首次工具调用
User asks: "What rocket launches are happening soon?"
↓
ADK → callTool (MCP method)
├─ header_provider() called
│ └─ Returns: Client Credentials Token (for MCP protocol)
│
└─ auth_scheme configured?
└─ Yes → oauth_helper.get_user_credentials()
└─ No cached token found
└─ Return AuthConfig
└─ ADK shows Auth0 consent screen
└─ User authorizes
└─ Authorization Code Grant flow
└─ Cache user token
└─ Execute tool with user token第三阶段:后续工具调用
User asks: "Who is currently in space?"
↓
ADK → callTool (MCP method)
├─ header_provider() called
│ └─ Returns: Client Credentials Token (for MCP protocol)
│
└─ auth_scheme configured?
└─ Yes → oauth_helper.get_user_credentials()
└─ Cached token found ✓
└─ Execute tool with cached user token
└─ No consent screen needed第四阶段:令牌刷新
User asks: "More launches please"
↓
ADK → callTool (MCP method)
├─ header_provider() called
│ └─ Returns: Client Credentials Token (for MCP protocol)
│
└─ auth_scheme configured?
└─ Yes → oauth_helper.get_user_credentials()
└─ Cached token expired ✗
└─ Refresh token found ✓
└─ OAuth2CredentialRefresher.refresh()
└─ Get new access + refresh tokens
└─ Update cache
└─ Execute tool with new user token为什么两种身份验证方法?
我们的双重身份验证方法满足不同的需求:
header_provider:MCP服务器本身需要Auth0身份验证才能连接auth_scheme:工具可能需要用户特定的身份验证(不同于服务器身份验证)
其他可能的配置:
- 仅
header_provider:如果MCP服务器需要身份验证,但工具不需要用户同意 - 仅
auth_scheme:如果MCP服务器是公共的,但工具需要用户身份验证 - 两者都不如果一切都是公开的
关键见解: header_provider 用于 传输级身份验证, auth_scheme 用于 工具级身份验证.ADK始终使用 header_provider 用于MCP通信,但仅参与 auth_scheme 当工具被实际调用时。
重要提示:页眉合并行为
ADK实际上是如何工作的 (基于源代码分析):
两者 auth_scheme 和 header_provider 转换为HTTP标头 融为一体:
auth_scheme凭证→ HTTP报头(例如。,Authorization: Bearer)header_provider()→ 其他HTTP标头- 两个集合合并:
headers.update(auth_headers)然后headers.update(dynamic_headers) - 如果发生碰撞,
header_provider胜利 (最后申请)
在我们的实施中:
两者都试图设置 Authorization 头球
auth_scheme→Authorization: Bearerheader_provider→Authorization: Bearer
结果: MCP服务器接收 客户端凭据令牌 (从 header_provider),而不是用户令牌。用户令牌由OAuth流生成,但在到达MCP服务器之前会被覆盖。
为什么这仍然有效:
我们的MCP服务器仅验证是否存在有效的Auth0令牌。它不需要用户特定的权限,因此客户端凭据令牌就足够了。
对于用户级身份验证:
如果您需要MCP服务器接收用户令牌(例如,用于用户级授权):
✅ 已实施的解决方案:
我们的 get_mcp_headers() 函数现在检查用户凭据并进行调整:
def get_mcp_headers(context: ReadonlyContext) -> dict[str, str]:
# Check if user credentials are cached in session
user_credential = context._invocation_context.session_state.get(CREDENTIAL_CACHE_KEY)
if user_credential and user_credential.oauth2.access_token:
# User is authenticated - don't set Authorization header
# Let auth_scheme provide the user token
return {"Content-Type": "application/json"}
# No user credentials - use client credentials for initial connection
return {
"Authorization": f"Bearer {get_client_credentials_token()}",
"Content-Type": "application/json",
}它是如何工作的:
- 第一个请求(listTools):没有用户凭据→ 使用的客户端凭据
- 用户登录后:缓存用户凭据→
header_provider跳过授权标头 - ADK的认证方案:提供
Authorization: Bearer无碰撞 - MCP服务器接收:用于正确授权的用户特定令牌
备选方案:
- 选项2:使用
header_provider仅适用于非授权标头(例如,自定义标头、API密钥) - 选项3:删除
header_provider如果MCP服务器不需要身份验证即可发现
看 ADK_认证_内部.md 以获取源代码参考的详细分析。
项目结构
apollo-mcp-auth/
├── .env # Environment configuration (DO NOT COMMIT)
├── .env.example # Example environment variables template
├── .gitignore # Git ignore patterns
├── README.md # This file
├── ADK_AUTHENTICATION_INTERNALS.md # Deep dive into ADK auth mechanisms
│
├── space_agent/ # Google ADK Agent
│ ├── __init__.py # Package initialization
│ ├── agent.py # Main agent with dual OAuth flows
│ ├── oauth_helper.py # Credential lifecycle management
│ ├── requirements.txt # Python dependencies
│ └── README.md # Detailed agent documentation
│
├── test_mcp_auth.py # Standalone Auth0 + MCP test script
└── requirements.txt # Root-level Python dependencies入门指南
先决条件
- Python 3.10+ (MCP SDK需要)
- Auth0帐户 已配置常规Web应用程序
- Google Gemini API密钥 或谷歌云项目
- 阿波罗MCP服务器 在端口8000上运行
快速开始
- 克隆存储库:
git clone git@github.com:prashantkul/apollo-mcp-oauth.git
cd apollo-mcp-oauth- 配置环境:
cp .env.example .env
# Edit .env with your Auth0 and Google credentials- 安装依赖项:
cd space_agent
pip install -r requirements.txt- 启动代理:
adk web space_agent- 与代理交互:
- 打开ADK web UI(通常http://localhost:3000) - 问一些问题,比如“什么火箭即将发射?” - 第一次工具调用将触发Auth0同意屏幕 - 后续调用使用缓存的凭据
Auth0配置
应用程序设置
- 应用类型:常规Web应用程序
- 资助类型:
- ✅ 授权码 - ✅ 刷新令牌 - ✅ 客户端凭证
- 允许的回调URL:
http://127.0.0.1:8081/dev-ui/ - 允许的Web来源:
http://127.0.0.1:8081
API配置
- 标识符:
http://127.0.0.1:8000/mcp - 范围:
read:users(API的自定义范围) - 授权申请:您的常规Web应用程序
标准OIDC范围
代理请求这些作用域(自动可用):
openid-OpenID连接身份验证profile-用户资料信息email-用户电子邮件地址offline_access-刷新令牌以续订令牌
身份验证流程详解
第一阶段:代理初始化
1. User runs: adk web space_agent
2. Agent calls get_mcp_headers()
3. get_mcp_headers() requests token from Auth0 (client credentials)
4. Auth0 returns access token
5. Agent calls listTools on MCP server with Bearer token
6. MCP server validates token and returns available tools
7. Agent is ready, web UI displays第二阶段:首次工具调用
1. User asks: "What rocket launches are happening soon?"
2. Agent selects: space_SearchUpcomingLaunches tool
3. ADK calls oauth_helper.get_user_credentials()
4. No cached credentials found, returns AuthConfig
5. ADK opens Auth0 consent screen in browser
6. User logs in and accepts permissions
7. Auth0 redirects to http://127.0.0.1:8081/dev-ui/?code=...
8. ADK exchanges code for access + refresh tokens
9. ADK caches tokens in session state
10. Agent executes tool with user's access token
11. MCP server validates token and executes GraphQL query
12. Agent returns results to user第三阶段:后续通话
1. User asks: "Who is currently in space?"
2. Agent selects: space_GetAstronauts tool
3. ADK checks session cache - valid token found
4. Agent executes tool with cached token
5. No authentication prompt needed
6. Results returned immediately阶段4:令牌刷新
1. Access token expires (after ~1 hour typically)
2. ADK detects expiration
3. ADK uses refresh token to get new access token
4. Auth0 returns new access + refresh tokens
5. ADK updates cache with new tokens
6. Tool execution continues seamlessly
7. No user interaction needed文档
- 太空特工:参见 space_agent/README.md 获取详细的代理文档
- 架构图:代理README中的组件和序列图
- Auth0设置:代理README中的配置说明
使用的技术
- 谷歌ADK:用于构建AI代理的代理开发工具包
- 阿波罗路由器:支持MCP的GraphQL路由器
- 身份验证0:身份和访问管理平台
- 模型上下文协议(MCP):AI工具通信的标准协议
- 空间开发API:用于空间探索数据的公共API
- 双子座2.0闪光灯:谷歌的大型语言模型
实现模式
实施的关键模式:
- 基于环境的配置
- 关注点分离(oauth_helper.py)
- 凭证生命周期管理
- 基于会话的令牌缓存
- 自动令牌刷新
故障排除
看 space_agent/README.md 有关详细的故障排除指南。
贡献
这是一个示范项目。请随意分叉并适应您自己的用例。
许可证
麻省理工学院
致谢
- 谷歌ADK团队 用于代理开发工具包
- 阿波罗GraphQL 阿波罗路由器中的MCP支持
- 身份验证0 用于OAuth2身份验证平台
- 太空开发人员 用于空间数据API
