Skip to content

Enterprise managed authorization#770

Open
radar07 wants to merge 10 commits intomodelcontextprotocol:mainfrom
radar07:enterprise-managed-authorization
Open

Enterprise managed authorization#770
radar07 wants to merge 10 commits intomodelcontextprotocol:mainfrom
radar07:enterprise-managed-authorization

Conversation

@radar07
Copy link

@radar07 radar07 commented Jan 30, 2026

auth, oauthex: implement Enterprise Managed Authorization (SEP-990)
This PR implements Enterprise Managed Authorization (SEP-990) for the Go MCP SDK, enabling MCP Clients and Servers to leverage enterprise Identity Providers for seamless authorization without requiring users to authenticate separately to each MCP Server.
Overview
Enterprise Managed Authorization follows the Identity Assertion Authorization Grant specification (draft-ietf-oauth-identity-assertion-authz-grant), implementing a three-step flow:

  1. Single Sign-On (SSO): User authenticates to the MCP Client via enterprise IdP (Okta, Auth0, Azure AD, etc.)
  2. Token Exchange (RFC 8693): Client exchanges ID Token for Identity Assertion JWT Authorization Grant (ID-JAG) at the IdP
  3. JWT Bearer Grant (RFC 7523): Client exchanges ID-JAG for Access Token at the MCP Server
    This enables:
  • For end users: Single sign-on across MCP Clients and Servers—no manual connection/authorization per server
  • For enterprise admins: Centralized visibility and control over which MCP Servers can be used within the organization
  • For MCP clients: Automatic token acquisition without user interaction for each server

Closes: #628

@maciej-kisiel
Copy link
Contributor

Hi @radar07, thanks for submitting this PR. Could you link the issue that it is addressing?

Also, as a heads-up: it will likely take some time to review your proposal. Both because it's quite large, but more importantly I'm also working on a proposal how to structure the client-side OAuth implementation and this change will need to be aligned with it.

@radar07
Copy link
Author

radar07 commented Jan 31, 2026

Thanks @maciej-kisiel. I updated the description with the SEP that this PR solves.

@radar07
Copy link
Author

radar07 commented Feb 3, 2026

@maciej-kisiel I'd be happy to contribute to OAuth implementation. Let me know if I can help with anything. Just want to know if I should add conformance tests to this because I can see that there are PRs related to conformance tests.

Copy link
Contributor

@maciej-kisiel maciej-kisiel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a brief look at your PR and I believe we could utilize the oauth2 library more aggressively.

Please also take a look at my PR/proposal for client-side OAuth at #785. I think this PR could be expressed with the proposed abstractions, but your OAuth expertise would make the feedback valuable.


// JWTBearerResponse represents the response from a JWT Bearer grant request
// per RFC 7523. This uses the standard OAuth 2.0 token response format.
type JWTBearerResponse struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

// JWTBearerError represents an error response from a JWT Bearer grant request.
type JWTBearerError struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// "mcp-client-secret",
// nil,
// )
func ExchangeJWTBearer(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like https://pkg.go.dev/golang.org/x/oauth2#Config.Exchange could be used to replicate the behavior of the function. Is there any reason not to use it?

mediaType, _, err := mime.ParseMediaType(ct)
if err != nil || mediaType != "application/json" {
return nil, fmt.Errorf("bad content type %q", ct)
ct := strings.TrimSpace(strings.SplitN(res.Header.Get("Content-Type"), ";", 2)[0])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you comment why special behavior is needed in place of mime.ParseMediaType?

// }
//
// resp, err := ExchangeToken(ctx, idpTokenEndpoint, req, clientID, clientSecret, nil)
func ExchangeToken(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like https://pkg.go.dev/golang.org/x/oauth2#Config.Exchange can be used for Token Exchange as well? golang/oauth2#409


// TokenExchangeResponse represents the response from a token exchange request
// per RFC 8693 Section 2.2.
type TokenExchangeResponse struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments as in oauthex/jwt_bearer.go apply to the request and error types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ext-auth: Enterprise Managed Authorization

2 participants