openai / openai/codex

MCP OAuth DCR: client_secret_post is incorrectly sent as HTTP Basic

Open
#40,928 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

auth bug CLI mcp
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

What issue are you seeing?

Description

When authenticating an MCP server using OAuth Dynamic Client Registration (DCR), Codex does not honor the token_endpoint_auth_method returned by the authorization server.

If the DCR response specifies:

{
  "client_id": "<redacted>",
  "client_secret": "<redacted>",
  "token_endpoint_auth_method": "client_secret_post"
}

Codex sends the client credentials using an HTTP Authorization: Basic ... header. It also omits client_id and client_secret from the token request body.

For client_secret_post, the credentials should instead be included as form parameters in the token request body.

The authorization-code flow and local callback complete successfully, but the subsequent token exchange fails with:

Error: failed to handle OAuth callback
Caused by:
OAuth token exchange failed: Server returned error response:
invalid_request: Missing required parameters

The same MCP server authenticates successfully with Claude and LibreChat using OAuth 2.0.

Steps to reproduce

  1. Configure an MCP server whose OAuth authorization server supports Dynamic Client Registration.

  2. Have the DCR endpoint return a client registration containing:

    • client_id
    • client_secret
    • token_endpoint_auth_method: client_secret_post
  3. Run:

    codex mcp login <server-name> --oauth-client-registration dcr
    
  4. Complete authorization in the browser.

  5. The browser reports that authorization was completed.

  6. Inspect the request sent by Codex to the token endpoint.

Actual behavior

Codex sends a request equivalent to:

POST /token
Authorization: Basic <redacted>
Content-Type: application/x-www-form-urlencoded

With a body similar to:

grant_type=authorization_code
&code=<redacted>
&redirect_uri=http://127.0.0.1:<port>/callback/<id>
&code_verifier=<redacted>
&resource=<mcp-resource>

The body does not contain client_id or client_secret.

The authorization server rejects the request because it expects client credentials in the form body, as specified by client_secret_post.

Expected behavior

When the DCR response specifies:

token_endpoint_auth_method=client_secret_post

Codex should send the token request without HTTP Basic client authentication and include the credentials in the form body:

grant_type=authorization_code
&code=<redacted>
&redirect_uri=<redirect-uri>
&code_verifier=<redacted>
&client_id=<client-id>
&client_secret=<client-secret>
&resource=<mcp-resource>

Controlled reproduction

I reproduced the behavior with a controlled OAuth server that captures the token request:

  • When the DCR response returns client_secret_post, Codex sends the credentials using HTTP Basic.
  • When only the returned method is changed to client_secret_basic, Codex sends the same HTTP Basic authentication and the exchange succeeds.

This suggests that Codex currently treats client_secret_post as client_secret_basic, rather than changing how the client credentials are encoded.

Additional context

The DCR request generated by Codex asks for:

{
  "token_endpoint_auth_method": "none"
}

However, the authorization server registers the client with a secret and returns:

{
  "token_endpoint_auth_method": "client_secret_post"
}

Codex appears to recognize that client authentication is required, but uses HTTP Basic instead of the authentication method returned in the DCR response.

The issue was initially observed with an InHire MCP server. The same server and user account work with Claude and LibreChat.

Related issues:

  • #40783
  • #35006

Environment

  • ChatGPT desktop app: 26.820.60940
  • Bundled Codex CLI: 0.150.0-alpha.8
  • OS: macOS 26.6.2 (25G83)
  • Architecture: Apple Silicon (arm64)
What steps can reproduce the bug?
  1. Configure an MCP server that uses OAuth 2.0 with Dynamic Client Registration (DCR).

  2. Configure the DCR endpoint to return:

    {
      "client_id": "<client-id>",
      "client_secret": "<client-secret>",
      "token_endpoint_auth_method": "client_secret_post"
    }
    
  3. Register the MCP server in Codex and run:

    codex mcp login <server-name> --oauth-client-registration dcr
    
  4. Complete the authorization flow in the browser. The browser displays Authorization completed.

  5. Inspect the request sent by Codex to the OAuth token endpoint.

  6. Observe that Codex sends the client credentials through an Authorization: Basic <redacted> header and does not include client_id or client_secret in the form body.

  7. The token endpoint rejects the request with:

    invalid_request: Missing required parameters
    

The bug can also be isolated by changing only the DCR response from client_secret_post to client_secret_basic. Codex sends HTTP Basic authentication in both cases, but the exchange succeeds only for client_secret_basic.

What is the expected behavior?

No response

Additional information

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the codex mcp login <server-name> --oauth-client-registration dcr flow and inspect the token request after authorization. Reproduce with a DCR response using token_endpoint_auth_method=client_secret_post, then verify the request omits HTTP Basic authentication and includes client_id and client_secret as form parameters, while client_secret_basic continues to work.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
authentication, cli
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.