[BUG] OAuth redirect_uri uses localhost instead of 127.0.0.1, violating RFC 8252 Section 7.3
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Claude Code's OAuth client uses http://localhost:<port>/callback as the redirect URI when authenticating with MCP servers. This violates RFC 8252 Section 7.3, which specifies that native apps MUST use loopback IP literals (127.0.0.1 or ::1), not the localhost hostname.
This matters because the RFC only mandates dynamic port flexibility for loopback IP literals:
The authorization server MUST allow any port to be specified at the time of the request for loopback IP redirect URIs, to accommodate clients that obtain an available ephemeral port from the operating system at the time of the request.
OAuth authorization servers that strictly follow the RFC (including Spring Authorization Server, which is widely used) implement port flexibility only for 127.0.0.1 and ::1 — not for localhost. Since Claude Code uses ephemeral ports (e.g. http://localhost:54738/callback), and localhost does not qualify for dynamic port matching, the redirect URI fails validation.
Real-World Impact
Any OAuth authorization server that strictly implements RFC 8252 will reject Claude Code's redirect URI. Spring Authorization Server's default isLoopbackAddress() implementation, for example, only recognizes 127.0.0.1 and ::1 — not localhost. This means:
- Claude Code starts a local HTTP server on an ephemeral port
- It sends
redirect_uri=http://localhost:54738/callbackto the authorization server - The auth server sees
localhost, does NOT apply port flexibility (per RFC), and rejects the redirect URI since it doesn't exactly match any registered URI
This breaks MCP OAuth for any organization using an RFC-compliant authorization server. The workaround is to register localhost redirect URIs for every possible port, which is not practical.
Additional Context
- Previous issue #14079 reported the same problem but was auto-closed due to inactivity — no human evaluation was made
- The RFC also notes that
localhostcan resolve to addresses other than the loopback address on some systems, making it a less reliable choice for security-sensitive OAuth flows - This affects any MCP server that proxies OAuth to an RFC-compliant authorization server
Expected Behavior
Claude Code should use http://127.0.0.1:<port>/callback (not http://localhost:<port>/callback) for OAuth redirect URIs, per RFC 8252 Section 7.3.
Claude Code Version
2.1.90
Platform
Linux
13 Comments
Found 1 possible duplicate issue:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Confirming this hits RFC-compliant authorization servers in the wild — the Spring Authorization Server
isLoopbackAddress()behavior you described is spec-correct and Claude Code'slocalhostchoice is the outlier.As a workaround until this is fixed upstream, mcp-stdio (a stdio ↔ Streamable-HTTP bridge) runs its own OAuth 2.1 client that complies with RFC 8252 §7.3:
127.0.0.1(loopback IP literal), notlocalhost.redirect_urisent to the authorization server ishttp://127.0.0.1:<ephemeral-port>/callback.Claude Code only ever sees a plain stdio server for this entry, so its own
localhost-based OAuth client is never invoked for the affected MCP server. This unblocks users stuck behind RFC-compliant auth servers today.Bumping, I ran into this today.
Ran into this today, using a Spring Authorization Server which is RFC-compliant.
Run into this issue as well wih Spring authorization server
+1, running into this as well.
Still present in 2.1.205. I pulled apart the shipped bundle to see how big the fix is. It's one string.
The MCP OAuth redirect URI is built in exactly one place (minified names from the current release):
And the callback listener already binds the IP literal, not the hostname:
So the client already receives the callback on
127.0.0.1today. The only thing wrong is the string sent to the authorization server. Changinglocalhostto127.0.0.1in that one template is the entire fix, and it's what RFC 8252 §7.3 requires for dynamic-port loopback redirects. The claude.ai/console login flow builds its redirect_uri in a separate code path (the one usingCLIENT_ID/MANUAL_REDIRECT_URL), so this change cannot touch first-party login.And there's no workaround that scales:
MCP_OAUTH_CALLBACK_PORTonly pins the port, and no identity team is going to register per-portlocalhostredirect URIs.Since this repo doesn't carry the CLI source, here's the change in apply-ready patch form for whoever picks it up internally: https://gist.github.com/ryanfrigo/fc3d56da851589289c60497056bc362a
To be direct about the impact: our identity team and our InfoSec team are blocking Claude Code from being onboarded as an MCP client for our remote MCP server because of this bug. Our authorization server implements RFC 8252 strictly, like it's supposed to. Every other MCP client we've evaluated passes review. Claude Code is the one we can't approve.
The analysis above shows it's a one-string change in a single call site with no way to regress anything else. Please merge the fix and ship it.
Is there any ETA on solving this issue. It seems to be a clear change to make Claude Code compliant with RFC 8252 Section 7.3
@localden — flagging you on this since you've triaged the related MCP OAuth redirect issues (#53290, #57313, #47975). This one is a clean RFC 8252 §7.3 compliance bug and it's blocking real enterprise adoption.
Where it stands as of 2.1.209:
http://localhost:${e}/callbackin a single call site. The local callback listener already binds127.0.0.1, so changing the string to127.0.0.1is the whole fix, with no effect on the claude.ai/Console login flow (separate code path).Is there any chance of getting this into a release? Strict-RFC authorization servers reject the
localhostform outright, and there's no per-port registration workaround that any identity team will accept. Happy to provide anything else useful.Adding a clean real-world repro of this from a remote (streamable-HTTP) MCP server using OAuth 2.1 + PKCE on macOS, Claude Code v2.1.218 → v2.1.219. The
localhost-vs-127.0.0.1choice this issue calls out is exactly what breaks it.Setup:
claude mcp add --transport http <name> https://.../mcp, then/mcp→ Authenticate.What happens:
redirect_uri = http://localhost:<port>/callbackand starts its loopback callback listener.lsofshows the listener binds IPv4 only:TCP 127.0.0.1:<port> (LISTEN)— there is no[::1]socket.localhostresolves to::1first (IPv6), then127.0.0.1(the default/etc/hostsmaps both).http://localhost:<port>/callback?code=..., tries::1:<port>, and gets connection refused (nothing is listening on IPv6)./mcpstays "needs authentication" indefinitely.Confirmed from both ends:
curl http://127.0.0.1:<port>/callback→ reaches the listener (HTTP 400 from CC's own callback handler).curl http://[::1]:<port>/callback→Connection refused./oauth/token).::1 localhostin/etc/hostssolocalhostresolves only to127.0.0.1→ the redirect reaches the listener → token exchange completes → connected.Per RFC 8252 §7.3, native apps SHOULD use the loopback IP literal (
127.0.0.1/[::1]) rather than the namelocalhostin the redirect URI, precisely to avoid this resolver-order failure. Two clean fixes: (a) registerhttp://127.0.0.1:<port>/callbackinstead oflocalhost, and/or (b) bind the callback listener on both127.0.0.1and::1. A configurable--callback-host(see #69326, closed as duplicate) would also cover it.Environment: macOS 15, Claude Code v2.1.218 → v2.1.219, remote streamable-HTTP MCP server, OAuth 2.1 authorization-code + PKCE (S256).
any thoughts on when this might get picket up ?
Adding a concrete real-world repro of this, with evidence isolating it to Claude Code specifically:
Provider: Teamwork.com's hosted MCP server (
mcp.ai.teamwork.com)Claude Code (CLI), v2.1.206 and v2.1.228 (same result on both):
Fails before any consent screen renders — rejected at redirect_uri validation.
Same Teamwork account, same MCP server, via Claude Cowork App: OAuth succeeds without issue.
Since the same server accepts one Anthropic client's redirect and rejects another's, this isolates the failure to Claude Code's redirect_uri construction rather than a peculiarity of Teamwork's server. Consistent with this issue's diagnosis — Claude Code should be sending
127.0.0.1, notlocalhost, per RFC 8252 §7.3.