fix(auth): add Accept: application/json header to OAuth token requests#2754
fix(auth): add Accept: application/json header to OAuth token requests#2754sohammmmm10 wants to merge 1 commit into
Conversation
Token exchange and refresh requests only set Content-Type but omit the Accept header. Some OAuth providers (e.g. GitHub) return form-encoded data by default and require Accept: application/json to return JSON. Since _handle_token_response parses the body as JSON, omitting the Accept header causes parse failures with these providers. Add Accept: application/json to both _exchange_token_authorization_code and _build_refresh_token_request. Fixes modelcontextprotocol#1523
StantonMatt
left a comment
There was a problem hiding this comment.
I checked the request builders that feed the token-response parser. This branch now sets Accept: application/json for the authorization-code exchange and refresh-token requests, including after prepare_token_auth(), but the same token endpoint content-negotiation issue still remains in the client-credentials extension paths.
Local probe on this head:
authorization_code: Accept='application/json'; Content-Type='application/x-www-form-urlencoded'
refresh_token: Accept='application/json'; Content-Type='application/x-www-form-urlencoded'
client_credentials: Accept=None; Content-Type='application/x-www-form-urlencoded'
private_key_jwt: Accept=None; Content-Type='application/x-www-form-urlencoded'
ClientCredentialsOAuthProvider._exchange_token_client_credentials() and PrivateKeyJWTOAuthProvider._exchange_token_client_credentials() both build /token requests and their responses still flow through _handle_token_response(), so a provider that defaults non-JSON token responses unless Accept: application/json is present can still hit the same parse failure there. If the intended scope is all SDK token endpoint requests, those two builders need the same header too.
Focused local checks passed:
uv run --frozen pytest tests/client/test_auth.py::TestOAuthFallback::test_token_exchange_request_authorization_code tests/client/test_auth.py::TestOAuthFallback::test_refresh_token_request tests/client/test_auth.py::TestOAuthFallback::test_basic_auth_token_exchange tests/client/test_auth.py::TestOAuthFallback::test_basic_auth_refresh_token tests/client/auth/extensions/test_client_credentials.py::TestClientCredentialsOAuthProvider::test_exchange_token_client_credentials tests/client/auth/extensions/test_client_credentials.py::TestClientCredentialsOAuthProvider::test_exchange_token_client_secret_post_includes_client_id tests/client/auth/extensions/test_client_credentials.py::TestPrivateKeyJWTOAuthProvider::test_exchange_token_client_credentials -quv run --frozen ruff check src/mcp/client/auth/oauth2.py src/mcp/client/auth/extensions/client_credentials.py tests/client/test_auth.py tests/client/auth/extensions/test_client_credentials.pygit diff --check origin/main...HEAD
Token exchange and refresh requests only set Content-Type but omit the Accept header. Some OAuth providers (e.g. GitHub) return form-encoded data by default and require Accept: application/json to return JSON. Since _handle_token_response parses the body as JSON, omitting the Accept header causes parse failures with these providers.
Add Accept: application/json to both _exchange_token_authorization_code and _build_refresh_token_request.
Fixes #1523
Motivation and Context
How Has This Been Tested?
Breaking Changes
Types of changes
Checklist
Additional context