-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat: support oidc discovery in client sdk #652
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
feat: support oidc discovery in client sdk #652
Conversation
Hi @pcarleton , thanks for the heads-up on the spec change! I've now implemented the corresponding OIDC discovery support in the SDK. Would you mind taking a look when you get a chance? Thanks~ |
0b3f1bc
to
cf64a97
Compare
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.
OICD and OAuth 2.0 AS metadata are no the same. They share a common base. We need to represent this in the type system appropriately so that clients can distinguish what to call.
This implement should have a common base interface that an OAuthMetadataSchema
and an OICDMetadataSchema
that inherit from it and discoverOAuthMetadata
should return a Promise<OAuthMetadataSchema | OICDMetadataSchema | undefined>
, that clients can discriminate on or downcast to the AuthserverBaseSchema
.
src/client/auth.ts
Outdated
const fetchWithCorsFallback = async (url: URL, protocolVersion: string) => { | ||
try { | ||
return await fetch(url, { | ||
headers: { | ||
"MCP-Protocol-Version": protocolVersion | ||
} | ||
}) | ||
} catch (error) { | ||
if (error instanceof TypeError) { | ||
// CORS errors come back as TypeError, try again without protocol version header | ||
return await fetch(url); | ||
} | ||
throw 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.
This should be an async function fetchWithCorsFallback()
and not const fetchWithCorsFallback =
, to stay in line with everything else here and ensure that we define a proper return type.
src/client/auth.ts
Outdated
/** | ||
* The `OAuthMetadataSchema` is compatible with both OIDC and OAuth2 | ||
* discovery responses. Because OIDC's metadata is a superset, `zod` will | ||
* correctly parse the fields defined in our schema and simply ignore any | ||
* additional OIDC-specific fields. | ||
*/ | ||
return OAuthMetadataSchema.parse(await response.json()); |
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.
OICD is not a superset. They have a common base, but they do have both fields that they dont share, AS Metadata has introspection_endpoint
, revocation_endpoint
, introspection_endpoint_auth_methods_supported
and OICD has userinfo_endpoint
, subject_types_supported
, id_token_signing_alg_values_supported
for example.
tried adding more explicit typing here: main...feat-support-oidc-discovery-in-client-sdk |
Hi @dsp-ant , thank you so much for the detailed review. |
Hi @pcarleton , thanks for your input and for highlighting the relevant code. Feel free to push directly to this branch if you'd like to collaborate on the changes. No worries if not, I'm happy to handle the refactoring myself once the spec is ready. |
@dsp-ant in response to the following comments:
The history of how Discovery 1.0 and RFC8414 came to be appears to give their nuance some credibility but at the end of the day they infact describe one and the same entity. Both Discovery 1.0 and RFC8414 responses for the same issuer identifier describe the same Authorization Server, when resolved from a given issuer identifier they ought to return the same metadata.
As a Client module developer my expectation is that I am able to resolve an issuer identifier to the two respective well-known URLs this issuer may host its AS metadata at and race to successful completion of either one. Not having these endpoints return the same response when both are supported by the issuer has lead to client race conditions wrt. missing metadata in the past. Therefore, the only typings related thing that may differ is the optionality of some of the properties, but not the properties themselves. They may (and in fact often do) all appear in both endpoints' responses. |
cf64a97
to
a3fe322
Compare
a3fe322
to
2de25f0
Compare
e0b4ffe
to
bcbaa86
Compare
bcbaa86
to
c148ef1
Compare
Hi @dsp-ant @pcarleton @ihrpr , I hope you're all doing well. The OIDC discovery spec has been merged into the draft authorization specification, and I've now fully implemented the OpenID Connect Discovery support as required by the latest MCP draft authorization specification. When you have a moment, I'd appreciate your review. Thanks! 🙏 |
Adds validation during OIDC provider discovery to ensure S256 code challenge method is supported as required by MCP specification. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Moves fallback logic back to individual functions instead of generating "fake" metadata during discovery. Discovery now returns undefined on 404 like the original code, maintaining the existing architectural pattern where functions handle their own URL fallbacks. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Implements root OAuth discovery fallback when path-aware discovery fails, restoring compatibility behavior from original code. Adds comprehensive RFC documentation for OAuth 2.0 Authorization Server Metadata (RFC 8414) and OpenID Connect Discovery 1.0 path handling rules. Discovery sequence for issuer with path components: 1. OAuth with path insertion (RFC 8414 Section 3.1) 2. OIDC with path insertion (RFC 8414 Section 5 compatibility) 3. OIDC with path appending (OIDC Discovery 1.0 Section 4.1) 4. OAuth root fallback for compatibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
👋 @xiaoyijun I have a few changes to add, but I need you to enable "maintainers can make changes to the branch": Changes are in this branch: |
Hi @pcarleton , I can't see the "Allow edits and access to secrets by maintainers" checkbox, but I'm sure I've checked it when I create this PR. |
Hi @pcarleton , I have merged your commits into this branch. And maybe because I'm using my own fork, so you can't edit this PR. |
Changes discovery sequence to be more logical and consistent: 1. Path-aware OAuth discovery 2. Root OAuth discovery (if path exists) 3. OIDC discovery methods This ensures OAuth methods are exhausted before trying OIDC, providing more predictable fallback behavior. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Removes tests that are now covered by the parameterized discovery sequence tests, eliminating duplication while maintaining full coverage of all discovery scenarios. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
src/client/auth.ts
Outdated
} | ||
|
||
if (!response.ok) { | ||
if (response.status === 404) { |
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.
Can we continue on any 4XX (#804)? The spec doesn't specify that fallbacks should only occur on 404s. I put out an equivalent fix on the Python side just now here: modelcontextprotocol/python-sdk#1193.
I was planning to open a PR for this independently, but that would directly conflict with this PR, I 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.
yea good call, added it in my branch
@xiaoyijun I've added a few more changes in main...pr-652 can you pull those in here? (re: whether the maintainers setting is selected, the only way I know of to check is this view. If ) |
@pcarleton Thanks for the heads up! I’ll make sure to select the “Allow edits from maintainers” option next time. (It seems I wasn’t able to enable this option because of how my branch settings.) I’ve just merged the latest changes from main (pr-652) as well. If there’s anything else, feel free to tag me! |
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.
LGTM
This PR adds comprehensive OpenID Connect (OIDC) Discovery support to the client SDK, implementing the multi-protocol discovery strategy required by the latest MCP draft authorization specification.
🔄 MCP Draft Spec Compliant Discovery Strategy
Implements the exact discovery sequence mandated by the MCP Draft Spec:
✅ Specification Compliance
🆕 New Types and Schemas
🔧 New Discovery Functions
⚡ Enhanced Authorization Flow
Motivation and Context
This change implements the latest MCP draft authorization specification requirements at https://modelcontextprotocol.io/specification/draft/basic/authorization, ensuring the SDK is compliant with the evolving MCP standard.
How Has This Been Tested?
Unit tests have been added and updated, and all of them passed.
Breaking Changes
None. This change is fully backward-compatible:
Types of changes
Checklist