MCP OAuth login retries after explicit access_denied when using discovered scopes
Nobody has claimed this yet.
- 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?
-
Configure a streamable HTTP MCP server protected by OAuth.
-
Have its OAuth metadata advertise supported scopes.
-
Add the server to Codex.
-
Run:
codex mcp login <server> -
Click Cancel on the provider’s consent page.
-
Observe that Codex opens another authorization page.
-
Click Cancel again.
-
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:
However, the retry classifier matches every OAuthProviderError when scopes were discovered:
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_scopewith discovered scopes retries once without scopes.access_denieddoes not retry.- Other unrelated OAuth provider errors do not retry.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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