Skip to content

Conversation

@aaronpk
Copy link
Contributor

@aaronpk aaronpk commented Jun 4, 2025

Motivation and Context

This extension is designed to facilitate secure and interoperable authorization of MCP clients within corporate environments, leveraging existing enterprise identity infrastructure.

  • For end users, this removes the need to manually connect and authorize the MCP Client to individual services within the organization.
  • For enterprise admins, this enables visibility and control over which MCP Servers are able to be used within the organization.

How Has This Been Tested?

We have an end to end implementation of this here, and have in-progress implementations with some partners.

Breaking Changes

This is designed to augment the existing OAuth profile by providing an alternative when used under an enterprise IdP. MCP clients can opt in to this profile when necessary.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

For more background on this problem, you can refer to my blog post about this here:

Enterprise-Ready MCP

I also presented this at the MCP Dev Summit in May.

A high level overview of the flow is below:

sequenceDiagram
    participant UA as Browser
    participant C as MCP Client
    participant MAS as MCP Authorization Server
    participant MRS as MCP Resource Server
    participant IdP as Identity Provider

    C-->>UA: Redirect to IdP
    UA->>IdP: Redirect to IdP
    Note over IdP: User Logs In
    IdP-->>UA: IdP Authorization Code
    UA->>C: IdP Authorization Code
    C->>IdP: Token Request with IdP Authorization Code
    IdP-->C: ID Token

    note over C: User is logged<br>in to MCP Client.<br>Client stores ID Token.

    C->IdP: Exchange ID Token for ID-JAG
    note over IdP: Evaluate Policy
    IdP-->C: Responds with ID-JAG
    C->MAS: Token Request with ID-JAG
    note over MAS: Validate ID-JAG
    MAS-->C: MCP Access Token

    loop
    C->>MRS: Call MCP API with Access Token
    MRS-->>C: MCP Response with Data
    end
Loading

aaronpk and others added 4 commits June 4, 2025 11:36
Co-authored-by: Den Delimarsky 🌺 <53200638+localden@users.noreply.github.com>
Co-authored-by: Den Delimarsky 🌺 <53200638+localden@users.noreply.github.com>
Co-authored-by: Den Delimarsky 🌺 <53200638+localden@users.noreply.github.com>
Co-authored-by: Den Delimarsky 🌺 <53200638+localden@users.noreply.github.com>
Co-authored-by: Den Delimarsky 🌺 <53200638+localden@users.noreply.github.com>
Co-authored-by: Den Delimarsky 🌺 <53200638+localden@users.noreply.github.com>
aaronpk and others added 5 commits June 4, 2025 11:44
Co-authored-by: Den Delimarsky 🌺 <53200638+localden@users.noreply.github.com>
Co-authored-by: Den Delimarsky 🌺 <53200638+localden@users.noreply.github.com>
Co-authored-by: Den Delimarsky 🌺 <53200638+localden@users.noreply.github.com>
Co-authored-by: Den Delimarsky 🌺 <53200638+localden@users.noreply.github.com>
Co-authored-by: Den Delimarsky 🌺 <53200638+localden@users.noreply.github.com>
Co-authored-by: Den Delimarsky 🌺 <53200638+localden@users.noreply.github.com>
| `audience` | REQUIRED | The Issuer URL of the MCP server's authorization server. | `https://auth.chat.example/` |
| `resource` | REQUIRED | The RFC9728 Resource Identifier of the MCP server. | `https://mcp.chat.example/` |
| `scope` | OPTIONAL | The space-separated list of scopes at the MCP Server that are being requested. | `scope1 scope2` |
| `subject_token` | REQUIRED | The identity assertion (e.g. the OpenID Connect ID Token or SAML assertion) for the target end-user. | (JWT or SAML assertion string) |
Copy link

Choose a reason for hiding this comment

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

In the OIDC case, it might be better to use the access token here, assuming that the IdP could authorize a special scope for that token, which tells the user that the client is requesting to access MCP servers with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

for that to work, the initial OIDC SSO flow would have to include something in addition to the standard SSO flow, to tell the IdP about the future intent. We wanted to be able to do this without anything special on the initial SSO, both for OIDC and SAML.

Choose a reason for hiding this comment

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

It was expected that the IdP would need an authorization statement (e.g. policy) that grants the client_id the ability to perform cross-domain access to another AS for specific scopes for a specific set of users. Since the token exchange operation is a intent-based request with context by the client and not just another protected resource it didn't make sense to require a scope (you authorize token-exchange grant type for ID-JAG token type). Its the downstream resource+scopes that matter.

Copy link

Choose a reason for hiding this comment

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

Another benefit of using the access token instead of the ID Token here could be that we could use DPoP by treating the MCP Authorization Server as the resource server. By using @aaronpk 's client URI mechanism, this can also provide client attestation. To do this, the MCP Authorization Server obtains the public key from the client metadata rather than from the access token JWT or token introspection.

Copy link

Choose a reason for hiding this comment

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

It was expected that the IdP would need an authorization statement (e.g. policy) that grants the client_id the ability to perform cross-domain access to another AS for specific scopes for a specific set of users. Since the token exchange operation is a intent-based request with context by the client and not just another protected resource it didn't make sense to require a scope (you authorize token-exchange grant type for ID-JAG token type). Its the downstream resource+scopes that matter.

I agree that the token exchange intent was not to scope-limit the exchange, but it might be beneficial from both an enterprise policy point of view (does this user need access to MCP servers right now), or from a user consent point of view in the consumer case (be warned this token gives the client access to MCP servers on your behalf).

Copy link

Choose a reason for hiding this comment

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

I would rather do that by having the client request a new scope from the IdP. There's already OpenID scopes used to indicate what claims to include in an ID token and it feels conceptually similar to that.

Yup. That is indeed what I'm suggesting.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great, we can do that without changing this to an access token input then.

Copy link

Choose a reason for hiding this comment

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

Ah ok. I guess I was suggesting the scope to be set on the access token. I didn't realize it could be set on the ID Token too.

Copy link

@sandeepdhankhar sandeepdhankhar Aug 28, 2025

Choose a reason for hiding this comment

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

The possible concern could be overloading the conventional OIDC scopes, if the token contains the permissions(scopes) for (MCP server) then its no longer a authentication only flow, might be more clear to call it OAuth2 explicitly. The client can still request for the scopes in the authorization call itself where the user can explicitly provide consent. The scopes can still be embedded in the Access Token as a part of regular code grant flow or use the access token to request for this new JWT with scopes in case the list of scopes could be too long.

Copy link

@sandeepdhankhar sandeepdhankhar Aug 28, 2025

Choose a reason for hiding this comment

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

while it can certainly be done, setting the scopes/claims in idToken or using it for accessing the resources could be confusing and anti-pattern on how the idToken is used conventionally

…n.mdx

Co-authored-by: Hi120ki <12624257+hi120ki@users.noreply.github.com>
@aaronpk
Copy link
Contributor Author

aaronpk commented Jul 29, 2025

I don't know what I clicked to request a review from the docs maintainers team. I think GitHub did that automatically when I pulled in the main branch changes into this.

@localden localden changed the title Enterprise-Managed Authorization Profile for MCP SEP-646: Enterprise-Managed Authorization Profile for MCP Jul 31, 2025
@sunishsheth2009
Copy link
Contributor

Thanks for adding this to the spec—really appreciate the detailed work here!

Quick question regarding token lifecycle: what happens when the access_token expires? How does the refresh mechanism work in this setup? Specifically:

  • Would clients need to go back to the IDP to get a new ID Token, and then use that to fetch a new ID-JAG token, which is then used to get a new access_token?
  • Or is the ID Token expected to be long-lived enough that it can be reused to fetch new ID-JAG tokens without needing to reauthenticate with the IDP? As far as I can see, I don't see an expires_in for the ID Token. Does that mean, its short lived?

Would love some clarification on the expected flow here. Thanks again! Sorry if this is already covered somewhere and I am missing something. Thank you.

@joshcanhelp
Copy link
Contributor

joshcanhelp commented Aug 2, 2025

@sunishsheth2009 - I asked that above and Aaron opened an issue here to discuss:

oauth-wg/oauth-identity-assertion-authz-grant#35

TLDR: the first flow you described: refresh the ID token, new ID-JAG, new access token 👍

ID tokens are meant to be verified and used by the client requesting them so you can use the exp claim in there to determine how long it should be used for. They are typically used during the transaction to verify identity or start a session and don't typically stick around for long.

Location: https://acme.idp.example/authorize?response_type=code&scope=openid&client_id=...
```

The user authenticates with the IdP, and is redirected back to the Client with an authorization code, which it can then exchange for an ID Token.

Choose a reason for hiding this comment

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

To confirm, this client has access to client secret to complete the authorization code flow for all possible scenarios or would there by any exceptions eg. local clients?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This could also work with public clients, that's really just a policy up to the IDP and AS.

Choose a reason for hiding this comment

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

so its safe to assume that a server side component would always be required for this flow to orchestrate it securely

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The question of client authentication/identity is not really related to this proposal.

Copy link

@sandeepdhankhar sandeepdhankhar Aug 29, 2025

Choose a reason for hiding this comment

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

Guess I am just trying to wrap my head around the usage of idToken instead of accessToken, one is more tied in pure client scenarios ( no client secret required) only for identity verification purposes. Is there is any literature about the explicit benefit of using the idToken instead of accessToken given the client secret is always required in the flow.

Copy link

@sdesen sdesen Sep 5, 2025

Choose a reason for hiding this comment

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

Is there is any literature about the explicit benefit of using the idToken

The goal of using the idToken is to assert to a 3rd party authorization server the user identity that originated from the idp. The user's access_token is less useful because it represents the user's capabilities at the IDP authz server.

But to this point, because MCP scenarios may be public clients, should we provide guidance on public client authentication in this PR, or perhaps recommend confidential clients? Given that client authentication is part of the processing rules for the Resource Authz Server when validating the ID-JAG?

Copy link

@sandeepdhankhar sandeepdhankhar Sep 6, 2025

Choose a reason for hiding this comment

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

The risk is that an idToken does not tells anything about how it was obtained, who obtained it, where as an access Token makes the caller a first party since they could have only obtained it with client secret that they had. I may be wrong, still in my opinion an idToken is only good for the first party that obtained it from security perspective

Copy link

Choose a reason for hiding this comment

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

is only good for the first party that obtained it from security perspective

Apologies, I might be confused here on your concern. I beleive what this PR propses is that the id_token is only used in a first-party context - that is, the client application registered with the IdP that requested it, as well as the Idp itself. The exchange between the client and the 3rd party server does not contain any id_token.

@aaronpk
Copy link
Contributor Author

aaronpk commented Oct 30, 2025

This has been accepted and merged as an auth extension: modelcontextprotocol/ext-auth#4

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

Labels

accepted SEP accepted by core maintainers, but still requires final wording and reference implementation. auth security SEP spec

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SEP-990: Enable enterprise IdP policy controls during MCP OAuth flows