Zum Hauptinhalt springen

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​

  1. In the Casdoor admin console, go to User Management > Organizations and open the organization of the users, for example acme.

  2. 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.

  3. Under Trusted ID-JAG issuers, add a row:

    ColumnValue
    IssuerThe iss of the ID-JAGs, for example https://acme.okta.com
    JWKS URLThe key set of the IdP. If you leave it empty, Casdoor reads jwks_uri from <issuer>/.well-known/openid-configuration
    UserHow to find the user in the organization: {email} (the default) takes the email claim, {sub} the sub claim, any {claim} that claim. Casdoor matches it against the user name, email, phone, and ID
  4. 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-metadata lists 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​

CheckRule
TypeThe JWT header has typ: oauth-id-jag+jwt. Other assertions go through the ordinary JWT bearer grant
ClientA 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
Audienceaud 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 signatureiss is a trusted issuer of that organization, and the key with the kid of the header in its JWKS verifies the signature
Lifetimeexp and iat are present and valid, with one minute of leeway
Client bindingclient_id equals the client ID of the client that redeems it
Single useCasdoor accepts each jti of an issuer once
UserThe user found through User exists in the organization, isn't forbidden, and may sign in to the client's application
ScopeA 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
Resourceresource, 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​

ErrorCause
invalid_clientA confidential client didn't authenticate
invalid_grantThe 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_scopeThe request asks for a scope that the ID-JAG doesn't grant
invalid_targetThe resource of the ID-JAG isn't an absolute URI
unsupported_grant_typeJWT 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:

  1. Fügen Sie Token Exchange zu den Grant types der Client-Anwendung hinzu.
  2. 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. Ohne resource findet Casdoor die Anwendung anhand der audience, entweder als Client-ID oder als Anwendungsname.
  3. Gewähren Sie Benutzern Zugriff: Fügen Sie eine Berechtigung mit Resource type Application, der Anwendung des Servers als Ressource und Effect Allow hinzu. Casdoor stellt eine ID-JAG nur einem Benutzer aus, der durch eine Allow-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.

Siehe auch​