Skip to content

Fix client_secret not sent during OAuth token exchange#137

Open
plehoux wants to merge 1 commit into
patvice:mainfrom
plehoux:feat/client-auth-methods
Open

Fix client_secret not sent during OAuth token exchange#137
plehoux wants to merge 1 commit into
patvice:mainfrom
plehoux:feat/client-auth-methods

Conversation

@plehoux

@plehoux plehoux commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

When a server returns a client_secret during dynamic registration but omits token_endpoint_auth_method, the gem was defaulting to "none" and silently dropping the secret from all token requests. This breaks any MCP server that expects client_secret in the POST body (e.g. Canny).

Per RFC 7591 §2, the default is "client_secret_basic", but in practice most MCP servers only support "client_secret_post". The fix:

  • client_registrar: infer auth method from the registration response — if client_secret is present but token_endpoint_auth_method is not, default to "client_secret_post" instead of "none"
  • token_manager: replace add_client_secret_if_needed with a proper apply_client_auth! that supports both client_secret_post (body) and client_secret_basic (HTTP Basic header per RFC 6749 §2.3.1)
  • token_manager: route all token requests (auth code, client creds, refresh) through the same auth method so the secret is always sent when it should be

Discovered and tested against Canny's MCP server (https://canny.io/api/mcp), which returns client_secret during registration but omits token_endpoint_auth_method. Token exchange was failing with 400 "invalid input" because the secret was never sent.

To test Canny MCP server you need a paid account, but I can ask the co-founder to create you a test account.

When a server returns a client_secret during dynamic registration but
omits token_endpoint_auth_method, the gem was defaulting to "none" and
silently dropping the secret from all token requests. This breaks any
MCP server that expects client_secret in the POST body (e.g. Canny).

Per RFC 7591 §2, the default is "client_secret_basic", but in practice
most MCP servers only support "client_secret_post". The fix:

- client_registrar: infer auth method from the registration response —
  if client_secret is present but token_endpoint_auth_method is not,
  default to "client_secret_post" instead of "none"
- token_manager: replace add_client_secret_if_needed with a proper
  apply_client_auth! that supports both client_secret_post (body) and
  client_secret_basic (HTTP Basic header per RFC 6749 §2.3.1)
- token_manager: route all token requests (auth code, client creds,
  refresh) through the same auth method so the secret is always sent
  when it should be
@plehoux

plehoux commented Mar 27, 2026

Copy link
Copy Markdown
Contributor Author

Hi @patvice, any chance you can check this? Anyway I can make this easier for you?

@judy

judy commented May 24, 2026

Copy link
Copy Markdown

Confirming the same bug against a second production MCP provider, Mercury. I discovered it yesterday from a real OAuth flow in a Rails app I run. Same DCR shape as Canny: Mercury returns a client_secret in the registration response but omits token_endpoint_auth_method. Pre-patch, every token exchange failed with OAuth error invalid_client: Client secret is required.

Applied your patch verbatim as an initializer-level monkey-patch. Mercury connects cleanly end-to-end and the credential lifecycle completes normally.

Two empirical data points worth recording:

  1. The ClientRegistrar default-inference change is the load-bearing fix. Mercury’s stored client_info_data had client_secret set but no token_endpoint_auth_method key at all. Exactly the shape this patch targets.
  2. Mercury accepts client_secret_post (your default-when-missing choice). I was prepared to fall back to client_secret_basic if Mercury needed it; turned out unnecessary. The pragmatic default matches Mercury in addition to Canny.

@patvice: I would love to see this merged. Two independent production reproducers (Canny via @plehoux, Mercury via me) plus a small fix that mirrors the existing client_secret_post path makes this a low-risk patch unblocking a real cluster of strict MCP providers. Happy to add tests or rebase if helpful.

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.

2 participants