Skip to content

feat(oauth): select token_endpoint_auth_method from AS metadata (RFC 8414)#62

Merged
shigechika merged 2 commits intomainfrom
feat/token-endpoint-auth-method
May 7, 2026
Merged

feat(oauth): select token_endpoint_auth_method from AS metadata (RFC 8414)#62
shigechika merged 2 commits intomainfrom
feat/token-endpoint-auth-method

Conversation

@shigechika
Copy link
Copy Markdown
Owner

Summary

  • Read token_endpoint_auth_methods_supported from RFC 8414 AS metadata and dynamically pick the best supported auth method for the token endpoint (preference: noneclient_secret_postclient_secret_basic)
  • Implement RFC 6749 §2.3.1 client_secret_basic: credentials sent as Authorization: Basic base64(percent_encode(id):percent_encode(secret)) instead of in the request body
  • Apply the selected method consistently across DCR, code exchange, token refresh, and Device Authorization Grant polling
  • Store the selected method in TokenData.token_endpoint_auth_method so refresh reuses it without re-reading the discovery document
  • Falls back to "none" with a warning when AS advertises no methods recognized by mcp-stdio (e.g. private_key_jwt only)

Motivation

Fixes compatibility with Microsoft Entra ID v2 and other enterprise OIDC providers that publish only client_secret_basic in token_endpoint_auth_methods_supported. Those servers reject requests that send credentials in the POST body (client_secret_post) or omit them entirely (none).

mcp-remote hardcodes token_endpoint_auth_method: none and always sends credentials in the POST body (mcp-remote#184, mcp-remote#217).

Changes

  • token_store.py: add token_endpoint_auth_method: str = "none" field to TokenData (backward-compatible default)
  • oauth.py:
    • OAuthMetadata: capture token_endpoint_auth_methods_supported
    • _pick_token_endpoint_auth_method(): preference-order selection helper
    • ClientRegistration.auth_method: carry selected method from DCR
    • exchange_code, refresh_access_token: accept and apply auth_method; Basic auth branch adds Authorization header and excludes credentials from body
    • _token_response_to_data: store auth_method in TokenData
    • refresh_cached_token, _run_authorization_flow, _run_device_authorization_flow: thread auth_method end-to-end
  • WORKAROUNDS.md: document the mcp-remote regression this fixes
  • README.md / README.ja.md: update RFC 7591 bullet; add RFC 6749 §2.3.1 bullet

Test plan

  • pytest tests/ -v — 390 tests pass
  • TestPickTokenEndpointAuthMethod — 8 tests covering preference order, None input, fallback + warning for unsupported methods
  • TestDiscoverMetadataAuthMethods — 2 tests for field parsing from discovery
  • TestExchangeCodeBasicAuth — 3 tests: Basic header present, percent-encoding, client_secret_post body
  • TestRefreshTokenBasicAuth — 2 tests: Basic header and post method on refresh
  • TestRegisterClientAuthMethod — 3 tests: DCR picks correct method
  • TestTokenEndpointAuthMethodPersistence — 4 tests including legacy-token backward compat and round-trip refresh

🤖 Generated with Claude Code

shigechika and others added 2 commits May 7, 2026 22:18
…8414)

Read `token_endpoint_auth_methods_supported` from RFC 8414 Authorization
Server Metadata and pick the best supported auth method for the token
endpoint (preference: none → client_secret_post → client_secret_basic).
Apply the selected method consistently across DCR, code exchange, token
refresh, and Device Authorization Grant polling.

RFC 6749 §2.3.1 `client_secret_basic` sends credentials as
`Authorization: Basic base64(percent_encode(id):percent_encode(secret))`
instead of in the request body.  The selected method is stored in
`TokenData.token_endpoint_auth_method` so refresh reuses it without
re-reading the discovery document.

Fixes compatibility with Microsoft Entra ID v2 and other enterprise OIDC
providers that publish only `client_secret_basic` in
`token_endpoint_auth_methods_supported` and reject `none`/body-credential
requests.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Verify that client_secret_basic sends credentials in the Authorization
header (not the request body) for the device authorization request
itself (Step 1, RFC 8628 §3.1), not just the token polling loop.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@shigechika shigechika merged commit 9e6feff into main May 7, 2026
0 of 6 checks passed
@shigechika shigechika deleted the feat/token-endpoint-auth-method branch May 7, 2026 13:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant