-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feature(auth): Allow delegating OAuth authorization to existing app-level implementations #485
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
base: main
Are you sure you want to change the base?
feature(auth): Allow delegating OAuth authorization to existing app-level implementations #485
Conversation
3352fce
to
96f19fc
Compare
b0e2654
to
1bf3a74
Compare
7758221
to
02f8659
Compare
59b8e7f
to
e544126
Compare
Hi @ihrpr , Sorry for the direct tag. I really appreciate that this is already on the HPR list, and I completely understand you have a lot on your plate. This is just a gentle nudge on the PR - a decision here would really help me move forward on my side, especially if the change is accepted. Thanks again for all the amazing work on the SDK! It’s been a real pleasure working with it over the past three months ;-) |
b8b1a68
to
96d2b31
Compare
One additional detail worth mentioning: the OAuth implementation used by my company (and others as well 😉) includes JWT tokens in the final authentication response. These tokens encode valuable metadata such as user identity, organization context, and more. This is yet another reason to allow client implementations to fully control the authentication flow - they may want to extract and act on this information in ways that are specific to their environment. |
aa7a7b1
to
2be7d47
Compare
👋 Hello, I just wanted to follow up on this PR. I’ve been keeping it up to date over the past month, including adapting to changes like the recent addition of protected resource support (RFC 8707), which this PR now explicitly handles. I really appreciate that it was marked as an HPR and I took that as a sign that it might be reviewed soon. I also sent a (hopefully gentle) nudge to @ihrpr at the time, just to make sure it was on the radar. My main reason for commenting now is to ask for a bit of clarity: I’m more than happy to continue monitoring and updating this PR for as long as there’s a reasonable chance it might be reviewed and potentially merged. I sincerely believe it improves the SDK’s OAuth2 support and brings tangible value to the community. That said, I totally understand if this PR isn’t likely to be accepted either for technical or strategic reasons. However, in this case, I’d rather step back than keep chasing changes unnecessarily. Regardless, thanks again for all the work you do on this project! |
dd35f24
to
9d0539e
Compare
Hi @m-paternostro thanks for providing more context. Are you able to share a snippet of what your: Also, is this a client targeting a single MCP server? or is it meant to be used for any user provided MCP server? The overall message I'm getting is "I've already got a function that does the auth flow i need, please let me uses it, and don't make me re-write it / re-test it etc. just to fit the more generic flow that I don't need." Is that right? Some priorities in my mind are:
In general we want MCP clients and servers to be compatible with each other as much as is reasonable. For security, standardized auth flows means its easier to get fixes deployed more quickly if there are fewer implementations of the same critical code. And for API surface maintainability, we want to be able to quickly reason about whether a given change is breaking or not. Weighing all those together, and if my understanding above is correct (pending the snippet etc.) I'd prefer we delegate at a higher level. That makes this function easier to reason about, and introduces fewer cases where odd interactions might happen. If we add a parameter, or change metadata discovery, I don't want to have to decide whether that comes above or below the delegation call. Similarly, it should be clear it's fully up to the delegating implementer to implement the security best practices, it shouldn't be a mix of responsibility. In terms of a higher level, this could mean:
I'm leaning towards 401 interception as being a more complete solution to this issue, wdyt? |
Really my pleasure. Thank you for your time!
I forgot you asked this on your first comment. Sorry. Here you are:
This is being used when targeting MCP Servers that are available on an already authenticated backend.
Not at the moment - for now we only do this for very specific servers.
Pretty much ;-) I would just rephrase the "re-write it" to emphasize the goal of not duplicating it, and ending up with "similar but slightly different" implementations, one for the extension and another for MCP server. Btw, besides the code duplication, a more pressing issue for us would be to ensure that both flows are always in sync - without going into too many details, our users need to switch between "organizations" and that's imply managing different live OAuth sessions.
Nice list.
All points make total sense.
I certainly agree with moving the call to Some ideas regarding the other concerns...
Hmmm... Not a bad call although I see 3 potential issues:
Let me know your thoughts. I can update the code based on whatever is decided here. |
9d0539e
to
e500c2b
Compare
Good callout, you're right, it'd also need to be able to provide tokens.
I think is fine, it can be the standard way we respond to headers. As in, we'll need to handle unauthorized anyway, so seems fine to do it in a standard "hook" way that's applicable across http-based transports (this is http header handling anyway)
this is a plus in my mind since it would mean we have consistent handling across both SSE and streamable, and would be harder to miss spots. I think we currently miss at least one spot where we should be setting resource_metadata_url, and the intention is to also handle imo the higher level override is the more flexible option to allow full customization for folks that want the underlying jsonrpc handling, but need full control over auth. if you're up for coding that'd up, would be happy to review. |
This all make great sense. I will implement it - I should have the PR ready for review soon (no later than Thursday) |
e500c2b
to
0ac8051
Compare
👋 Hello, I've updated to code based on the ideas we've discussed. I've also rewrote the PR description to reflect the changes: the first paragraph and the 'Additional context' section should be enough to convey what is going on - the rest of the text is more about the "why" ;-) @pcarleton let me know what you think! |
eb43d42
to
48d00de
Compare
hey thanks for this. I'll be away for a week, and will take a closer look once I'm back. |
5c574af
to
af6fe5c
Compare
👋 Hello @pcarleton, Hope you had a great week off! We all need to recharge now and then - I’ll actually be off for the next two weeks starting Monday. If you get a chance to take a look at the changes before Friday and spot anything we should tweak, I’ll do my best to fit it in before I go. Let me know! |
063bbec
to
1cab5a6
Compare
An optional provider that can be passed to the SSE and StreamableHttp client transports in order to completely delegate the authentication to an external system.
1cab5a6
to
c536db1
Compare
Hey @m-paternostro sorry for the delay here, this is on my list, just didn't get to it this week. |
hey @m-paternostro thanks for the patience here. I had a long chat with some other folks (@ihrpr , @dsp-ant, @ochafik ) and we're thinking that an even higher level approach would be better here, and I wanted to see if it'd work for you and if you'd be up for adjusting this PR to tackle it. It'd look something like this:
The idea is then we could support any type of custom auth there, or other request/response munging. Let me know what you think. |
An optional provider that clients can use whenever the authentication should be delegated to an existing implementation.
This PR introduces a new optional
DelegatedAuthClientProvider
interface. It allows clients to delegate authentication to existing systems when they already manage authorization through another mechanism (e.g. platform tokens, ambient credentials, preexisting identity systems). When provided, this provider takes precedence over the standard OAuth flow and gives control to the host application for both header injection and authentication handling.Motivation and Context
Some applications embedding the MCP SDK already have fully functional authorization systems. In such cases, the SDK’s built-in OAuth flow can be redundant or even problematic - especially when the app simply needs to know when authorization is required, not how to perform it.
Prior to this change, the only way to hook into the authorization process was by subclassing
StreamableHTTPClientTransport
and/orSSEClientTransport
and overriding enough methods to reimplement the auth flow. However, because the relevant methods are private and deeply interwoven, doing so required replicating a significant amount of transport code - leading to maintenance burden and fragile overrides.This change introduces a clean, focused interface for delegating authentication to external systems without needing to reimplement transport logic. The
DelegatedAuthClientProvider
is implemented directly in both transport classes and takes precedence overOAuthClientProvider
when both are provided.How Has This Been Tested?
The implementation was validated with comprehensive unit tests covering both
StreamableHTTPClientTransport
andSSEClientTransport
. Tests verify header injection, precedence over OAuth providers, 401 handling with successful reauth, and error handling when authentication fails.Breaking Changes
No: the new method is purely opt-in, backward-compatible, and safely ignored if unimplemented. It’s designed to be as simple and low-friction as possible while avoiding the need to subclass transports or bypass internal behavior.
Types of changes
Checklist
Additional context
Notes about the changes:
DelegatedAuthClientProvider
interface is defined insrc/client/auth.ts
withheaders()
andauthorize()
methods.StreamableHTTPClientTransport
andSSEClientTransport
support the optionaldelegatedAuthProvider
option.delegatedAuthProvider
takes precedence overauthProvider
for all authentication operations.