[BUG] OAuth redirect_uri uses localhost instead of 127.0.0.1, violating RFC 8252 Section 7.3

Status Open
Reported on v2.1.90
Maintainer reply None cached
Activity 13 comments · opened Apr 2, 2026

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:

  1. Claude Code starts a local HTTP server on an ephemeral port
  2. It sends redirect_uri=http://localhost:54738/callback to the authorization server
  3. 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 localhost can 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

View original on GitHub ↗

13 Comments

github-actions[bot] · 5 months ago

Found 1 possible duplicate issue:

  1. https://github.com/anthropics/claude-code/issues/14079

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

shigechika · 4 months ago

Confirming this hits RFC-compliant authorization servers in the wild — the Spring Authorization Server isLoopbackAddress() behavior you described is spec-correct and Claude Code's localhost choice 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:

  • The callback server binds on 127.0.0.1 (loopback IP literal), not localhost.
  • The redirect_uri sent to the authorization server is http://127.0.0.1:<ephemeral-port>/callback.
  • Because it's an IP literal, the RFC-mandated port flexibility applies and strict servers (Spring, Keycloak with strict mode, etc.) accept it.
claude mcp add myserver -- uvx mcp-stdio --oauth https://server.example.com/mcp

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.

UberKitten · 4 months ago

Bumping, I ran into this today.

Kehrlann · 2 months ago

Ran into this today, using a Spring Authorization Server which is RFC-compliant.

pzgadzaj · 2 months ago

Run into this issue as well wih Spring authorization server

mihasya · 1 month ago

+1, running into this as well.

ryanfrigo · 1 month ago

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):

function rIr(e=nEi){return`http://localhost:${e}/callback`}

And the callback listener already binds the IP literal, not the hostname:

n.listen(e,"127.0.0.1",()=>{...})

So the client already receives the callback on 127.0.0.1 today. The only thing wrong is the string sent to the authorization server. Changing localhost to 127.0.0.1 in 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 using CLIENT_ID / MANUAL_REDIRECT_URL), so this change cannot touch first-party login.

And there's no workaround that scales: MCP_OAUTH_CALLBACK_PORT only pins the port, and no identity team is going to register per-port localhost redirect 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

ryanfrigo · 1 month ago

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.

pkozio · 1 month ago

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

ryanfrigo · 1 month ago

@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:

  • The redirect_uri is still built as http://localhost:${e}/callback in a single call site. The local callback listener already binds 127.0.0.1, so changing the string to 127.0.0.1 is the whole fix, with no effect on the claude.ai/Console login flow (separate code path).
  • Apply-ready patch: https://gist.github.com/ryanfrigo/fc3d56da851589289c60497056bc362a
  • This keeps recurring against Spring Authorization Server specifically (see comments from shigechika, Kehrlann, pzgadzaj, mihasya here), and it's the same class of failure as the Microsoft Entra redirect-mismatch reports.

Is there any chance of getting this into a release? Strict-RFC authorization servers reject the localhost form outright, and there's no per-port registration workaround that any identity team will accept. Happy to provide anything else useful.

glassmkr-com · 1 month ago

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.1 choice this issue calls out is exactly what breaks it.

Setup: claude mcp add --transport http <name> https://.../mcp, then /mcp → Authenticate.

What happens:

  • Claude Code registers redirect_uri = http://localhost:<port>/callback and starts its loopback callback listener.
  • lsof shows the listener binds IPv4 only: TCP 127.0.0.1:<port> (LISTEN) — there is no [::1] socket.
  • On macOS, localhost resolves to ::1 first (IPv6), then 127.0.0.1 (the default /etc/hosts maps both).
  • After the user approves, the browser follows the redirect to http://localhost:<port>/callback?code=..., tries ::1:<port>, and gets connection refused (nothing is listening on IPv6).
  • The authorization code is issued but never delivered to the listener, so Claude Code never calls the token endpoint. /mcp stays "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>/callbackConnection refused.
  • Authorization-server side: a grant + authorization code are minted on every attempt, but zero token exchanges occur (the code is never presented to /oauth/token).
  • Workaround that fixes it: commenting out ::1 localhost in /etc/hosts so localhost resolves only to 127.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 name localhost in the redirect URI, precisely to avoid this resolver-order failure. Two clean fixes: (a) register http://127.0.0.1:<port>/callback instead of localhost, and/or (b) bind the callback listener on both 127.0.0.1 and ::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).

bimsonz · 21 days ago

any thoughts on when this might get picket up ?

conradcoffman · 18 days ago

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):

redirect URI host "localhost:3118" is no longer supported (sunset 2026-08-01)

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, not localhost, per RFC 8252 §7.3.