关于 OpenMetadata
OpenMetadata 的 MCP 模块为通过 MCP 访问元数据提供 OAuth 2.0 身份验证,面向使用 OpenMetadata 的组织。它通过 SSO 或 Basic Auth 保留用户身份与权限。
主要功能
- 01支持带 PKCE 的授权码流程
- 02支持 SSO 和 Basic Auth
- 03刷新令牌轮换与加密存储
- 04动态客户端注册和发现端点
- 05基于数据库的运行时配置
适用场景
- 01对 OpenMetadata 的 MCP 客户端进行身份验证
- 02将 OpenMetadata 用户权限应用于工具请求
- 03跨集群实例更新 MCP 设置
MCP 相关
通过 MCP 工具向客户端提供 OpenMetadata 元数据能力,包括血缘查询、元数据搜索和实体发现。OAuth 用于验证用户身份,OpenMetadata 授权策略负责控制工具访问权限。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://{openmetadata_host}/mcpclaude mcp add --transport http open-metadata-openmetadata https://{openmetadata_host}/mcp{"mcpServers":{"open-metadata-openmetadata":{"type":"http","url":"https://{openmetadata_host}/mcp"}}}在 Cursor 中打开{"servers":{"open-metadata-openmetadata":{"type":"http","url":"https://{openmetadata_host}/mcp"}}}在 VS Code 中打开{"mcpServers":{"open-metadata-openmetadata":{"type":"http","url":"https://{openmetadata_host}/mcp"}}}{"mcpServers":{"open-metadata-openmetadata":{"serverUrl":"https://{openmetadata_host}/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
详细介绍
OpenMetadata MCP OAuth 实现
用于 Model Context Protocol(MCP)与 OpenMetadata 集成的 OAuth 2.0 身份验证服务,可通过 Claude Desktop 和其他 MCP 客户端安全访问元数据。
概述
本模块为 MCP 客户端实现了完整的 OAuth 2.0 授权码流程和 PKCE,支持通过 OpenMetadata 现有的 SSO 提供方(Google、Okta、Azure AD、Auth0、AWS Cognito、自定义 OIDC、LDAP、SAML)或 Basic Auth 进行用户身份验证。此实现通过 MCP 工具安全且符合标准地访问 OpenMetadata 的元数据管理功能。
重要提示:这是面向 MCP 客户端的用户 SSO 身份验证,并非面向数据源的基于连接器的 OAuth。用户使用其 OpenMetadata 凭据(SSO 或用户名/密码)进行身份验证,MCP 工具则以该用户的权限执行操作。
功能
OAuth 2.0 实现
- 带 PKCE 的授权码流程——符合 RFC 7636,可防止授权码拦截攻击
- 刷新令牌轮换——自动刷新令牌并进行轮换,增强安全性
- 令牌加密——使用 Fernet 对称加密保护静态令牌
- CSRF 防护——在整个 OAuth 流程中验证 state 参数
- 防止会话固定攻击——身份验证成功后重新生成会话
身份验证方式
- SSO 集成——通过 pac4j 与 Google、Okta、Azure AD、Auth0、AWS Cognito、自定义 OIDC、LDAP 和 SAML 提供方集成 OAuth 2.0
- Basic Auth——使用 OpenMetadata 凭据进行用户名/密码身份验证
- 用户模拟——支持 MCP 工具中的模拟用户上下文
- 自动检测——根据 OpenMetadata 配置自动选择 SSO 或 Basic Auth
安全功能
- PKCE 验证——验证 SHA-256 code challenge/verifier
- 令牌过期管理——访问令牌自登录起有效期为 10 分钟;刷新令牌自登录起有效期为 30 天,轮换不会延长有效期
- 速率限制——保护注册端点(每个 IP 每小时 10 次)和令牌端点(每个 IP 每分钟 30 次)
- 线程安全的并发处理——使用 ThreadLocal 隔离请求
- 审计日志——通过 SLF4J 记录 OAuth 操作(提供 oauth_audit_log 表供将来使用)
数据库驱动的配置
- 运行时配置——通过 REST API 更新 MCP 设置,无需重启服务
- 集群同步——通过数据库轮询(间隔为 10 秒)确保所有集群实例的配置一致
- 配置变更监听器——配置变更时动态更新 CORS 来源
- 持久化存储——配置变更会在服务重启后保留(优先使用数据库,YAML 作为后备)
- HTTP 超时配置——可配置获取 SSO 提供方元数据时的连接和读取超时
MCP 集成
- 支持 Claude Desktop——与 Claude Desktop MCP 客户端深度集成
- OAuth 发现端点——提供标准 .well-known 端点供客户端配置
- 动态客户端注册——符合 RFC 7591 的客户端注册
- JWKS 支持——用于 JWT 验证的公钥端点
架构
核心组件
UserSSOOAuthProvider
- 实现授权码流程的主要 OAuth 提供方
- 处理 Google SSO 和 Basic Auth 流程
- 令牌生成、验证和刷新逻辑
- 使用时序安全比较验证 PKCE challenge/verifier
OAuthHttpStatelessServerTransportProvider
- OAuth 端点的 HTTP 传输层
- 路由授权、令牌和发现请求
- 基于 Servlet 的无状态请求处理
- 感知提供方的 OAuth scope 配置
SecurityConfigurationManager
- 用于运行时安全配置(身份验证、授权、MCP 设置)的单例管理器
- 优先从数据库加载配置,YAML 作为回退
- 配置变更监听器模式,用于响应式更新
- 感知集群的轮询机制(间隔为 10 秒),用于检测各实例间的变更
- 配置更新失败时的回滚机制
- 线程安全的同步 getter,确保一致地读取配置
OAuth Repositories
- OAuthClientRepository - 客户端管理和验证
- OAuthAuthorizationCodeRepository - 授权码 CRUD 操作
- OAuthAccessTokenRepository - 访问令牌生命周期管理
- OAuthRefreshTokenRepository - 刷新令牌轮换和清理
- McpPendingAuthRequestRepository - 由数据库支持的 OAuth 状态持久化
- OAuthAuditLogRepository - 全面的审计跟踪
数据库架构
包含审计日志的五个核心 OAuth 表:
- oauth_clients - 通过 RFC 7591 动态注册的 MCP 客户端
- oauth_authorization_codes - 短时有效的授权码(TTL 为 10 分钟),包含 PKCE challenge
- oauth_access_tokens - 目前不会写入:访问令牌是返回给客户端的 10 分钟有效 JWT
- oauth_refresh_tokens - 刷新令牌(自登录起有效 30 天),并自动轮换以维持该有效期
- mcp_pending_auth_requests - 用于跨域重定向的 OAuth 状态参数(TTL 为 10 分钟)
- oauth_audit_log - 所有 OAuth 操作的全面审计跟踪
清理任务:OAuthTokenCleanupJob 每 10 分钟运行一次,清除过期令牌和待处理请求。
OAuth 流程
使用 PKCE 的授权码流程
┌─────────────┐ ┌──────────────┐
│ Claude │ │ OpenMetadata │
│ Desktop │ │ MCP │
│ (MCP Client)│ │ Server │
└──────┬──────┘ └──────┬───────┘
│ │
│ 1. Generate PKCE code_verifier (random 43-128 chars) │
│ Calculate code_challenge = BASE64URL(SHA256(verifier)) │
│ │
│ 2. GET /api/v1/mcp/authorize │
│ ?client_id={registered_client_id} │
│ &redirect_uri=http://127.0.0.1:XXXXX/callback │
│ &code_challenge={challenge} │
│ &code_challenge_method=S256 │
│ &state={client_state} │
│─────────────────────────────────────────────────────────────────>│
│ │
│ 3. Store OAuth state in database: │
│ - client_id, redirect_uri │
│ - code_challenge, method │
│ - state, scopes, TTL (10 min) │
│ Generate authRequestId │
│ │
│ 4. 302 Redirect to Auth Page │
│ /api/v1/mcp/authorize?state=mcp:{authRequestId} │
│<─────────────────────────────────────────────────────────────────│
│ │
│ 5. User authenticates via: │
│ ┌──────────────────────────────────────┐ │
│ │ Option A: SSO Provider │ │
│ │ - Redirect to SSO provider: │ │
│ │ • Google OAuth │ │
│ │ • Okta │ │
│ │ • Azure AD │ │
│ │ • Auth0 │ │
│ │ • AWS Cognito │ │
│ │ • Custom OIDC │ │
│ │ • LDAP │ │
│ │ • SAML │ │
│ │ - User grants consent │ │
│ │ - SSO callback with ID token (pac4j)│ │
│ └──────────────────────────────────────┘ │
│ OR │
│ ┌─────────────────────────────┐ │
│ │ Option B: Basic Auth │ │
│ │ - Username/password form │ │
│ │ - Validate with │ │
│ │ OpenMetadata │ │
│ └─────────────────────────────┘ │
│ │
│ 6. Lookup OAuth state from DB using │
│ authRequestId from state parameter │
│ Generate authorization code │
│ Store code + code_challenge in DB │
│ │
│ 7. 302 Redirect with authorization code │
│ {redirect_uri}?code={auth_code}&state={client_state} │
│<─────────────────────────────────────────────────────────────────│
│ │
│ 8. POST /api/v1/mcp/token │
│ grant_type=authorization_code │
│ code={auth_code} │
│ code_verifier={verifier} │
│ client_id={registered_client_id} │
│ redirect_uri=http://127.0.0.1:XXXXX/callback │
│─────────────────────────────────────────────────────────────────>│
│ │
│ 9. PKCE Validation: │
│ Lookup code_challenge from DB │
│ Verify: BASE64URL(SHA256(verifier)) │
│ == code_challenge │
│ Delete authorization code (single-use)│
│ │
│ 10. 200 OK │
│ { │
│ "access_token": "eyJhbGc...", // JWT, 10 min TTL │
│ "refresh_token": "fernet_encrypted", // 30 days TTL │
│ "token_type": "Bearer", │
│ "expires_in": 600 │
│ } │
│<─────────────────────────────────────────────────────────────────│
│ │
│ 11. Use MCP Tools │
│ Authorization: Bearer eyJhbGc... │
│─────────────────────────────────────────────────────────────────>│
│ MCP Tool Execution (lineage, search, discovery, etc.) │
│<─────────────────────────────────────────────────────────────────│
│ │
│ 12. Token Expiry - Refresh Flow │
│ POST /api/v1/mcp/token │
│ grant_type=refresh_token │
│ refresh_token={encrypted_token} │
│─────────────────────────────────────────────────────────────────>│
│ │
│ 13. Refresh Token Rotation: │
│ - Decrypt and validate refresh token │
│ - Delete old refresh token │
│ - Generate new access + refresh tokens│
│ │
│ 14. 200 OK │
│ { │
│ "access_token": "eyJhbGc...", // New JWT │
│ "refresh_token": "new_encrypted", // New rotated token │
│ "token_type": "Bearer", │
│ "expires_in": 600 │
│ } │
│<─────────────────────────────────────────────────────────────────│
│ │
关键安全机制
PKCE(代码交换证明密钥)
- 客户端生成随机的 code_verifier(43–128 个字符)
- 计算 code_challenge = BASE64URL(SHA256(code_verifier))
- 服务端将 code_challenge 与授权码一同存储
- 客户端在令牌交换时发送 code_verifier,以证明其持有该值
- 服务端验证:BASE64URL(SHA256(code_verifier)) == 已存储的 code_challenge
- 防止授权码拦截攻击
由数据库支持的状态持久化
- OAuth 状态参数存储在数据库中,而非 HTTP 会话中
- 可在跨域重定向期间保留(例如 Google OAuth 回调)
- 每个请求都会获得嵌入状态参数的唯一 authRequestId
- 10 分钟 TTL 可防止过期状态攻击
- 单次使用:成功回调后即删除
令牌安全
- 访问令牌:使用 RSA-256 签名的 JWT,通过 JWKS 端点验证
- 刷新令牌:静态存储时使用 Fernet 对称加密
- 授权码:单次使用,有效期为 10 分钟,并与 PKCE challenge 绑定
- 刷新令牌轮换:签发新令牌时使旧令牌失效
配置
MCP 配置(由数据库驱动)
MCP 专属设置通过 REST API 管理,并持久化到数据库:
# Get current MCP configuration
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8585/api/v1/system/mcp/config
# Update MCP configuration (no restart required)
curl -X PUT -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"baseUrl": "https://metadata.example.com",
"allowedOrigins": ["https://app.example.com"],
"connectTimeout": 30000,
"readTimeout": 60000,
"enabled": true
}' \
http://localhost:8585/api/v1/system/mcp/config
配置属性:
baseUrl- OAuth 颁发者 URL(用于元数据端点)allowedOrigins- OAuth 端点的 CORS 白名单(使用特定来源,不要使用*)connectTimeout- 获取 SSO 提供方元数据时的 HTTP 连接超时(毫秒)readTimeout- 获取 SSO 提供方元数据时的 HTTP 读取超时(毫秒)enabled- 启用/禁用 MCP 服务
主要功能:
- 更改会立即应用于所有集群实例(轮询间隔为 10 秒)
- 配置可在服务重启后保留(优先使用数据库,YAML 作为回退)
- 通过监听器模式动态更新 CORS 来源,无需重启
OAuth 服务配置
OAuth 服务在 openmetadata.yaml 中配置:
- JWT 配置 - 用于签署令牌的 RSA 密钥对、JWKS 端点 URL
- 令牌过期时间 - 访问令牌(10 分钟)和刷新令牌(自登录起 30 天)的有效期在代码中固定,不能在此处设置
- 速率限制 - 注册(每个 IP 每小时 10 次)和令牌端点(每个 IP 每分钟 30 次)的速率限制
- SSO 提供方 - 用于 SSO 集成的 Google、Okta、Azure AD 等 OAuth 客户端 ID 和密钥
- 回调 URL - OAuth 客户端允许的重定向 URI
客户端注册
MCP 客户端通过 POST /api/v1/mcp/register 使用动态客户端注册(RFC 7591):
- client_name - 易于理解的客户端名称
- redirect_uris - OAuth 重定向允许使用的回调 URL
- scopes - 请求的 OAuth 作用域(openid、profile、email、offline_access)
- grant_types - 支持的授权类型(authorization_code、refresh_token)
注册端点会为 OAuth 流程返回一个 client_id 和可选的 client_secret。
MCP 工具集成
所有 MCP 工具都使用 OAuth 流程中的 Bearer 令牌进行身份验证:
- GetLineageTool - 在进行授权检查后检索实体血缘关系
- SearchTool - 根据用户权限搜索元数据
- DiscoveryTool - 在访问控制下发现实体
权限模型:工具权限由 OpenMetadata 的 Authorizer 根据 JWT 中的用户身份执行。这确保 MCP 用户拥有与在 OpenMetadata UI 中相同的访问权限,并遵循所有策略、角色和所有权规则。OAuth 负责验证用户身份;Authorizer 负责执行其访问权限。
传输提供方会在每个请求中提取并验证 JWT,为下游 MCP 工具执行设置安全上下文。
最近改进
安全性和可靠性修复
线程安全与并发
- 通过同步 getter 修复配置读取中的竞态条件
- 在外层 finally 块中实现 ThreadLocal 清理,以防止内存泄漏
- 为失败的配置更新添加回滚机制
HTTP 客户端配置
- 使用 pac4j 专属 HTTP 超时设置替代 JVM 全局系统属性
- 可配置获取 SSO 提供方元数据时的连接和读取超时
- 防止超时设置影响其他 HTTP 客户端
会话安全
- 在会话重新生成后添加空值检查,以处理失效/重新创建回退情况
- 同步修改 pac4j 客户端回调 URL,以防止竞态条件
- 通过正确处理会话来增强 CSRF 防护
配置管理
- 优先从数据库加载,确保配置在重启后仍然保留
- 通过集群轮询(间隔为 10 秒)确保各实例的配置一致
- 通过配置变更监听器动态更新 CORS,无需重启
- 验证 MCP 配置 API 中的 URL(防止无效协议和部分通配符)
输入验证
- 验证 baseUrl 协议(仅限 HTTP/HTTPS)
- 拒绝部分通配符来源(例如,
https://*.example.com) - 在开发环境中接受完整通配符(
*)
单元测试
SecurityConfigurationManagerTest(9 项测试)
- 单例模式验证
- 监听器注册与移除
- 线程安全的配置访问(10 个线程 × 100 次迭代)
- 同步 getter 防止竞态条件(50 个并发线程)
- 回滚机制验证
- 配置 getter 行为
MCPConfigurationIntegrationTest(9 项测试,暂缓)
- 验证优先从数据库加载
- 通过 API 更新配置
- 缓存重新加载后配置仍持久化
- 通过轮询检测配置变更
- 输入验证(无效协议、通配符来源)
- 配置重新加载时通知监听器
- 多次顺序更新
测试
OAuth 流程测试
UserSSOOAuthProviderIntegrationTest 中有 15 项全面的集成测试:
- 授权端点验证(client_id、redirect_uri、PKCE 参数)
- 使用 PKCE 验证进行令牌交换
- 刷新令牌轮换
- 拒绝无效的 PKCE challenge/verifier
- 处理过期的授权码
- 验证无效的 client_id 和 redirect_uri
- 处理缺失参数错误
安全测试
- 在多个测试用例中验证 PKCE challenge/verifier
- 验证令牌过期和刷新流程
- 强制授权码仅可使用一次
- 验证 CSRF state 参数
- 验证速率限制行为
SSO 集成测试
使用 pac4j 和模拟身份提供商(Google、Okta、Azure AD 等)测试 SSO 提供商集成。UserSSOOAuthProvider 会根据 OpenMetadata 的身份验证配置自动检测已配置的 SSO 提供商。
部署
数据库迁移
架构迁移文件位于 bootstrap/sql/migrations/native/1.12.0/:
- mysql/schemaChanges.sql - 创建 OAuth 表(oauth_clients、oauth_authorization_codes、oauth_access_tokens、oauth_refresh_tokens、mcp_pending_auth_requests、oauth_audit_log)
- postgres/schemaChanges.sql - 创建 OAuth 表(PostgreSQL 对应版本)
服务初始化
在 McpServer 中初始化 OAuth 组件:
- 设置 JwtFilter 和授权器
- 实例化 OAuth 存储库
- 使用 SSO 配置初始化
UserSSOOAuthProvider - 在 /mcp/* 注册
OAuthHttpStatelessServerTransportProvider - 注册 SSO 回调 servlet 和 Basic Auth 登录 servlet
- 安排
OAuthTokenCleanupJob(间隔 10 分钟)
环境变量
SSO 提供商配置(因提供商而异):
- OIDC_CLIENT_ID - SSO 提供商(Google、Okta、Azure 等)的 OAuth 客户端 ID
- OIDC_CLIENT_SECRET - SSO 提供商的 OAuth 客户端密钥
- OIDC_TYPE - SSO 提供商类型(google、okta、azure、auth0、aws-cognito、custom-oidc)
- OIDC_DISCOVERY_URI - OIDC 发现端点 URL
JWT 令牌配置:
- JWT_ISSUER - 用于令牌验证的 JWT issuer 声明
- JWT_KEY_ID - 用于令牌签名的 RSA 密钥对 ID
MCP 配置(可选,可通过 API 设置):
- MCP_BASE_URL - OAuth issuer 基础 URL
- MCP_ALLOWED_ORIGINS - 以逗号分隔的 CORS 来源
安全注意事项
- 公共客户端安全性 - 所有授权码流程均必须使用 PKCE
- 重定向 URI 验证 - 根据 RFC 8252,HTTP 重定向 URI 仅限环回地址;HTTPS URI 根据已注册的客户端 URI 进行验证
- 令牌存储 - 使用 Fernet 对静态存储的刷新令牌进行加密
- 会话管理 - 采用无状态设计,并通过数据库持久化状态
- 审计跟踪 - 记录所有 OAuth 操作,以满足合规和取证要求
- 速率限制 - 对注册(每个 IP 每小时 10 次)和令牌(每个 IP 每分钟 30 次)端点进行速率限制
- CORS 安全性 - MCP 配置不可用时,拒绝所有 CORS 请求(不采用宽松的 localhost 回退方案)
- 一次性代码 - 兑换后删除授权码
- 令牌轮换 - 每次刷新时轮换刷新令牌,以限制暴露风险
- 时序安全比较 - CSRF 和 PKCE 验证使用 MessageDigest.isEqual(),以防止时序攻击
- 提供方感知型作用域 - 根据 SSO 提供方(Google、Okta、Azure 等)自动调整 OAuth 作用域
- JWK 缓存 - TTL 为 6 小时,并在缓存未命中时重试,以灵活处理密钥轮换
许可证
Apache License 2.0
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。