MCP transport does not implement OAuth step-up authentication for 403 insufficient_scope

Open 💬 3 comments Opened Apr 30, 2026 by AtharvaPatil-at

What version of Codex CLI is running?

0.125.0

What subscription do you have?

Enterprise

Which model were you using?

gpt-5.5

What platform is your computer?

Darwin 25.4.0 arm64 arm

What terminal emulator and version are you using (if applicable)?

_No response_

What issue are you seeing?

When an MCP server returns 403 Forbidden with insufficient_scope, Codex surfaces it as an error and stops. The MCP authorization spec describes a step-up authentication flow for this case: the client should parse the WWW-Authenticate header, determine the required scopes, and re-initiate the OAuth flow to obtain a token with sufficient permissions.

The WWW-Authenticate header in the 403 response already contains everything needed:

WWW-Authenticate: Bearer error="insufficient_scope",
                         error_description="Insufficient scope",
                         resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource",
                         scope="data.records:read data.records:write schema.bases:read ..."

What steps can reproduce the bug?

  1. Configure an MCP server protected by OAuth where the current token has limited scope.
  2. Invoke a tool that requires a scope the token does not have.
  3. Observe that Codex errors out rather than prompting the user to re-authenticate with the required scopes.

What is the expected behavior?

On 403 Forbidden with error="insufficient_scope", Codex should:

  1. Parse the WWW-Authenticate header to extract the required scope and resource_metadata.
  2. Re-initiate the OAuth authorization flow requesting the additional scopes.
  3. Retry the tool call with the new token.

This is analogous to the existing 401 handling in http_client_adapter.rs, which already returns Err(AuthRequired(...)) when a WWW-Authenticate header is present. 403 with insufficient_scope should follow the same pattern.

Additional information

The fix would be in codex-rs/rmcp-client/src/http_client_adapter.rs. The 401 case already does:

https://github.com/openai/codex/blob/6014b6679ffbd92eeddffa3ad7b4402be6a7fefe/codex-rs/rmcp-client/src/http_client_adapter.rs#L136-L143

A 403 handler should be added alongside it:

StatusCode::FORBIDDEN => {
   if let Some(www_auth) = response.headers.get("WWW-Authenticate") {
      // Parse scope from www_auth and trigger step-up auth.
      return Err(StreamableHttpError::InsufficientScope(...));
   }
}

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗