openai / openai/codex

MCP OAuth login retries after explicit access_denied when using discovered scopes

Open
#39,789 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 version of Codex CLI is running?

codex-cli 0.148.0

What issue are you seeing?

When codex mcp login uses scopes discovered from OAuth metadata, clicking Cancel on the provider’s consent page launches a second authorization flow.

The authorization server correctly redirects to Codex’s localhost callback:

http://127.0.0.1:<port>/callback/<id>?error=access_denied&error_description=The+user+denied+access&state=<state>

Codex receives and parses this callback. Its local tiny-http server responds:

HTTP 400 Bad Request

OAuth provider returned `access_denied`: The+user+denied+access

Codex then opens a second authorization page. Clicking Cancel a second time finally terminates codex mcp login.

I reproduced this with only one Codex process running.

What steps can reproduce the bug?
  1. Configure a streamable HTTP MCP server protected by OAuth.

  2. Have its OAuth metadata advertise supported scopes.

  3. Add the server to Codex.

  4. Run:

    codex mcp login <server>
    
  5. Click Cancel on the provider’s consent page.

  6. Observe that Codex opens another authorization page.

  7. Click Cancel again.

  8. Observe that the second denial finally terminates the command.

What is the expected behavior?

The first access_denied callback should immediately terminate the OAuth login.

An explicit user denial should not trigger another authorization prompt.

Additional information

The scope fallback introduced in #14419 is intended to retry when a provider rejects discovered scopes:

https://github.com/openai/codex/blob/3ba0f711642a888aec92a611a3f3b2211157ff89/codex-rs/cli/src/mcp_cmd.rs#L256-L313

However, the retry classifier matches every OAuthProviderError when scopes were discovered:

https://github.com/openai/codex/blob/3ba0f711642a888aec92a611a3f3b2211157ff89/codex-rs/codex-mcp/src/mcp/auth.rs#L158-L161

pub fn should_retry_without_scopes(
    scopes: &ResolvedMcpOAuthScopes,
    error: &anyhow::Error,
) -> bool {
    scopes.source == McpOAuthScopesSource::Discovered
        && error.downcast_ref::<OAuthProviderError>().is_some()
}

Because access_denied is represented as an OAuthProviderError, an explicit cancellation incorrectly activates the no-scope fallback.

The second cancellation works because the fallback flow is returned directly and is not retried again.

Suggested fix

Only retry without discovered scopes for provider errors that actually indicate a scope rejection, such as invalid_scope.

Errors representing explicit user intent, especially access_denied, should be returned immediately.

Suggested regression coverage:

  • invalid_scope with discovered scopes retries once without scopes.
  • access_denied does not retry.
  • Other unrelated OAuth provider errors do not retry.

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 in codex-rs/codex-mcp/src/mcp/auth.rs at should_retry_without_scopes, then trace its caller in codex-rs/cli/src/mcp_cmd.rs. Add regression coverage showing that discovered scopes retry only for invalid_scope, while access_denied and unrelated OAuth provider errors terminate immediately.

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
82/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.