Enterprise-managed authorization mit ID-JAG
Dieses Handbuch erklärt Enterprise-Managed Authorization, eine Erweiterung der MCP-Autorisierungsspezifikation für Unternehmen: Der Enterprise-Identitätsanbieter (IdP) entscheidet, welche MCP-Server ein Mitarbeiter nutzen darf, und der MCP-Client erhält seine Tokens ohne Browser-Anmeldung bei jedem Server. Casdoor übernimmt entweder Rolle:
- Der Autorisierungsserver von MCP-Servern: Casdoor akzeptiert die ID-JAGs eines Enterprise-IdPs, dem eine Organisation vertraut.
- The enterprise IdP: Casdoor issues ID-JAGs for MCP servers whose authorization server trusts it.
Lernziele
- Understand the flow and what an ID-JAG is.
- Let an organization of Casdoor trust the ID-JAGs of an enterprise IdP.
- Issue ID-JAGs from Casdoor.
Voraussetzungen
- Globaler Administratorzugriff auf die Casdoor-Admin-Konsole
- An MCP client that supports the extension, for example Claude Team or Enterprise with Okta Cross App Access
Wie es funktioniert
An ID-JAG (Identity Assertion JWT Authorization Grant) is a short-lived JWT that the IdP signs. It says: this user may use this client at this authorization server. It is defined in draft-ietf-oauth-identity-assertion-authz-grant, and the MCP extension io.modelcontextprotocol/enterprise-managed-authorization builds on it. See Enterprise-Managed Authorization.
The user never sees the sign-in page of Casdoor. When the IdP stops the user, for example after they leave the company, the client gets no new ID-JAG, and no new access token.
Accept ID-JAGs in Casdoor
Trust the IdP
-
In the Casdoor admin console, go to User Management > Organizations and open the organization of the users, for example
acme. -
Optionally, turn on Use organization issuer, so that the organization has its own issuer,
https://your-casdoor.com/orgs/acme. Otherwise, the issuer is the global one,https://your-casdoor.com. -
Under Trusted ID-JAG issuers, add a row:
Column Value Issuer The issof the ID-JAGs, for examplehttps://acme.okta.comJWKS URL The key set of the IdP. If you leave it empty, Casdoor reads jwks_urifrom<issuer>/.well-known/openid-configurationUser How to find the user in the organization: {email}(the default) takes theemailclaim,{sub}thesubclaim, any{claim}that claim. Casdoor matches it against the user name, email, phone, and ID -
Save the organization. Only a global administrator can change the trusted issuers.
In the IdP, give Casdoor the issuer of the organization as the audience of the ID-JAGs, and the MCP server URLs as resources. For a server behind the MCP gateway, that is https://your-casdoor.com/api/server/acme/<server-name>.
Let the client use the grant
The client redeems the ID-JAG at the token endpoint with the JWT bearer grant (urn:ietf:params:oauth:grant-type:jwt-bearer), so the grant type must be allowed for its application:
- Claude: Its client ID metadata document
https://claude.ai/oauth/mcp-oauth-client-metadatalists the grant. Turn on Enable dynamic client registration for the organization, so that Casdoor accepts the document. See Connect Claude. - An application that you create: Add JWT Bearer to its Grant types.
What the client sends
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 answers with an access token whose audience is the resource of the ID-JAG:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 604800,
"scope": "openid profile"
}
What Casdoor checks
| Check | Rule |
|---|---|
| Type | The JWT header has typ: oauth-id-jag+jwt. Other assertions go through the ordinary JWT bearer grant |
| Client | A confidential client authenticates, with its client secret or private_key_jwt. A public client of a client ID metadata document, such as Claude, has no secret and sends only client_id |
| Audience | aud is the issuer of an organization with its own issuer, or the global issuer, which stands for the organization of the client's application |
| Issuer and signature | iss is a trusted issuer of that organization, and the key with the kid of the header in its JWKS verifies the signature |
| Lifetime | exp and iat are present and valid, with one minute of leeway |
| Client binding | client_id equals the client ID of the client that redeems it |
| Single use | Casdoor accepts each jti of an issuer once |
| User | The user found through User exists in the organization, isn't forbidden, and may sign in to the client's application |
| Scope | A scope in the request must be a subset of the scope of the ID-JAG. Without one, the token gets the scope of the ID-JAG |
| Resource | resource, when present, becomes the audience of the access token |
A client that registered itself (DCR) or uses a client ID metadata document can use the JWT bearer grant only with an ID-JAG.
Fehler
| Error | Cause |
|---|---|
invalid_client | A confidential client didn't authenticate |
invalid_grant | The ID-JAG failed one of the checks: wrong audience, untrusted issuer, bad signature, expired, issued to another client, used before, or the user doesn't exist |
invalid_scope | The request asks for a scope that the ID-JAG doesn't grant |
invalid_target | The resource of the ID-JAG isn't an absolute URI |
unsupported_grant_type | JWT Bearer ist nicht unter den Grant-Typen der Client-Anwendung enthalten |
Die Metadaten des Autorisierungsservers geben die Unterstützung an: grant_types_supported enthält urn:ietf:params:oauth:grant-type:jwt-bearer, und authorization_grant_profiles_supported enthält urn:ietf:params:oauth:grant-profile:id-jag.
ID-JAGs von Casdoor ausstellen
Wenn Casdoor der IdP des Unternehmens ist, tauscht ein Client das ID-Token des Benutzers am Token-Endpoint von Casdoor gegen eine ID-JAG über den Token-Austausch (RFC 8693) ein:
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"
}
Richten Sie es wie folgt ein:
- Fügen Sie Token Exchange zu den Grant types der Client-Anwendung hinzu.
- Registrieren Sie den MCP-Server auf der Seite LLM AI > MCP Servers der Organisation des Benutzers, mit seiner URL als
resource, und wählen Sie eine Application, die für ihn steht. Ohneresourcefindet Casdoor die Anwendung anhand deraudience, entweder als Client-ID oder als Anwendungsname. - Gewähren Sie Benutzern Zugriff: Fügen Sie eine Berechtigung mit Resource type
Application, der Anwendung des Servers als Ressource und EffectAllowhinzu. Casdoor stellt eine ID-JAG nur einem Benutzer aus, der durch eineAllow-Berechtigung abgedeckt wird; daher bleibt eine Anwendung, der niemand gewährt wurde, geschlossen.
Die ID-JAG ist 5 Minuten gültig. Ihr aud ist die audience der Anfrage, client_id ist die Client-ID der anfordernden Anwendung, und sie trägt die email des Benutzers, die resource und den scope. Der Autorisierungsserver des MCP-Servers muss dem Aussteller von Casdoor vertrauen, und der Client muss dort dieselbe Client-ID verwenden, zum Beispiel die URL seines Client-ID-Metadatendokuments.