`codex mcp login` fails with "No authorization support detected" on macOS against a spec-compliant OAuth server (same version works on Linux)
Summary
codex mcp login <server> fails instantly with Error: No authorization support detected
on macOS (arm64) against a fully spec-compliant OAuth 2.0 authorization server.
The same codex version completes the entire flow on Linux against the same server
(discovery → dynamic client registration → browser → token). Other MCP clients
(Claude Code, mcp-remote) complete OAuth against this server without issues —
including mcp-remote running on the same Mac, which rules out the machine's
network/TLS stack and the server itself.
A second, independent defect (RFC 9207 iss dropped when relaying the OAuth callback)
was found while debugging; see the last section. Happy to split it into its own issue.
Environment
- codex-cli 0.144.6 (pins rmcp 1.8.0 per
codex-rs/Cargo.lockof tagrust-v0.144.6) - macOS: 26.5.2, BuildVersion 25F84;
uname -m=arm64(Mac mini) - Install: official binary at
~/.local/bin/codex; **also reproduced with the pristine
release tarball** codex-aarch64-apple-darwin.tar.gz from the rust-v0.144.6 GitHub
release, executed directly
- No proxies:
env | grep -i proxyis empty;scutil --proxyshows only the default
ExceptionsList (*.local, 169.254/16), no HTTP/HTTPS/SOCKS/PAC
- Reproduced with a fresh
CODEX_HOME(empty directory), so user config is not involved RUST_LOG=debug codex mcp login <server>prints nothing beyond the error itself
Repro steps (macOS)
mkdir -p ~/codex-test && cd ~/codex-test
curl -LO https://github.com/openai/codex/releases/download/rust-v0.144.6/codex-aarch64-apple-darwin.tar.gz
tar xzf codex-aarch64-apple-darwin.tar.gz
mkdir home && export CODEX_HOME=~/codex-test/home
./codex-aarch64-apple-darwin mcp add sparrow --url https://mcp.example.com/mcp
# → Added global MCP server 'sparrow'.
# → Detected OAuth support. Starting OAuth flow…
# → Error: No authorization support detected
./codex-aarch64-apple-darwin mcp login sparrow
# → Error: No authorization support detected
(Hostname sanitized throughout: mcp.example.com = a self-hosted MCP server behind
nginx with a valid Let's Encrypt certificate, reachable over the public internet.)
Server capabilities
Self-hosted MCP server (Streamable HTTP) whose app is its own OAuth 2.0 authorization
server (OpenIddict 7.5):
401on/mcpwith
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
- RFC 9728 protected resource metadata at the root path and the path-suffixed variant
(/.well-known/oauth-protected-resource/mcp)
- RFC 8414 AS metadata at
/.well-known/oauth-authorization-server(root and
path-suffixed), plus /.well-known/openid-configuration
- RFC 7591 dynamic client registration (
registration_endpointadvertised; returns 201
with a public PKCE client, token_endpoint_auth_method=none)
token_endpoint_auth_methods_supportedincludes"none"authorization_servers[0]matches the ASissuerbyte-for-byte (trailing slash included)- Authorization Code + PKCE (S256);
response_types_supported: ["code"]
What the server sees during a failing macOS codex mcp login (nginx access log)
CLIENT_IP - - [20/Jul/2026:18:30:56 +0000] "GET /mcp HTTP/1.1" 401 0 "-" "-"
CLIENT_IP - - [20/Jul/2026:18:30:56 +0000] "GET /.well-known/oauth-protected-resource HTTP/1.1" 200 218 "-" "-"
CLIENT_IP - - [20/Jul/2026:18:30:56 +0000] "GET /.well-known/oauth-authorization-server HTTP/1.1" 200 1653 "-" "-"
All three requests succeed — then nothing. No fallback attempt to/.well-known/openid-configuration, no POST to the registration endpoint. The CLI
prints Error: No authorization support detected immediately.
This should be impossible if the 200 AS-metadata response was actually processed:
rmcp 1.8.0's discover_metadata() returns NoAuthorizationSupport only after all
discovery candidates fail, and a successful parse of that document would have continued
to DCR (as it does on Linux, below). This suggests the login path's HTTP client fails
client-side on macOS, separately from the requests above.
Same binary version on Linux (x86_64) — full success
CLIENT_IP - - [20/Jul/2026:18:02:01 +0000] "POST /oauth/register HTTP/1.1" 201 261 "-" "-"
CLIENT_IP - - [20/Jul/2026:18:02:01 +0000] "GET /oauth/authorize?response_type=code&client_id=…&code_challenge=…&code_challenge_method=S256&redirect_uri=http%3A%2F%2F127.0.0.1%3A33721%2Fcallback%2F…&scope=openid+offline_access+mcp&resource=https%3A%2F%2Fmcp.example.com%2Fmcp HTTP/2.0" 302 0
codex mcp login output on Linux (after the RFC 9207 server-side workaround described
below): Successfully logged in to MCP server 'sparrow'. — codex mcp list showsStatus enabled / Auth OAuth.
mcp-remote from the SAME Mac — full success
CLIENT_IP - - [20/Jul/2026:18:58:26 +0000] "GET /oauth/authorize?…" 302 … "node"
CLIENT_IP - - [20/Jul/2026:18:58:26 +0000] "GET /.well-known/oauth-protected-resource/mcp HTTP/1.1" 200 218 "-" "node"
CLIENT_IP - - [20/Jul/2026:18:58:26 +0000] "POST /oauth/token HTTP/1.1" 200 4575 "-" "node"
CLIENT_IP - - [20/Jul/2026:18:58:26 +0000] "POST /mcp HTTP/1.1" 200 77092 "-" "node" ← tools/list
Same machine, same network, same server: the stdio bridge (codex mcp add sparrow --) completes OAuth and lists tools. This is
npx -y mcp-remote https://mcp.example.com/mcp
our current workaround.
Live metadata documents (sanitized)
GET /.well-known/oauth-protected-resource → 200:
{"resource":"https://mcp.example.com/mcp","authorization_servers":["https://mcp.example.com/"],"scopes_supported":["mcp","offline_access"],"bearer_methods_supported":["header"]}
GET /.well-known/oauth-authorization-server → 200 (identical at/.well-known/openid-configuration and both path-suffixed variants):
{
"issuer": "https://mcp.example.com/",
"authorization_endpoint": "https://mcp.example.com/oauth/authorize",
"token_endpoint": "https://mcp.example.com/oauth/token",
"revocation_endpoint": "https://mcp.example.com/oauth/revoke",
"jwks_uri": "https://mcp.example.com/.well-known/jwks",
"grant_types_supported": ["authorization_code", "refresh_token"],
"response_types_supported": ["code"],
"response_modes_supported": ["query", "form_post", "fragment"],
"scopes_supported": ["openid", "offline_access", "mcp"],
"claims_supported": ["aud", "exp", "iat", "iss", "sub"],
"id_token_signing_alg_values_supported": ["RS256"],
"code_challenge_methods_supported": ["plain", "S256"],
"subject_types_supported": ["public"],
"prompt_values_supported": ["consent", "login", "none", "select_account"],
"token_endpoint_auth_methods_supported": ["none", "client_secret_basic", "client_secret_post", "private_key_jwt"],
"revocation_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"],
"require_pushed_authorization_requests": false,
"tls_client_certificate_bound_access_tokens": false,
"registration_endpoint": "https://mcp.example.com/oauth/register",
"claims_parameter_supported": false,
"request_parameter_supported": false,
"request_uri_parameter_supported": false,
"authorization_response_iss_parameter_supported": false,
"authorization_response_iss_parameter_supported_NOTE": "false is a WORKAROUND for the second bug below; the server actually sends iss. It was true when that bug reproduced."
}
(The _NOTE key is annotation for this issue, not part of the live document.)
Second defect: RFC 9207 iss is dropped when relaying the OAuth callback
Found while debugging on Linux, where the flow gets further. Withauthorization_response_iss_parameter_supported: true advertised (the honest value),codex mcp login on Linux failed at the callback stage:
Error: failed to handle OAuth callback
Caused by:
Authorization server response missing required issuer: expected https://mcp.example.com/
Evidence the server sends iss correctly — raw callback captured with a local listener
(DCR probe client, browser completed the redirect):
GET /cb?code=…&state=…&iss=https%3A%2F%2Fmcp.example.com%2F HTTP/1.1
Byte-exact match with the advertised issuer. Cause, from the codex source
(codex-rs/rmcp-client/src/perform_oauth_login.rs): the local callback server extracts
only code and state (OauthCallbackResult { code, state }) and callsoauth_state.handle_callback(&code, &csrf_state) — the variant without issuer.
rmcp 1.8.0 (validate_authorization_response_issuer) then requires iss because the
AS metadata advertises support → guaranteed failure against any server that honestly
advertises RFC 9207. Codex discards the very parameter rmcp then demands.
Suggested fix: extract iss from the callback query and usehandle_callback_with_issuer / handle_callback_url.
Current server-side workaround (required for codex to work at all): advertiseauthorization_response_iss_parameter_supported: false while still sending iss.
Expected behavior
- macOS: the OAuth flow proceeds as it does on Linux — or the error names the actual
failing step instead of No authorization support detected after three successful
discovery responses.
- The callback relay preserves
issso servers can honestly advertise RFC 9207.
10 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Facing the same problem with
issbeing discarded and OAuth login fails.This started after updating to Codex 0.145.0. It was working fine until 0.142.0.
It seems to be related to this PR had bumped the
rmcpversion.Analysis (community)
Thank you for the unusually high-signal report — nginx access logs, Linux control, same-Mac
mcp-remotecontrol, and the live metadata documents make this much easier to reason about than most OAuth failures.codex-aarch64-apple-darwinbinary + freshCODEX_HOME); Linux x86_64 control succeeds against the same server. I did not re-run the live macOS repro myself; analysis below is static reading of currentmainplus the evidence already in this thread / related issues.isspath is a clear code defect; the macOS pre-flow collapse is a strong stack-divergence hypothesis consistent with the logs, but the exact soft-fail mode on the login HTTP client is inferred, not packet-captured.GET /mcp401 → protected-resource 200 → AS metadata 200, then noPOST /oauth/register, CLI surfacesError: No authorization support detected.mcp addnuance: printsDetected OAuth support. Starting OAuth flow…then the same error — capability detection and the login flow are not the same HTTP stack (see below).authorization_response_iss_parameter_supported: true, fails later withAuthorization server response missing required issuerdespiteiss=present on the redirect.isswhen the AS requires it.Related: same opaque error class on Linear in #34427; earlier macOS MCP OAuth TLS/
native-tlsdirection in #18772.Root-cause hypothesis
Two separable defects; either alone is enough to break a subset of servers, and together they match this report’s Linux-vs-macOS split.
(1) Pre-flow macOS: discovery soft-fails to
NoAuthorizationSupport(medium confidence)On current
main, capability detection and login re-discovery use different clients:| Path | Where | HTTP client |
|---|---|---|
| “Detected OAuth support” /
oauth_login_support|codex-mcp→discover_streamable_http_oauth| Directreqwest0.13 + rustls-only features inrmcp-client/Cargo.toml, withno_proxy()(auth_status.rs, comment cites system-configuration / #8912) || Actual
perform_oauth_login|OauthLoginFlow::new|OAuthHttpClientAdapterover exec-serverReqwestHttpClient(perform_oauth_login.rsdefaults toArc::new(ReqwestHttpClient)), workspace reqwest 0.12 + cookie store;build_reqwest_client_with_custom_caonly forcesuse_rustls_tls()when a custom CA is configured |That split matches the report’s “Detected OAuth support … then No authorization support detected” pattern: detection can succeed on the rustls/
no_proxypath while login re-runs discovery on the adapter stack and fails client-side.rmcp 1.8.0 then collapses several real failures into one user-facing error: transport errors / unparseable AS bodies tend to soft-fail into “no metadata,” and
discover_metadatasurfacesAuthError::NoAuthorizationSupport(“No authorization support detected”). From the outside that is indistinguishable from a server that truly lacks OAuth — which is exactly the opaque failure after three nginx 401/200/200 lines.**What is not proven without a live capture of
OAuthHttpClientAdapterstatus/body:** whether this reporter’s server fails via empty body, parse failure, mid-stream reassembly, TLS backend differences, or something else on macOS. The dual-stack + soft-fail story is the best fit for the existing evidence; TLS 1.3-only cases in #18772 are one concrete historical instance of the same error string, not necessarily this server’s cause.(2) Callback bug:
issdropped (high confidence)Confirmed in
codex-rs/rmcp-client/src/perform_oauth_login.rs:OauthCallbackResultholds only{ code, state }.parse_oauth_callbackmatchescode/state/error/error_descriptionand ignoresiss.finish()callsoauth_state.handle_callback(&code, &csrf_state)— the no-issuer API.When AS metadata advertises
authorization_response_iss_parameter_supported: true, rmcp’svalidate_authorization_response_issuerrequiresiss. The browser redirect can include a correctiss=(as captured in the issue), but Codex never forwards it →Authorization server response missing required issuer. That matches the Linux failure and the server-side workaround of advertisingfalsewhile still sendingiss. Timing aligns with the rmcp 1.8.0 bump (#29634) relative to older 1.7.x pins.High-level fix outline (not a PR)
Keep the change small and layered; no need to touch credential store, DCR policy, unrelated MCP transport, or broad rmcp API rewrites.
Extend
OauthCallbackResultwithOption<String>issuer; parseissfrom the callback query; callhandle_callback_with_issuer(orhandle_callback_urlwith the full URL) when present. Prefer fixing the client over asking every AS to lie aboutauthorization_response_iss_parameter_supported.Prefer explicit rustls (and the same
no_proxypolicy discovery already uses for #8912) on the login /ReqwestHttpClient/ OAuth adapter path — or reuse one shared client construction so macOS does not depend on a different TLS/proxy stack for MCP OAuth only. Aligning with the direction discussed in #18772 without expanding scope to all exec-server HTTP.When discovery HTTP is non-success at the client, or a 200 body fails to parse, map to a concrete error (URL + status + parse/transport reason) instead of only
NoAuthorizationSupport. EnsureRUST_LOGactually surfaces adapter errors (the report’sRUST_LOG=debugproducing nothing past the opaque error is itself a product bug).Optional upstream: ask rmcp to reduce soft-fail opacity — but Codex can fix iss and client unification independently.
Non-goals: server workarounds as the long-term fix; rewriting MCP transports; changing how credentials are stored.
Test ideas
Failing-first coverage the team (or an invitee) could land with the fix:
parse_oauth_callback("/callback/<id>?code=c&state=s&iss=https%3A%2F%2Fas.example%2F", …)yields issuer; finish/mock path invokeshandle_callback_with_issuer(or equivalent) with that value; absentissstill succeeds when the AS does not require it.HttpClientstreams a 200 RFC 8414 JSON body; assert the reassembledHttpResponsebytes reach rmcp intact; empty body after 200 must not silently become “no auth” once codex maps errors explicitly.authorization_response_iss_parameter_supported: true+ redirect includingisscompletes login.no_proxyconsistent withauth_statusdiscovery.Questions for maintainers
Happy to be corrected on any of this — a few alignment checks only:
no_proxyconstruction asauth_statusdiscovery the preferred direction, or do you want a narrower adapter-only fix?issrelay), as the reporter offered?Happy to refine this analysis with more pointers or a cleaner minimal outline if useful. If the team later wants an invited PR for the small layered approach above, I’d be glad to help — no rush and no expectation.
Building on the root-cause analysis above with the latent introducing commit, one naming/construction correction, and a Defect B status re-check. Traced against
mainas of5dd992a(2026-07-24).Latent introducing PR (the
iss-drop predates the #29634 activation noted above).git log --diff-filter=A -- codex-rs/rmcp-client/src/perform_oauth_login.rsreturns1d17ca1fa/ #4517 (2025-10-03, "[MCP] Add support for MCP Oauth credentials"), which createdperform_oauth_login.rs(141 lines) andoauth.rs(822 lines) with theiss-droppingOauthCallbackResult { code, state }shape already in place. It was latent then because the issuer-enforcing rmcp version was not yet in play; it went active with the rmcp 1.8.0 bump (#29634) already cited above. So the drop is a 9-month-old structural choice, not something introduced by the 1.8.0 bump.Naming/construction correction - the login HTTP client was restructured post-issue, in two steps. #35023 (
1ee8f49, 2026-07-23, "Route exec-server HTTP through configured proxy policy") wired login throughHttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault)while still namedReqwestHttpClient(it changedReqwestHttpClient->ReqwestHttpClient::new(HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault))). #35059 (09241ae, 2026-07-24, "Decouple exec-server HTTP from reqwest types") then renamedReqwestHttpClient->RouteAwareHttpClient. The current login path isRouteAwareHttpClient::new(HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault))(perform_oauth_login.rs:165, 216); codex still calls the no-issuerhandle_callback(&code, &csrf_state)(:612). Any fix touching the login HTTP path should target the renamed type.Defect B status re-check (proxy-policy asymmetry, medium confidence, unremediated as of
5dd992a). Discovery usesClient::builder().timeout(DISCOVERY_TIMEOUT).no_proxy()(auth_status.rs:237-239, comment cites the system-configuration crate bug / #8912); login usesOutboundProxyPolicy::ReqwestDefault, which lets reqwest own proxy selection and does not callno_proxy()as discovery does. This is a real asymmetry in codex's own source. What I could not confirm from source alone is whether this path actually exercises the system-configuration behavior or contributes to the OP's failure - that is runtime-dependent (system proxy configuration + the system-configuration crate's behavior), so I hold this at medium confidence, not "confirmed." The discovery-vs-login client split noted in the first comment is also still present and unremediated: discovery uses rmcp-client's pinnedreqwest = "0.13"(rustls-only,rmcp-client/Cargo.toml:33), while the login client uses the workspacehttp-clientcrate's reqwest - a separate stack - without overclaiming either causes the OP failure.Caveat carried forward: rmcp 1.8.0's source is not in this machine's cargo cache, so I did not re-verify the exact rmcp-side method/error behavior; the codex-side facts above (
OauthCallbackResultshape,handle_callbackcall,no_proxyasymmetry, reqwest split) are from codex's own tree.I can reproduce the RFC 9207 issuer-callback failure with Render’s official hosted MCP server on Windows.
Environment:
codex mcp add render --url https://mcp.render.com/mcp --oauth-client-id codex
Browser authorization completes normally, Codex returns:
Error: failed to handle OAuth callback
Caused by:
Authorization server response missing required issuer: expected https://api.render.com
I can also reproduce the RFC 9207 issuer-callback failure on Linux with Sentry’s official hosted MCP server.
Environment:
linux-x86_64buildhttps://mcp.sentry.dev/mcp/<org>codex mcp login sentrysuccessfully discovers OAuth, opens Sentry’s authorization page, and Sentry accepts the consent. After the browser redirects to Codex’s local callback, the CLI fails with:As a same-machine control, native OAuth login completed successfully for the official hosted Supabase and Vercel MCP servers immediately before this test. This rules out a general browser callback, localhost listener, or machine-level OAuth failure.
So the
issrelay defect is not limited to the self-hosted server in the original report, macOS, or Windows/Render: it also affects Codex 0.146.0 on Linux against Sentry’s official MCP service.Independent reproduction of the RFC 9207 callback defect with another authorization-server implementation:
0.146.04.0.0b1, local Streamable HTTP athttp://localhost:8001/mcphttp://localhost:8001/authorization_response_iss_parameter_supported: trueThe flow completed discovery, DCR (
POST /register→ 201), authorization/consent, the upstream token exchange, and the FastMCP IdP callback (GET /auth/callback?...→ 302). Codex then failed with:FastMCP 4.0.0b1 builds that final client redirect with
build_client_redirect(..., iss=str(self.issuer_url)); a focused regression check confirms the generated callback URL carries the issuer byte-for-byte matching discovery.As the temporary server-side workaround, changing only the advertised metadata field to
authorization_response_iss_parameter_supported: false(while continuing to emitiss) makes the discovery document Codex-compatible. This adds a localhost/FastMCP control to the OpenIddict, Render, and Sentry cases already reported above.Confirmed this also affects Sentry's hosted MCP server on macOS, codex-cli 0.146.0.
Command:
Browser authorisation completed and returned to the local callback, then Codex failed with:
MCP config:
https://mcp.sentry.dev/mcpAfterwards,
codex mcp listshowed Sentry as enabled but withAuth: Unsupported.This suggests the callback issuer-handling defect also affects Sentry's production MCP service and is still present in 0.146.0.
anyone checking on that, they fixed it in 0.147.0-alpha.1.2, so wait a bit till release or install alpha and you are good to go
Confirming @patrikduksin's report with the exact changes, since two separable defects were in play here and only one is what most people in this thread are hitting.
Defect A (the RFC 9207
issdrop) is fixed onmainin61de0d8fe/ #35720 (2026-07-28, shipped in the 0.147.0 alphas).OauthCallbackResultnow carriesissuer: Option<String>(codex-rs/rmcp-client/src/perform_oauth_login.rs:293-296),parse_oauth_callbackreads"iss"(:339), andfinish()callshandle_callback_with_issuer(&code, &csrf_state, issuer.as_deref())(:613) instead of the no-issuer API. Regression coverage landed with it:parse_oauth_callback_preserves_rfc_9207_issuer(:1015) andoauth_callback_validates_rfc_9207_issuer_before_token_exchange(:863). That is precisely the failure @ahnaineh saw against FastMCP 4.0.0b1 and @Cliffinkent saw againstmcp.sentry.dev— both worth retesting on 0.147.0-alpha.1.2 or later, and any AS advertisingauthorization_response_iss_parameter_supported: falsepurely as a workaround can stop doing that.Defect B (discovery and login on different HTTP stacks) — the likely source of the OP's macOS
No authorization support detected— was addressed separately by #35806 (9ea975a2d) and #35814 (709283b43): callers must now pass an HTTP client into MCP OAuth discovery and login, and the separate directreqwestpath is gone.auth_status.rstakeshttp_client: Arc<dyn HttpClient>(:87, :199, :219) rather than building its ownno_proxy()client, so the asymmetry I flagged no longer exists in source.I have not re-run the original macOS repro, so @mike-phys08's pre-flow case is the one still worth verifying on 0.147.0+ before this is closed. One item from the outline does not appear to be covered by either PR: mapping a soft-failed metadata fetch to a concrete error (URL + status + parse/transport reason) instead of collapsing to
NoAuthorizationSupport. If that is wanted as a follow-up I am happy to help when invited.