-
Notifications
You must be signed in to change notification settings - Fork 474
[RFC] Update the Authorization specification for MCP servers #284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Server Metadata Discovery to allow the MCP server to respond based on the MCP protocol | ||
version. | ||
MCP clients **SHOULD** include the header `MCP-Protocol-Version: <protocol-version>` during Server | ||
Metadata Discovery to allow the MCP server to respond based on the MCP protocol version. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have clients telling the server their version, but does the server also need to tell the client their version at any point?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moving versioning out of initialization and into a header could make sense, but it feels like it should be a separate PR since this authorization proposal doesn't directly depend on it (that I can see).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it should be moved out of initialization, but indicating the version early on in the authorization process would allow future changes to be made in a more compatible way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does seem like something that is outside the scope of this specification to be decided (since implications go beyond auth). @dsp-ant - what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few comments to make it closer to OAuth taxonomy
|
||
The discovery flow is illustrated below: | ||
The discovery flow is illustrated below: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the discovery flow should starts at the 401 with WWW-Authenticate
with issuer
value and refer the issuer
value in the next request using the following notation [please not that the discovery endpoint resource location needs to be corrected too ]:
GET /.well-known/openid-configuration HTTP/1.1
Host: <issuer>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The discovery endpoint should be the OAuth Server Metadata endpoint, not OpenID Provider metadata endpoint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if both are supported, is it left to the client?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the client is only doing OAuth to the MCP server, so should only be using the OAuth Server Metadata endpoint. If the authorization server happens to also be an OpenID Connect provider, that’s not really relevant to this protocol, so the client shouldn’t be interacting with the OpenID configuration metadata at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
100% agree. However this would make multiple accounts on same MCP server a bit problematic.
Consider a real world MCP Deployment, TravelMCP (offered by some Travel Aggregator). I have a work profile and a personal account on that provider.
My Claude Desktop client might want to manage both of these accounts for me, if this spec only implements OAuth 2.1 / 2.0 without a /userinfo
endpoint the MCP client cannot communicate to me which profile it has on me. So either this spec would then need to require userinfo
or OIDC instead of just OAuth 2.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@PieterKas Consider Claude Desktop (MCP Client) which would likely have more than one account connected for a service per user (Google?). How does the MCP Client present the information to the user if they were to want to remove one of their account(s).
Your Current Accounts:
-
Google Account 1 [x]
Calendar, Mail -
Google Account 2 [x]
Calendar, Mail, Drive
There is nothing special in this on MCP Client v/s Just an OAuth 2 Client. The specific challenge in case of MCP Clients (agents) would be important when I ask Claude to "Manage my work Calendar" (ofc Claude could ask for this account name at some point during onboarding). In case of Google, the profile information can be obtained by either userinfo endpoints or by obtaining an id_token
.
Since MCP Client developers are not Identity / Security experts, I think how to navigate this might not be immediately obvious to them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider a real world MCP Deployment, TravelMCP (offered by some Travel Aggregator). I have a work profile and a personal account on that provider.
My Claude Desktop client might want to manage both of these accounts for me, if this spec only implements OAuth 2.1 / 2.0 without a
/userinfo
endpoint the MCP client cannot communicate to me which profile it has on me. So either this spec would then need to requireuserinfo
or OIDC instead of just OAuth 2.0.
You would authorize Claude to access resources for each of these accounts. This is not uncommon. Services are granted access to multiple calendars.
Claude wants access to the resource. That is an access token and authorization. Independently Claude can get info about you (what would come from an id_token) and that can come from any OIDC server.
I'm not following your desire to get userinfo from the "accounts"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we are in agreement:
In case of Google, the profile information can be obtained by either userinfo endpoints or by obtaining an id_token.
Independently Claude can get info about you (what would come from an id_token) and that can come from any OIDC server.
I was asking specifically was if there is any recommendation or the entire choice of which protocol to use left to the MCP Client (Agent). Which seems to be yes, in my proof of concept I pick OIDC if supported.
Since MCP Client developers might not fully appreciate the difference of one from the other, I suspect some examples / sdks might have to explain nuances here.
Sidtenote: Although on the flip side a whoami
tool for an LLM should be pretty straightforward to discover.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ciamshrek @dickhardt any follow-ups that we need to include in the spec based on this conversation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the issue of multiple accounts: I had precisely this issue with https://github.com/MarkusPfundstein/mcp-gsuite
The way I "solved" is that I let the user pre-define their accounts in a config file and then inject details about each account into the tool call . This info is used to inform the LLM about relevant context for each account. For instance: "This account is my work account". When the LLM then calls the tools, it can decide which account to use and then can account id as one of the parameters to the tool function.
(It works quite well but is pretty cumbersome and you also get random confusions. Especially when working with more complex scenarios, for instance when an account also has multiple calendars and then the LLM needs to decide which account and which calendar to use. though I'd say this is just an innate problem of the LLMs and not in scope of this RFC.)
So in general, I think it might be good to add a /userinfo
call and let the MCP Client use it to fetch relevant user info that allows the LLM to make sensible choices. Question is if the userinfo provides enough contextual information though.
|
||
The discovery flow is illustrated below: | ||
The discovery flow is illustrated below: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if both are supported, is it left to the client?
Say for example my MCP Server supports a list of tools, How would that be modelled with this spec? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good! Adding some comments.
An MCP client **MUST** check the `resource` identifier against the hostname of the MCP server. An MCP | ||
client **MUST** send the `resource` identifier to the OAuth Provider, following | ||
[RFC8707](https://www.rfc-editor.org/rfc/rfc8707.html). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resource indicators (RFC 8707) are a MAY in OAuth 2.1 - do they need to be a MUST here? Many authorization servers don't yet support resource indicators (or ignore them).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nbarbettini here is a scenario mentioned in a separate discussion (@will-bartlett called it out):
Say, you have an MCP client that is connected to someone's homebrew Spotify MCP server (spotify-notofficial.example.com
) and a Google MCP server (mcp-google.example.com
).
Your homebrew Spotify server (let's say it's not made by Spotify but by some malicious actor) may at some point return a HTTP 401
with the following challenge:
issuer=accounts.google.com&scopes=drive.files.read
Without resource
verification, the client would go "Cool, let me go to Google and acquire a token for Google Drive, that I can then send to this what-looks-like Spotify MCP Server."
As a customer, you don't want that. This is where the resource
in the WWW-Authenticate
comes in, because the MCP client can now also tell the IdP (Google, in this case) - "Looks like the resource
is spotify-notofficial.example.com
that is trying to access Google Drive" and the IdP can reject it and say that this resource is not authorized to access Google Drive.
We should probably have a solution in this spec to this scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, that is a good scenario to illustrate the problem.
Does Google actually support RFC 8707? (hard to tell in their docs)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple other ways to mitigate this threat, if not every AS supports RFC 8707:
- The client could be required to explicitly state the resource-issuer pair to the user, especially if it differs from a previously-seen resource-issuer pair
- MCP servers could be required to publish public metadata (similar to the OAuth AS discovery document) that explicitly states up front what issuer(s) it will use, so that it can't pull a "slight of hand" trick later
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nbarbettini I think (1) is likely the most reasonably straightforward approach to implement and likely something that is also a bit more usable.
"MCP server spotify-notofficial.example.com
is attempting to log in using accounts.google.com
with scopes drive.files.read
. Would you like to continue?"
This basically requires the clients to keep track of some kind of consent mapping, where a user can say "Yep, totally fine - I expect this MCP server to log in with this issuer with these scopes," and then not be prompted again unless the issuer changes, or other scopes are requested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current proposal enables BYO Authorization Server scenarios that could be 1P or 3P. There isn't a requirement that the issuer
have the same domain as the MCP server (resource
). The client has no way of knowing that authorization_servers
returned in the PRM are the intended authorization servers for the tools.
The only constraint that can be enforced is that the value of resource
returned in the PRM resolved from the WWW-Authenticate resource_metadata parameter matches the URL of the MCP request
If the protected resource metadata was retrieved from a URL returned by the protected resource via the WWW-Authenticate resource_metadata parameter, then the resource value returned MUST be identical to the URL that the client used to make the request to the resource server. If these values are not identical, the data contained in the response MUST NOT be used
Since many authorization servers don't support RFC 8707 and its not a requirement in OAuth 2.1 there is no way to ensure that the Authorization Server successfully processed a resource
parameter in the authorization request vs ignoring unknown params. Only authorization servers that have successfully implemented RFC 8707 would return invalid_target
error for a resource
that it didn't understand or unable to process. Many authorization servers will just see the valid scopes and ignore the invalid resource (e.g spotify-notofficial.example.com) and happily return the authorization code to the client.
Hopefully I'm missing something but without putting constraints around the issuer
I don't see how this won't be abused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mcguinness @aaronpk what is your recommendation here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mcguinness @aaronpk what is your recommendation here?
Hi @dsp-ant @localden @nbarbettini , I strongly agree to the original proposal.
As an MCP Server/Authorization Server provider, I'd like to highlight a security risk we need to consider:
Malicious actors could deploy fake MCP Servers mimicking our MCP Server. If victims are tricked into configuring these rogue servers, attackers could obtain valid AccessTokens and subsequently access our genuine MCP Server.
What We recommend for the authorization spec is the following:
- MCP Clients should strictly validate the resource identifier against the MCP Server's hostname Crucially
- MCP Clients MUST transmit this resource identifier to the OAuth Authorization Server during token acquisition, following RFC8707.
This ensures the Authorization Server can verify whether the MCP Client is being impersonated by a phishing server. Without this verification, there's seems no othe reliable way to distinguish between legitimate clients and forged ones.
I believe this would significantly strengthen the security posture of the specification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes requiring the MCP client to pass the resource
param to the AS per 8707 can help , but unfortunately it won't prevent an Authorization Server from silently ignoring the param. I'm OK with requiring the client to add the resource
indicator per 8707 but we would also need to require the AS to reject unknown/invalid resources with a invalid_target
error. This isn't something the client can discover using Authorization Server metadata as 8707 didn't define any AS metadata registrations. Maybe this is something we should take back to the OAuth WG @aaronpk?
We could also choose the most restrictive path similar to same-origin policy in browsers and by default require that MCP clients can only connect to PRM published authorization server if the authorization server has the "same-origin" as the resource. This would significantly constraint deployments of custom MCP Servers but would provide the strongest security posture. MCP clients would need to opt-in to 3rd party authorization severs which could be a policy statement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Requiring the authorization server and the resource server to be same-origin may not be a suitable solution in all cases. While this approach works well for services like PayPal, where both the authorization and resource servers are operated by the same entity, it becomes impractical in future scenarios where an MCP server may not have the capability to implement its own OAuth authorization server.
In such cases, relying on third-party identity providers like Okta is a natural and flexible extension of the OAuth protocol — one of the key reasons behind its widespread adoption. Imposing strict same-origin requirements would undermine this flexibility and hinder broader adoption of the MCP protocol.
@localden I added some comments above that got marked as "outdated" because of your formatting cleanup commit. (Sorry about the race condition) |
@nbarbettini - no worries, I will go through them anyway! |
Small note: "OP" typically refers to the OpenID Provider in the context of OIDC. In OAuth2 terminology, the equivalent role is usually called the Authorization Server. |
Thank you for starting this, I implemented this with draft of the protected resource metadata draft. IMO It would be much more simpler / easier for developers if the Resource Metadata taxonomy was at least compatible with this spec. It would allow tooling built for OAuth 2 clients to be immediately applicable to MCP. |
@dsp-ant Thanks for the proposal. I like OAuth 2.0 Protected Resource Metadata, and think #1 and #2 should go together to have broader compatibility with authorization schemes other than OAuth 2.0. Also, since OAuth 2.0 Protected Resource Metadata has not yet seen wide adoption, I suggest that we write this as "SHOULD" instead of "MUST". |
@fei-yuan - without this being a MUST this will fragment the implementation. We need to make sure that we standardize on one approach for all MCP servers. |
```http | ||
WWW-Authenticate: Bearer resource_metadata="https://resource.example.com/.well-known/oauth-protected-resource", | ||
scope="mcp:read mcp:write" | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about the scenarios where a new authorization scope is needed as part of responding/steaming?
I imagien this is good for initial handshake and such, but what if client is doign a toolcall where the MCP server finds out that it requires additional authorization to continue. I would be great if the spec covers a standalized way tool calls should response with needed authorization similar to www-authenticate over http does?
Else we end up with servers and clients that are made specially to understand eachother and not easy to swap to a new client.
let hasValidUpstreamToken = await hasValidToken(azureAdProvider.id, userId);
if ('error' in hasValidUpstreamToken) {
return { error: "No valid upstream provider tokens available. Authorize the mcp://azure-resoruce-manager scope.\nDetails:"+ hasValidUpstreamToken.error };
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this can be decomposed into two issues:
First issue - how does incremental authorization work if the client is only instructed to request a minimal set of scopes initially? This can still be accomplished with the WWW-Authenticate
header which contains scope + resource params that can guide incremental authorization requests. This RFC describes that process that on line ~110. The problem with the header-based approach over the streamable-http transport is that errors can only be returned in response to the initial HTTP Post message, which means all permission validation needs to be done synchronously. That's not great.
Second issue - how do tools return structured error information? Can we return the equivalent of the WWW-Authenticate
header in a JSON-RPC tool call response error? How does WWW-Authenticate
interact with batching? I don't think this is defined well yet - but I think more structure in error responses should come in a future RFC since it will have ramifications far past Auth concerns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree - its about having a way defined by spec so we dont endup doing different things at the clients to make it work.
Right now i been experimenting with tool error messages and only thing i was able to get to work somewhat consistant was to actualy report the partial authorization url back that the client need to visit:
but i also needed some metadata for my tooling which i at the client level injected into the response content on the client side to all tool call responses.
and finally to give users somewhat okay ui experience i gave the chat app a ui tool that it could use to further improve the flow
opening it in a popup and such (some bugs here but just to show the idea)
So we really would love that to be a spec defined handling and support on library level to avoid to much customization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll say that tool authorization is a separate issue from this spec - as @max-stytch called out, there are questions about things like incremental consent that need to be addressed, but that would fall somewhat out of scope of this particular RFC.
@nbarbettini and @wdawson have been giving a lot of thought to the tool authorization problem and are looking at proposing a few tweaks in the future. Let's take that discussing there when it's ready.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RFC 9470 covers that in very great detail.
instead retrieve credentials from the environment. | ||
- Implementations using alternative transports **MUST** follow established security best | ||
practices for their protocol. | ||
- Streamable HTTP or SSE transports **SHOULD** conform to this specification. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this leaves out just standard HTTP calls (which are supported in the 2025-03-26 spec) with no sse component, which would also want to handle this. The former wording was more permissive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great to see this!
I've left a few questions and suggestions below.
|
||
A MCP client **MAY** be pre-registered with an AS and not require dynamic registration. | ||
|
||
A MCP client **MUST** send the `resource` identifier to the AS, following |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an implicit requirement that the AS should support and implement RFC 8707? There is another point in section 2.4.4 which also implies that. If so, this could be difficult to enforce given that the AS could audience-restrict the token using a different strategy and the outcome would be the same.
[OAuth 2.1 (draft)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-12) authorization | ||
flow after receiving a `HTTP 401 Unauthorized` response. | ||
|
||
### 2.2.1 OAuth Grant Types | ||
|
||
OAuth specifies different flows or grant types, which are different ways of obtaining an | ||
access token. Each of these targets different use cases and scenarios. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is intended for the section below, which I can't tie my comment to.
MCP servers SHOULD support the OAuth grant types that best align with the intended
audience. For instance:
Is this still valid given that MCP servers are now resource servers, which are generally not concerned with the authorization grant type?
|
||
### 2.10 Third-Party Authorization Flow | ||
1. Implementations **MUST** follow OAuth 2.1 security best practices. | ||
2. PKCE is **REQUIRED** for all clients. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This bullet point is already covered by point 1, given that PKCE is required for all clients using the authorization code flow in OAuth 2.1.
Co-authored-by: Eleftheria Stein-Kousathana <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
Co-authored-by: Nick Bell <[email protected]>
Co-authored-by: Tommy Hansen <[email protected]>
|
||
#### 2.9.2 Token Handling | ||
|
||
Resource servers (MCP servers _or_ downstream APIs) **MUST** validate access tokens as described |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider referencing the token validation in the Security Considerations section later in this document, which outlines critical aspects of token validation.
1. MCP servers **MUST** take all necessary steps to ensure no data is returned to unauthorized parties | ||
and **MUST** ensure any credentials are valid before processing the request. For example, a MCP server | ||
could validate inbound tokens through one of the following approaches: | ||
1. Token introspection, according to [RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I look at the rendered version of this markdown, it appears that this subsection of the list is incorrectly rendered, as it doesn't seem to have the correct indentation. It could be a GitHub preview rendering issue though, don't know.
Co-authored-by: Xiao Yijun <[email protected]>
clients to make requests to protected MCP servers on behalf of resource owners. This specification | ||
defines the authorization flow for HTTP-based transports. | ||
|
||
The scope of this document is **user authorization** - how a MCP client can authenticate a user |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to say I am confused reading this right at the start. Apart from the authN/authZ mingling (which one is used to nowadays), OAuth is NOT user authorization. ever. It is
an authorization protocol that allows a client application to request permission to access a protected resource on the resource owner’s behalf [1]
or, as per the specs abstract says itself: It
... enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf...
[2]
What are we talking about here? User authZ oder delegated client authZ?
I think the latter, but read here (and e.g. in some diagrams in 2.58.1) the former.
If I may have one wish: Please avoid this confusion in a spec, where words really matter. When we talk about user authZ, I have multiple thoughts and ideas on how ReBAC- or ABAC-Systems and Drafts such as openID authZEN* could be a starting point to simplify this whole complicatedness a lot and solve multiple problems like the confused deputy on the way. But I think this spec is not about that topic... right?
*: Subjective thought: The authZEN spec is the right direction, XACML is not, and should be deleted from the authZEN implementers draft - but what do I know *shrugs
edit: Also, may I ask why the spec defines scopes when their coarse-grainedness is widely known (and all of this is limited by http header size) which is the reason specs like RaR exist?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I beg to differ a little @DGuhr -- from the client's point of view, the access token represents what the user has authorized the client to do => OAuth is how a client get's user authorization to access the user's resources -- the request from the client is represented by the scope
in the flow. Hence the user authorization is scoped to what the user authorized the client to do with their resources.
This user authorization is a subset of what the user is authorized to do.
I agree that words are important and the term "user authorization" means different things depending on the perspective being taken.
From the user's point of view, it is what the user has been authorized to do -- but in this document, it is what the user has authorized the client to do. Make sense?
wrt. scopes -- fine grained access is impractical to be represented to a user in an OAuth flow for them to understand what is being granted. I bet that fine grained access is only needed at the user level, not at the client level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dickhardt What you're describing here:
[...] from the client's point of view, the access token represents what the user has authorized the client to do => OAuth is how a client get's user authorization to access the user's resources -- the request from the client is represented by the scope in the flow. Hence the user authorization is scoped to what the user authorized the client to do with their resources.
[...]
This user authorization is a subset of what the user is authorized to do.
is exactly the concept of "authorization on behalf of" / delegated access as of my understanding. As such, the appropriate wording made it into several IETF-Specs, because there is a difference and you tap into all kinds of problem classes when OAuth is used for user authZ, as it is inherently not designed to account for it.
I just scanned all the IETF RFCs linked in this spec, and also RFC 6479. The latter is actually the only spec that mentions user authorization (searched for "user authoriz") at all, but only in two sentences where from the context it is clear that clients are meant (e.g. "... end-user authorization for a malicious client").
I am just saying the used wording creates unnecessary ambiguity, that likely leads to wrong usage of a spec family that was not designed to be used for this kind of authZ. I'll give you that OAuth is already widely used for it, with all kinds of resulting problems in the real world. See e.g. the linked article, or ask your llm of choice "where's the problem using OAuth for user authorization?" - I just did, the result was good enough.
In the end all I say is the wording confused me, as it would likely confuse others. I'd wish for it to be changed to adhere to IETF-used wording. Especially, as implementing the (remote) MCP spec w.r.t. authZ already seems to be non-trivial at all for most, which could hurt adoption badly. I personally do not see many customers using varying audiences - though they should. Or DPoP - which was a nice idea (token binding), but where the newest incarnation renders it also open to varying attack vectors. Let alone dynamic client registration and more. In the world that I live in, even ROPC is still used often in 2025.
I think what I want to say is: On top of all the complicated things that already made it into the spec, people shouldn't need to figure out what is meant here with "user authorization".
Happy to agree to disagree here, though.
wrt. scopes -- fine grained access is impractical to be represented to a user in an OAuth flow for them to understand what is being granted. I bet that fine grained access is only needed at the user level, not at the client level.
I think I'm with you here for client authZ, though I'd "not bet on it", given the "generic container for basically everything"-nature of MCP Servers. Time will tell.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @DGuhr -- words are hard and I have found in identity one group of people will have a clear idea of what a term means, and others will have a clear, but completly different meaning.
Good to point out potential areas of confusion. We should root out all areas where we need to be more clear and add clarity and not assume everyone will have the same understanding.
From the OAuth perspective, the user authorization is what is represented by the access token. I'm sure suggestions to improve the text to eliminiate confusion would be appreciated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the more refined optimization of the Auth specification, I have commented with some suggestions
We strongly recommend that all clients implement metadata discovery. This reduces the | ||
need for users to provide endpoints manually or clients to fallback to the defined | ||
defaults. | ||
If the MCP server connects to third-party APIs, the MCP client **MUST NOT** hold tokens for said |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am hesitant about this constraint. I understand the purpose is to decouple the auth from MCP Client to MCP Server and from MCP Server to Third Party API.
From the perspective of ideal engineering standards, I think it makes sense. However, considering the current ecosystem of Remote MCP Servers, it seems a bit impractical. Currently, for example, service providers like Higress/Composio/Zapier that offer Remote MCP Servers, when connecting to Third Party APIs, require users to store their tokens in the configuration of the Remote MCP Server. But users may trust SaaS service providers less than they trust their local MCP Clients. At least, if the token is stored locally, there is no risk of the token being leaked due to a hacker attack on the SaaS provider's database.
My suggestion is to change the MUST NOT here to SHOULD NOT, and clarify that whether the third-party API token should be held by the MCP Client or the MCP Server should be left to the user to decide based on specific circumstances.
I'll provide a specific case. We host a large number of MCP Servers converted from APIs in the Alibaba Cloud API Market at https://mcp.higress.ai. However, when users use these MCP Servers, they need to first subscribe to the API in the Alibaba Cloud API Market, obtain a token, and then store the token with us. Although we can ensure the secure storage of the token without leakage, this is still not as secure as the direct pass-through of the API token.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is more of a reason for a specific API connection flow than expose that through the server authorization specification. Those are fundamentally two different concepts, IMO. One is "I am protecting my server from unauthorized access" and the other is "My server needs to access upstream APIs."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I second @johnlanni here. I expect that a significant amount of Remote MCP Servers will provide access via APIs to their users' data.
to access a MCP server and the MCP server determine whether to authorize the user to access its tools, | ||
resources, or any other capabilities. | ||
|
||
This document **does not discuss server-to-server authorization patterns**. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This explicit scope is great, and I also think it's more reasonable to limit the OAuth2 related specifications described in the document to the user authorization scenario. However, the description in section 2.2.1 here is inconsistent with this scope agreement:
Client Credentials: the client is another application (not a human)
Should the description in section 2.2.1 be removed? I believe Client Credentials should be considered in server-to-server authorization patterns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a fair call-out. I think the grant section alludes to the fact that different grants may be supported between clients and servers. Ultimately, a client may choose to add support for client credentials too (as impractical as that may be for the general case), but this specification does not cover the implementation or architectural implications.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where would be server-to-server authorization be described, if not here?
Hey everyone. Thanks to @localden and everyone else chiming in and helping sorting out the details of how Authorization works. In the background @localden, myself and few others have worked on splitting the PR idea into separate steps to make it more digestible. #338 landed as such today and we will do follows ups with security recommendation, more in depth discussion around DCR, and other parts that are worth touching on. I big thank you to @dickhardt @aaronpk @PieterKas @D-McAdams @fei-yuan @nbarbettini @mcguinness and literally everyone else. I am closing this out in favor of the other PRs towards auth revision. |
Hey folks! Quick plug for the new PR that @wdawson and I put together: User Interaction capability It directly addresses one of the biggest side discussions in this thread: the case of an MCP server that serves tools that require "downstream" authorization (e.g. a Google tool that requires specific Google scopes from the Google authorization server, separate from the AS protecting the MCP server). By adding the ability for the server to request interaction from the end-user, there is a clean path for authz/step-up authz, as well as payments, asking the user for demographic info, etc. Comments and suggestions welcome! |
Introduces an update to the current MCP server authorization specification. The goal of this proposal is to shift the implementation from the MCP server being an OAuth Provider (OP) to a Resource Provider (RP) that uses an external identity provider (IdP) as the "golden path."
Important
Upon ratification of the change, the location of the document will move to the right protocol revision version.
Proposal credits (authors, contributors, and reviewers)
A massive thank you to folks that helped craft, review, and evolve this proposal - @dksmetters, @will-bartlett, @tolginator, @CaitieM20, @annaji-msft, @halter73, @hpsin, @wdawson, @dasiths, @dsp-ant, @TylerLeonhardt, @calclavia, @cliffhall, @aaronpk, @irvinebroque, @freiadr, @jspahrsummers and many other folks who have dedicated their time, attention, and energy to look through this change.
Pull request reviewers
Special shout-out to folks that helped review this PR: @aaronpk, @connor4312, @nbarbettini, @identitymonk, @ciamshrek, @PieterKas, @mcguinness, @max-stytch, @pwwpche, @dickhardt, @blowdart, @pcarleton, @mattchenderson
TL;DR
Compared to the current specification, we suggest:
WWW-Authenicate
instead of a server-hosted metadata document.Motivation and Context
The meta-goal of this specification is to simplify how developers integrate OAuth to protect their MCP servers.
Benefits
How Has This Been Tested?
Tested through prototyping of the current specification vs. the updated one that depends on
WWW-Authenticate
headers for auth-related discovery processes. No production-ready code developed for this specification at this time.Breaking Changes
Customers that wrote code that supports the current MCP server authorization specification (e.g., using the TypeScript SDK, which implements the current specification) may need to update their code. This would apply to both servers and clients.
Types of changes
Additional context
WWW-Authenticate
for authentication rather than just the MCP server's OIDC metadata document #195Follow-ups/open discussion
WWW-Authenticate
and would likely map better to an end-to-end OAuth specification. The current proposal for PRM is in draft, however. Is this a path we'd want to pursue?