Summary
codex mcp login <server> --oauth-client-registration dcr does not follow the absolute resource_metadata URL advertised in the MCP server's 401 WWW-Authenticate challenge. Instead, it probes only .well-known metadata paths on the MCP resource origin. When those paths return 404, Codex falls back to dynamic registration on the MCP origin (/register) rather than discovering the distinct authorization server and its advertised registration_endpoint.
This breaks deployments where:
- the MCP resource server and OAuth authorization server are different origins;
- the resource server advertises protected-resource metadata through an absolute, centralized URL in
WWW-Authenticate; and
- the MCP resource origin intentionally does not serve
.well-known/oauth-protected-resource itself.
Other MCP clients that follow the challenge's resource_metadata URL complete the flow.
Environment
- Codex CLI 0.152.1 (
rust-v0.152.1)
- macOS arm64
codex mcp login oauth-repro --oauth-client-registration dcr
Minimal repro
The self-contained executable reproducer is in the first comment below. Save it as codex_mcp_oauth_dcr_repro.py and run:
python3 codex_mcp_oauth_dcr_repro.py
It starts three loopback-only servers:
- MCP resource server:
/mcp returns 401 with an absolute resource_metadata URL.
- Central protected-resource-metadata server: returns an
authorization_servers value pointing at server 3.
- OAuth authorization server: advertises
/oauth2/register as its registration_endpoint.
The authorization server's DCR handler deliberately returns:
{"probe":"advertised-registration-endpoint"}
so a request reaching it is unambiguous and does not begin a browser OAuth flow.
Actual result from 0.152.1:
Error: Registration failed: Dynamic registration failed: Registration failed:
HTTP 404 Not Found: {"probe":"resource-not-found"}
The mock request log contains no request to the centralized PRM server and no request to the authorization server. It shows only requests to the MCP resource origin, followed by:
Expected behavior
Codex should follow the absolute resource_metadata URL from the WWW-Authenticate challenge, fetch its authorization_servers metadata, then post the DCR request to the full registration_endpoint advertised by that authorization server.
Why this appears inconsistent with the current code
codex-rs/rmcp-client/src/oauth_client_registration.rs constructs an AuthorizationManager and calls resolve_metadata(). The same release's oauth_client_registration_tests.rs includes verified_issuer_can_delegate_authorization_and_token_to_one_origin, which verifies a DCR POST is sent to a delegated authorization-server endpoint.
The reproducer suggests the direct mcp login discovery path does not preserve or use the initial WWW-Authenticate challenge's resource_metadata URL when resolving metadata. That occurs before the delegated-registration test coverage.
Related issues
Summary
codex mcp login <server> --oauth-client-registration dcrdoes not follow the absoluteresource_metadataURL advertised in the MCP server's401 WWW-Authenticatechallenge. Instead, it probes only.well-knownmetadata paths on the MCP resource origin. When those paths return404, Codex falls back to dynamic registration on the MCP origin (/register) rather than discovering the distinct authorization server and its advertisedregistration_endpoint.This breaks deployments where:
WWW-Authenticate; and.well-known/oauth-protected-resourceitself.Other MCP clients that follow the challenge's
resource_metadataURL complete the flow.Environment
rust-v0.152.1)codex mcp login oauth-repro --oauth-client-registration dcrMinimal repro
The self-contained executable reproducer is in the first comment below. Save it as
codex_mcp_oauth_dcr_repro.pyand run:It starts three loopback-only servers:
/mcpreturns401with an absoluteresource_metadataURL.authorization_serversvalue pointing at server 3./oauth2/registeras itsregistration_endpoint.The authorization server's DCR handler deliberately returns:
{"probe":"advertised-registration-endpoint"}so a request reaching it is unambiguous and does not begin a browser OAuth flow.
Actual result from 0.152.1:
The mock request log contains no request to the centralized PRM server and no request to the authorization server. It shows only requests to the MCP resource origin, followed by:
Expected behavior
Codex should follow the absolute
resource_metadataURL from theWWW-Authenticatechallenge, fetch itsauthorization_serversmetadata, then post the DCR request to the fullregistration_endpointadvertised by that authorization server.Why this appears inconsistent with the current code
codex-rs/rmcp-client/src/oauth_client_registration.rsconstructs anAuthorizationManagerand callsresolve_metadata(). The same release'soauth_client_registration_tests.rsincludesverified_issuer_can_delegate_authorization_and_token_to_one_origin, which verifies a DCR POST is sent to a delegated authorization-server endpoint.The reproducer suggests the direct
mcp logindiscovery path does not preserve or use the initialWWW-Authenticatechallenge'sresource_metadataURL when resolving metadata. That occurs before the delegated-registration test coverage.Related issues
codex mcp loginfails with "No authorization support detected" on macOS against a spec-compliant OAuth server (same version works on Linux) #34684: macOS Codex OAuth discovery failure while other MCP clients work on the same machine.