Summary
SEP-990 ("Enable enterprise IdP policy controls during MCP OAuth flows") is a finalized MCP specification that enables enterprise Identity Providers to control authorization of MCP clients within corporate environments. It provides:
- For enterprise admins: Centralized visibility and control over which MCP servers can be used within the organization
- For end users: Automatic single sign-on across MCP clients and servers -- no manual authentication per server
This issue evaluates what SEP-990 means for muster and identifies the work needed to integrate it.
Background: What SEP-990 Does
SEP-990 implements the Identity Assertion Authorization Grant (ID-JAG) specification, combining RFC 8693 Token Exchange and RFC 7523 JWT Bearer Grant into a three-step flow:
- SSO Login: User authenticates to the MCP Client (muster) via enterprise IdP (Okta, Azure AD, etc.)
- Token Exchange (RFC 8693): Client exchanges the ID Token for an Identity Assertion JWT (ID-JAG) at the enterprise IdP. The IdP evaluates policy ("Is this user allowed to use this MCP server?") before issuing the ID-JAG.
- JWT Bearer Grant (RFC 7523): Client exchanges the ID-JAG for an access token at the MCP Server's authorization server.
The critical difference from standard OAuth is that the enterprise IdP sits in the middle of every MCP server connection, giving it policy control and eliminating per-server user consent.
User -> MCP Client (muster) -> Enterprise IdP (token exchange) -> MCP Server Auth Server (JWT bearer grant) -> Access Token
How Muster Is Affected
Current State
Muster currently supports three auth mechanisms for downstream MCP servers:
- Authorization Code Flow (
core_auth_login): User manually authenticates via browser-based OAuth redirect per server. Implemented in internal/oauth/client.go and internal/aggregator/auth_tools.go.
- Token Forwarding (
forwardToken: true): Muster's own ID token is forwarded to downstream servers that trust muster's OAuth client ID. Implemented in EstablishConnectionWithTokenForwarding() in internal/aggregator/connection_helper.go.
- Token Exchange (RFC 8693) (
tokenExchange config): Muster exchanges its local token for a token valid on a remote cluster's Dex instance. Implemented in EstablishConnectionWithTokenExchange() and internal/oauth/token_exchange.go.
SEP-990 introduces a fourth mechanism that is conceptually similar to the existing token exchange but targets a different use case: instead of exchanging tokens between Dex instances within Giant Swarm's infrastructure, it exchanges tokens through an enterprise IdP (Okta, Azure AD, etc.) for access to third-party MCP servers (SaaS tools, partner services, etc.).
Key Differences from Existing Token Exchange
| Aspect |
Current RFC 8693 Token Exchange |
SEP-990 Enterprise IdP |
| IdP Type |
Internal Dex instances |
Enterprise IdP (Okta, Azure AD) |
| Target |
Remote Giant Swarm clusters |
Any MCP server (SaaS, internal) |
| Step 1 |
Exchange ID token at remote Dex |
Exchange ID token for ID-JAG at enterprise IdP |
| Step 2 |
Use exchanged token directly |
Exchange ID-JAG at MCP server's auth server (RFC 7523) |
| Policy Control |
Trust-based (Dex connector config) |
Enterprise admin policy at IdP |
| User Interaction |
None (SSO) |
None (SSO) |
| Token Type Returned |
Access token from Dex |
ID-JAG (intermediate), then access token from MCP server |
Muster's Role as Gateway/Proxy
Muster is an MCP aggregator/gateway -- the exact architecture pattern the MCP roadmap identifies for enterprise deployments. As a gateway, muster is the ideal place to implement SEP-990 because:
- All MCP server connections flow through muster
- Muster already handles SSO token management (ID token storage, refresh, forwarding)
- Enterprise admins already configure MCP servers through muster's MCPServer CRDs
- Adding SEP-990 at the gateway level means individual MCP servers don't need to change
Implementation Considerations
1. Go SDK Dependency
The mcp-go SDK has an open PR (#770) implementing SEP-990 as an extauth package under auth/. This PR is under active review (as of March 2026) and implements the OAuthHandler interface pattern. Muster should track this PR and evaluate whether to:
- Wait for the upstream SDK to stabilize and use it
- Implement the ID-JAG flow directly using existing libraries (
golang.org/x/oauth2, mcp-oauth)
2. Two-Step Token Acquisition
Unlike the current single-step RFC 8693 exchange, SEP-990 requires two sequential token requests:
Step 1: Token Exchange at Enterprise IdP
POST /oauth2/token HTTP/1.1
Host: enterprise.okta.com
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token={id_token}
&subject_token_type=urn:ietf:params:oauth:token-type:id_token
&requested_token_type=urn:ietf:params:oauth:token-type:id-jag
&audience={mcp_server_auth_server_issuer}
Step 2: JWT Bearer Grant at MCP Server Auth Server
POST /oauth2/token HTTP/1.1
Host: auth.mcp-server.example.com
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion={id_jag_token}
This means the TokenExchanger abstraction in internal/oauth/token_exchange.go would need to be extended or a new EnterpriseIdPExchanger created alongside it.
3. MCPServer CRD Configuration
A new auth configuration option would be needed in the MCPServer CRD, e.g.:
apiVersion: mcp.giantswarm.io/v1alpha1
kind: MCPServer
spec:
auth:
enterpriseIdP:
enabled: true
idpTokenEndpoint: https://enterprise.okta.com/oauth2/token
mcpAuthServerIssuer: https://auth.mcp-server.example.com
clientCredentialsSecretRef:
name: enterprise-idp-credentials
scopes: "read write"
4. Integration with initSSOForSession
SEP-990 connections should integrate with the existing initSSOForSession() flow in internal/aggregator/auth_resource.go, establishing enterprise IdP connections automatically at login time (just like token forwarding and token exchange do today).
5. ID-JAG Validation
When muster receives an ID-JAG (if it also acts as an MCP authorization server), it needs to:
- Validate the JWT signature using the enterprise IdP's JWKS
- Check the
iss, aud, sub, and exp claims
- Verify the
client_id claim matches the requesting application
6. Caching Strategy
The existing token exchange cache (oidc.TokenExchangeCache) could be extended to cache both the intermediate ID-JAG and the final access token. The ID-JAG typically has a short lifetime (5 minutes), while the final access token may be longer-lived.
Upstream Dependencies
Acceptance Criteria
References
Summary
SEP-990 ("Enable enterprise IdP policy controls during MCP OAuth flows") is a finalized MCP specification that enables enterprise Identity Providers to control authorization of MCP clients within corporate environments. It provides:
This issue evaluates what SEP-990 means for muster and identifies the work needed to integrate it.
Background: What SEP-990 Does
SEP-990 implements the Identity Assertion Authorization Grant (ID-JAG) specification, combining RFC 8693 Token Exchange and RFC 7523 JWT Bearer Grant into a three-step flow:
The critical difference from standard OAuth is that the enterprise IdP sits in the middle of every MCP server connection, giving it policy control and eliminating per-server user consent.
How Muster Is Affected
Current State
Muster currently supports three auth mechanisms for downstream MCP servers:
core_auth_login): User manually authenticates via browser-based OAuth redirect per server. Implemented ininternal/oauth/client.goandinternal/aggregator/auth_tools.go.forwardToken: true): Muster's own ID token is forwarded to downstream servers that trust muster's OAuth client ID. Implemented inEstablishConnectionWithTokenForwarding()ininternal/aggregator/connection_helper.go.tokenExchangeconfig): Muster exchanges its local token for a token valid on a remote cluster's Dex instance. Implemented inEstablishConnectionWithTokenExchange()andinternal/oauth/token_exchange.go.SEP-990 introduces a fourth mechanism that is conceptually similar to the existing token exchange but targets a different use case: instead of exchanging tokens between Dex instances within Giant Swarm's infrastructure, it exchanges tokens through an enterprise IdP (Okta, Azure AD, etc.) for access to third-party MCP servers (SaaS tools, partner services, etc.).
Key Differences from Existing Token Exchange
Muster's Role as Gateway/Proxy
Muster is an MCP aggregator/gateway -- the exact architecture pattern the MCP roadmap identifies for enterprise deployments. As a gateway, muster is the ideal place to implement SEP-990 because:
Implementation Considerations
1. Go SDK Dependency
The
mcp-goSDK has an open PR (#770) implementing SEP-990 as anextauthpackage underauth/. This PR is under active review (as of March 2026) and implements theOAuthHandlerinterface pattern. Muster should track this PR and evaluate whether to:golang.org/x/oauth2,mcp-oauth)2. Two-Step Token Acquisition
Unlike the current single-step RFC 8693 exchange, SEP-990 requires two sequential token requests:
Step 1: Token Exchange at Enterprise IdP
Step 2: JWT Bearer Grant at MCP Server Auth Server
This means the
TokenExchangerabstraction ininternal/oauth/token_exchange.gowould need to be extended or a newEnterpriseIdPExchangercreated alongside it.3. MCPServer CRD Configuration
A new auth configuration option would be needed in the MCPServer CRD, e.g.:
4. Integration with initSSOForSession
SEP-990 connections should integrate with the existing
initSSOForSession()flow ininternal/aggregator/auth_resource.go, establishing enterprise IdP connections automatically at login time (just like token forwarding and token exchange do today).5. ID-JAG Validation
When muster receives an ID-JAG (if it also acts as an MCP authorization server), it needs to:
iss,aud,sub, andexpclaimsclient_idclaim matches the requesting application6. Caching Strategy
The existing token exchange cache (
oidc.TokenExchangeCache) could be extended to cache both the intermediate ID-JAG and the final access token. The ID-JAG typically has a short lifetime (5 minutes), while the final access token may be longer-lived.Upstream Dependencies
mcp-goSDK: PR #770 - Enterprise Managed Authorization implementation (under review)Acceptance Criteria
mcp-goSDK'sextauthpackage (when merged) is suitable for muster's use caseinitSSOForSessionflowReferences