-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Support client_credentials flow with JWT and Basic auth #1663
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
New OAuth providers for machine-to-machine authentication: - ClientCredentialsOAuthProvider: For client_credentials with client_id + client_secret - PrivateKeyJWTOAuthProvider: For client_credentials with private_key_jwt (RFC 7523 Section 2.2) - SignedJWTParameters: Helper class for SDK-signed JWT assertions - static_assertion_provider(): Helper for pre-built JWTs from workload identity federation The new providers set client_info directly in constructor, bypassing dynamic client registration which isn't needed for pre-registered machine clients. Deprecate RFC7523OAuthClientProvider: The original implementation incorrectly used RFC 7523 Section 2.1 (jwt-bearer authorization grant) instead of the intended Section 2.2 (private_key_jwt client authentication with grant_type=client_credentials). Also skip 3 flaky timing-dependent tests in test_stdio.py.
b9dae14 to
b007b28
Compare
The base class _initialize() loads client_info from storage, which overwrites any value set in the constructor. Move client_info setup to _initialize override so it's properly set after tokens are loaded. Also update tests to call _initialize() before checking client_info.
- Add missing blank line after function definition (ruff-format) - Add pragma: no cover to mock function not executed in test 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add explicit None checks before accessing client_info attributes to satisfy pyright type narrowing requirements. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Remove pytest.mark.skip decorators that were accidentally added, restoring the file to match main branch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
felixweinberger
left a comment
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.
Left a comment regarding something that's pre-existing, otherwise LGTM
| headers: dict[str, str] = {"Content-Type": "application/x-www-form-urlencoded"} | ||
|
|
||
| # Use standard auth methods (client_secret_basic, client_secret_post, none) | ||
| token_data, headers = self.context.prepare_token_auth(token_data, headers) |
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.
Not an issue with this PR, but something pre-existing @claude pointed out here as a discrepancy between TS & Python:
prepare_token_auth doesn't include client_id for the client_secret_post auth method. According to RFC 6749 both _id and _secret should be in the request body for this method.
TS does that as it does different things for applyBasicAuth and applyPostAuth: https://github.com/modelcontextprotocol/typescript-sdk/blob/de41e4703c937753de5299b11255656efae59bc4/src/client/auth.ts#L310
While Python seems to use prepare_token_auth in both cases.
Might be worth a follow-up?
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.
huh... yea we should fix that! i've got a draft conformance test for it here which should catch this:
modelcontextprotocol/conformance#48
the client credentials one uses client_secret_basic which would be why this didn't catch it
Summary
Implements SEP-1046 OAuth client_credentials flow support with simplified providers for machine-to-machine authentication.
New OAuth Providers
ClientCredentialsOAuthProvider: Forclient_credentialsgrant with client_id + client_secretclient_secret_basic(default) andclient_secret_postauth methodsclient_infodirectly, bypassing dynamic client registrationPrivateKeyJWTOAuthProvider: Forclient_credentialsgrant withprivate_key_jwtauthentication (RFC 7523 Section 2.2)assertion_providercallback that receives the audience (authorization server's issuer identifier per RFC 7523bis) and returns a JWTSignedJWTParameters: Helper class for SDK-signed JWT assertionscreate_assertion_provider()returns a callback for use withPrivateKeyJWTOAuthProviderstatic_assertion_provider(): Helper for pre-built JWTs that don't need the audience parameterDeprecation
RFC7523OAuthClientProvideris now deprecated with aDeprecationWarning.The original implementation incorrectly used RFC 7523 Section 2.1 (jwt-bearer authorization grant where the JWT itself is the authorization) instead of the intended Section 2.2 (private_key_jwt client authentication with
grant_type=client_credentials).Use
ClientCredentialsOAuthProviderorPrivateKeyJWTOAuthProviderinstead.Example Usage
Testing