使用 ID-JAG 的企业托管授权
本指南解释了企业托管授权(Enterprise-Managed Authorization),这是针对企业的 MCP 授权规范的扩展:企业身份提供商 (IdP) 决定员工可以使用哪些 MCP 服务器,而 MCP 客户端在每个服务器上获取令牌时无需浏览器登录。Casdoor 可以承担以下任一角色:
- MCP 服务器的授权服务器:Casdoor 接受组织信任的企业 IdP 签发的 ID-JAG。
- 企业 IdP:Casdoor 为授权服务器信任它的 MCP 服务器签发 ID-JAG。
学习目标
- 了解流程以及什么是 ID-JAG。
- 让 Casdoor 的组织信任企业 IdP 的 ID-JAG。
- 从 Casdoor 签发 ID-JAG。
前提条件
- 拥有 Casdoor 管理控制台的超级管理员权限
- 支持该扩展的 MCP 客户端,例如带有 Okta Cross App Access 的 Claude Team 或 Enterprise
工作原理
ID-JAG(Identity Assertion JWT Authorization Grant)是由 IdP 签名的短期有效 JWT。它表明:此用户可以在此授权服务器使用此客户端。其定义见 draft-ietf-oauth-identity-assertion-authz-grant,MCP 扩展 io.modelcontextprotocol/enterprise-managed-authorization 基于此构建。参见 Enterprise-Managed Authorization。
用户永远不会看到 Casdoor 的登录页面。当 IdP 阻止用户时,例如他们离开公司后,客户端将不会获得新的 ID-JAG,也不会获得新的访问令牌。
在 Casdoor 中接受 ID-JAG
信任 IdP
-
在 Casdoor 管理控制台中,前往 User Management > Organizations 并打开用户所属的组织,例如
acme。 -
可选地,开启 Use organization issuer,以便该组织拥有自己的发行者
https://your-casdoor.com/orgs/acme。否则,发行者为全局发行者https://your-casdoor.com。 -
在 Trusted ID-JAG issuers 下添加一行:
Column Value Issuer ID-JAG 的 iss,例如https://acme.okta.comJWKS URL IdP 的密钥集。如果留空,Casdoor 将从 <issuer>/.well-known/openid-configuration读取jwks_uriUser 如何在组织中查找用户: {email}(默认值)取email声明,{sub}取sub声明,任何{claim}取该声明。Casdoor 将其与用户名、电子邮件、电话和 ID 进行匹配 -
保存组织。只有全局管理员可以更改受信任的发行者。
在 IdP 中,将组织的发行者作为 ID-JAG 的受众授予 Casdoor,并将 MCP 服务器 URL 作为资源。对于位于 MCP gateway 后面的服务器,即 https://your-casdoor.com/api/server/acme/<server-name>。
让客户端使用该授权
客户端在令牌端点使用 JWT bearer grant (urn:ietf:params:oauth:grant-type:jwt-bearer) 兑换 ID-JAG,因此必须允许其应用使用该授权类型:
- Claude:其客户端 ID 元数据文档
https://claude.ai/oauth/mcp-oauth-client-metadata列出了该授权。为组织开启 Enable dynamic client registration,以便 Casdoor 接受该文档。参见 Connect Claude。 - 你创建的应用:在其 Grant types 中添加 JWT Bearer。
客户端发送的内容
curl -X POST https://your-casdoor.com/api/login/oauth/access_token \
-d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \
-d client_id=<client-id> \
-d client_secret=<client-secret> \
-d assertion=<ID-JAG>
Casdoor 返回一个访问令牌,其受众是 ID-JAG 的 resource:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 604800,
"scope": "openid profile"
}
Casdoor 检查的内容
| 检查项 | 规则 |
|---|---|
| Type | JWT 标头包含 typ: oauth-id-jag+jwt。其他断言通过普通的 JWT bearer grant 处理 |
| Client | 机密客户端进行身份验证,使用其客户端密钥或 private_key_jwt。来自客户端 ID 元数据文档的公共客户端(如 Claude)没有密钥,仅发送 client_id |
| Audience | aud 是拥有自己发行者的组织的发行者,或者是全局发行者,后者代表客户端应用所属的组织 |
| Issuer and signature | iss 是该组织的受信任发行者,且使用标头中的 kid 在其 JWKS 中对应的密钥验证签名 |
| Lifetime | exp 和 iat 存在且有效,有一分钟的容差 |
| Client binding | client_id 等于兑换它的客户端的客户端 ID |
| Single use | Casdoor 只接受每个发行者的 jti 一次 |
| User | 通过 User 找到的用户在组织中存在,未被禁用,并且可以登录到客户端的应用 |
| Scope | 请求中的 scope 必须是 ID-JAG 的 scope 的子集。如果没有提供,令牌将获得 ID-JAG 的范围 |
| Resource | 如果存在,resource 将成为访问令牌的受众 |
自行注册(DCR)或使用客户端 ID 元数据文档的客户端只能配合 ID-JAG 使用 JWT bearer grant。
错误
| Error | Cause |
|---|---|
invalid_client | 机密客户端未进行身份验证 |
invalid_grant | ID-JAG 未通过某项检查:受众错误、发行者不受信任、签名无效、过期、颁发给其他客户端、已被使用,或用户不存在 |
invalid_scope | 请求要求 ID-JAG 未授予的范围 |
invalid_target | ID-JAG 的 resource 不是绝对 URI |
unsupported_grant_type | JWT Bearer 不在客户端应用的授权类型中 |
授权服务器元数据声明了支持:grant_types_supported 包含 urn:ietf:params:oauth:grant-type:jwt-bearer,且 authorization_grant_profiles_supported 包含 urn:ietf:params:oauth:grant-profile:id-jag。
从 Casdoor 签发 ID-JAG
当 Casdoor 是公司的身份提供商 (IdP) 时,客户端在 Casdoor 的令牌端点通过令牌交换 (RFC 8693) 将用户的 ID 令牌换取为 ID-JAG:
curl -X POST https://your-casdoor.com/api/login/oauth/access_token \
-d grant_type=urn:ietf:params:oauth:grant-type:token-exchange \
-d client_id=<client-id> \
-d client_secret=<client-secret> \
-d requested_token_type=urn:ietf:params:oauth:token-type:id-jag \
-d subject_token=<ID token of the user> \
-d subject_token_type=urn:ietf:params:oauth:token-type:id_token \
-d audience=https://mcp-as.example.com \
-d resource=https://mcp.example.com/mcp \
-d scope="openid profile"
{
"issued_token_type": "urn:ietf:params:oauth:token-type:id-jag",
"access_token": "eyJ0eXAiOiJvYXV0aC1pZC1qYWcrand0Ii...",
"token_type": "N_A",
"expires_in": 300,
"scope": "openid profile"
}
按如下方式设置:
- 将 Token Exchange 添加到客户端应用的 Grant types 中。
- 在用户组织的 LLM AI > MCP Servers 页面注册 MCP 服务器,将其 URL 作为
resource,并选择一个代表它的应用 (Application)。如果没有resource,Casdoor 会通过audience(作为客户端 ID 或应用名称)查找应用。 - 授予用户访问权限:添加一个权限,其中 Resource type 为
Application,资源为该服务器的应用,Effect 为Allow。Casdoor 仅向被Allow权限覆盖的用户签发 ID-JAG,因此未被授权给任何人的应用保持关闭状态。
ID-JAG 有效期为 5 分钟。其 aud 是请求中的 audience,client_id 是发起请求的应用的客户端 ID,并且携带用户的 email、resource 和 scope。MCP 服务器的授权服务器必须信任 Casdoor 的颁发者,且客户端在那里必须使用相同的客户端 ID,例如其客户端 ID 元数据文档的 URL。