在构建基于 MCP (Model Context Protocol) 架构的企业级 LLM 应用时,一个核心挑战是如何确保授权员工才能访问特定的内部工具与自动化任务。简单的 API Key 机制难以满足复杂的权限控制需求,尤其当企业需要集成现有的单点登录(SSO)等安全体系时。
本文将从 OAuth 2.0 的基础概念入手,通过一个完整的 Python 示例,演示如何利用 MCP SDK 与第三方身份提供商(以 Google 为例),为您的 MCP Server 构建强大而灵活的 OAuth 2.0 安全认证体系。
OAuth 2.0 核心概念
OAuth 2.0 是一套开放的授权标准协议,它允许第三方应用在不获取用户密码的前提下,安全地访问用户在某一服务上受保护的资源。
关键角色
理解 OAuth 2.0 需要了解以下四个核心角色:
- 资源拥有者 (Resource Owner):通常指终端用户,是受保护资源的所有者。
- 客户端 (Client):希望访问受保护资源的第三方应用程序,例如一个需要获取您 Google 账户信息的 Web 应用。
- 授权服务器 (Authorization Server):负责验证用户身份,并在用户授权后,向客户端发放访问令牌(Token)的服务器。
- 资源服务器 (Resource Server):存储受保护资源的服务器。它会验证客户端出示的访问令牌,并根据令牌的权限提供相应的资源。
我们可以用一个银行保险柜的例子来类比:您是保险柜的主人(资源拥有者),一位朋友(客户端)需要临时取用您的资料。您不会直接把钥匙(密码)给他,而是到银行前台(授权服务器)登记,签发一张有时效的临时访问凭证(访问令牌)。保险柜管理员(资源服务器)只认这张凭证,凭证过期后自动作废。
授权码模式 (Authorization Code Flow)
授权码模式是 OAuth 2.0 中功能最完整、流程最严谨的授权模式,常见于各类 Web 应用。其典型流程如下:
- 用户授权:用户在应用中点击“使用 Google 登录”。应用将浏览器重定向到 Google 的授权页面,并在 URL 中附带自身的客户端 ID、请求的权限范围 (scope) 和回调地址 (redirect_uri)。
- 用户登录并同意:用户在 Google 页面登录,并确认是否同意应用请求的权限。
- 返回授权码:用户同意后,Google 授权服务器将浏览器重定向回应用指定的回调地址,并在 URL 中附上一个一次性的授权码 (code)。
- 交换访问令牌:应用的后端服务收到授权码后,带上自身的客户端 ID 和密钥,向 Google 授权服务器发起请求,用授权码换取访问令牌 (Access Token)。此过程对用户不可见。
- 访问资源:应用使用获取到的访问令牌,向 Google 的资源服务器(API)请求访问用户授权的资源。
为了进一步增强安全性,OAuth 2.1 规范要求所有客户端在授权流程中使用 PKCE (Proof Key for Code Exchange) 机制,以防止授权码被恶意拦截和利用。
MCP 对 OAuth 2.0 的支持架构
新版 MCP 协议引入了标准化的身份验证与授权框架,使 AI 应用能够无缝对接现有的身份提供商和安全机制。
在 MCP 架构中,各个角色与 OAuth 2.0 的对应关系如下:
- 资源服务器 -> MCP Server
- 客户端应用 -> MCP Client (例如带有 UI 的聊天机器人)
MCP 规范定义的标准授权流程与标准流程略有不同:
- MCP 客户端首先向 MCP Server 发起授权请求。
- MCP Server 负责将客户端重定向到第三方的授权服务器(如 Google 或企业 SSO)。
- 用户在第三方授权服务器完成认证和授权。
- 第三方授权服务器将带有授权码的请求重定向回 MCP Server。
- MCP Server 使用授权码从第三方换取访问令牌,并与客户端信息绑定。
- MCP Server 生成一个自己的内部授权码,并将其返回给 MCP 客户端。
- MCP 客户端使用这个内部授权码,向 MCP Server 交换最终的 MCP 访问令牌。
- 客户端使用此令牌安全地访问 MCP Server 提供的资源。
此流程的核心区别在于 MCP Server 充当了客户端与第三方授权服务器之间的中间人,它不直接暴露第三方令牌给客户端,而是生成并管理自己的令牌体系,从而实现了更强的安全隔离与控制。
实战:在 MCP 中集成 Google OAuth 2.0
接下来,我们将使用 Python 和 MCP Python SDK 构建一个完整的示例。我们将本地运行一个 MCP Server,并将其配置为使用 Google 的 OAuth 2.0 服务进行用户认证。
环境准备与配置
-
安装 MCP Python SDK:确保已安装最新版本的官方 SDK。
-
注册 Google OAuth 应用:
- 访问 Google API 控制台,创建一个新的 OAuth 客户端 ID 凭证。
- 应用类型选择“Web 应用”。
- 将重定向 URI 设置为 MCP Server 的回调地址,例如 `
- 获取生成的客户端 ID 和客户端密钥。
-
配置服务端:将获取的凭证和相关 URL 配置为环境变量,或写入配置文件。我们可以使用
pydantic-settings
来管理配置。
from pydantic import AnyHttpUrl
from pydantic_settings import BaseSettings, SettingsConfigDict
class ServerSettings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="MCP_")
# MCP Server 配置
host: str = "localhost"
port: int = 3000
server_url: AnyHttpUrl = AnyHttpUrl("http://localhost:3000")
callback_path: str = "http://localhost:3000/callback"
# Google OAuth 配置
client_id: str # 从环境变量 MCP_CLIENT_ID 读取
client_secret: str # 从环境变量 MCP_CLIENT_SECRET 读取
auth_url: str = "https://accounts.google.com/o/oauth2/auth"
token_url: str = "https://oauth2.googleapis.com/token"
scope: str = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile openid"
settings = ServerSettings()
服务端实现 (MCP Server)
实现 OAuth 认证的核心是实现 OAuthAuthorizationServerProvider
接口。该接口定义了服务端在 OAuth 流程中需要执行的所有操作,框架会在授权流程的不同阶段自动调用这些方法。
我们创建一个 GoogleOAuthProvider
类来实现此接口,并使用字典来模拟数据库存储客户端信息、授权码和令牌。
from typing import Dict, Optional
# ... 其他 mcp_server 导入 ...
class GoogleOAuthProvider(OAuthAuthorizationServerProvider):
def __init__(self, settings: ServerSettings):
self.settings = settings
# 用于存储动态注册的客户端信息
self.clients: Dict[str, OAuthClientInformationFull] = {}
# 用于存储生成的 MCP 内部授权码
self.auth_codes: Dict[str, AuthorizationCode] = {}
# 用于存储从 Google 获取的令牌信息
self.tokens: Dict[str, Dict] = {}
# 用于存储 state,防止 CSRF 攻击
self.state_mapping: Dict[str, Dict[str, str]] = {}
接下来,我们分步实现接口中的关键方法。
1. 客户端管理接口
这两个方法用于处理 MCP 客户端的动态注册。
async def register_client(self, client_info: OAuthClientInformationFull):
# 将 SDK 自动生成的客户端信息保存起来
self.clients[client_info.client_id] = client_info
async def get_client(self, client_id: str) -> Optional[OAuthClientInformationFull]:
# 根据 client_id 获取客户端信息
return self.clients.get(client_id)
2. 授权流程接口
这两个方法是授权流程的核心。
authorize
: 处理 MCP 客户端的授权请求,生成并返回 Google 的授权 URL。
import secrets
import urllib.parse
async def authorize(self, client: OAuthClientInformationFull, params: AuthorizationParams) -> str:
# 生成 state 参数用于后续验证
state = secrets.token_hex(16)
self.state_mapping[state] = {
"redirect_uri": str(params.redirect_uri),
"client_id": client.client_id,
"code_challenge": getattr(params, 'code_challenge', None),
}
# 构建 Google 的授权 URL
auth_params = {
"client_id": self.settings.client_id,
"redirect_uri": self.settings.callback_path,
"response_type": "code",
"scope": self.settings.scope,
"state": state,
"access_type": "online",
}
auth_url = f"{self.settings.auth_url}?{urllib.parse.urlencode(auth_params)}"
return auth_url
handle_callback
: 处理 Google 授权成功后的回调,生成 MCP 内部授权码并重定向回客户端。
import time
from fastapi import HTTPException
async def handle_callback(self, code: str, state: str) -> str:
state_data = self.state_mapping.get(state)
if not state_data:
raise HTTPException(status_code=400, detail="Invalid state parameter")
# 使用从 Google 获取的 code 交换 Google 的 access_token
token_response = await create_mcp_http_client().post(
self.settings.token_url,
data={
"client_id": self.settings.client_id,
"client_secret": self.settings.client_secret,
"code": code,
"redirect_uri": self.settings.callback_path,
"grant_type": "authorization_code",
},
headers={"Content-Type": "application/x-www-form-urlencoded"},
)
token_response.raise_for_status()
google_data = token_response.json()
# 生成 MCP 内部授权码
mcp_code = f"mcp_{secrets.token_hex(16)}"
self.auth_codes[mcp_code] = AuthorizationCode(
code=mcp_code,
client_id=state_data["client_id"],
redirect_uri=state_data["redirect_uri"],
# ... 其他参数 ...
)
# 存储 Google 令牌信息,用于后续交换
self.tokens[mcp_code] = google_data
del self.state_mapping[state]
# 构建并返回给 MCP 客户端的回调 URI,附上 MCP 内部授权码
return construct_redirect_uri(state_data["redirect_uri"], code=mcp_code, state=state)
3. 令牌交换与验证接口
exchange_authorization_code
: 客户端使用 MCP 内部授权码来交换最终的访问令牌。
async def exchange_authorization_code(
self, client: OAuthClientInformationFull, authorization_code: AuthorizationCode
) -> AccessToken:
# 取出之前存储的 Google 令牌信息
google_data = self.tokens.pop(authorization_code.code)
# 注意:这里为了简化,直接使用了 Google 的 access_token。
# 严格实现中,应在此处生成 MCP 服务器自己的 JWT 令牌。
access_token = google_data["access_token"]
# 存储令牌信息,用于后续验证
self.tokens[access_token] = {
"client_id": client.client_id,
"expires_at": time.time() + google_data.get("expires_in", 3600),
}
return AccessToken(
token=access_token,
# ... 其他参数 ...
)
load_access_token
: 在每次 API 请求时验证令牌的有效性,这是保障资源安全的关键。
async def load_access_token(self, token: str) -> Optional[AccessToken]:
token_data = self.tokens.get(token)
if not token_data:
return None
# 检查令牌是否过期
if token_data.get("expires_at", 0) < time.time():
del self.tokens[token]
return None
return AccessToken(
token=token,
client_id=token_data["client_id"],
scopes=self.settings.scope.split(),
expires_at=int(token_data.get("expires_at", 0)),
)
创建与启动 FastMCP Server
实现了 Provider
后,我们就可以创建并启动 FastMCP
服务实例。
from mcp_server.fast_mcp import FastMCP
from fastapi import Request, Response
from fastapi.responses import RedirectResponse
# 实例化 Provider 和 AuthSettings
oauth_provider = GoogleOAuthProvider(settings)
auth_settings = AuthSettings(
issuer_url=settings.server_url,
client_registration_options=ClientRegistrationOptions(enabled=True),
required_scopes=["openid"],
)
# 创建 FastMCP 应用
app = FastMCP(
name="Google OAuth MCP Server",
auth_server_provider=oauth_provider,
host=settings.host,
port=settings.port,
auth=auth_settings,
)
# 添加处理 Google 回调的路由
@app.custom_route("/callback", methods=["GET"])
async def callback_handler(request: Request) -> Response:
code = request.query_params.get("code")
state = request.query_params.get("state")
if not code or not state:
raise HTTPException(status_code=400, detail="Missing code or state")
redirect_uri = await oauth_provider.handle_callback(code, state)
return RedirectResponse(url=redirect_uri)
# 启动服务
if __name__ == "__main__":
app.run()
客户端实现与测试
MCP SDK 提供了一个通用的 OAuth 客户端,我们可以直接使用它来测试。
- 运行客户端:启动命令行客户端。
- 浏览器授权:客户端会自动打开浏览器,将您重定向到 Google 登录页面。输入您的 Google 账户信息并同意授权。
- 授权成功:授权成功后,浏览器会显示一个成功页面。此时,客户端已获取到访问令牌,可以安全地调用 MCP Server 的接口,例如查看工具列表。
此外,您还可以使用 MCP Inspector 工具进行调试。在 Inspector 的 Web 界面中配置好服务器 URL,使用其内置的 OAuth 工具,可以分步执行授权流程,方便观察每一步的请求与响应。
注意:本示例为了简化,直接将 Google 颁发的
access_token
作为访问 MCP 资源的凭证。在生产环境中,更安全的做法是 MCP Server 在换取到第三方令牌后,签发自己的、与应用业务逻辑相关的 JWT 令牌。
结合其他安全方案
上述方案非常适合有 UI 界面的应用场景,尤其是需要与企业现有身份系统集成时。但在某些场景下,例如 MCP 客户端是无界面的后台 Agent 或其他后端服务时,无法通过浏览器完成授权码流程。此时可以考虑其他方案:
- API Key 模式:对于简单的内部服务调用,可以使用传统的 API Key 认证。
- OAuth 客户端凭证模式 (Client Credentials Flow):适用于后端服务间的安全通信。客户端直接使用自身的 ID 和密钥向授权服务器请求令牌,无需用户参与。
最终选择何种安全方案,需要综合考虑应用架构、安全需求和用户体验,灵活运用不同的技术手段。
👉 如果你需要 ChatGPT 代充 / Claude / Claude Code / 镜像 / 中转 API:
- 购买 / 了解更多:ai4.plus
- 备用入口:kk4099.com