fix(client): treat *.localhost as loopback for OAuth token endpoints - #2597
Conversation
RFC 6761 §6.3 reserves names ending in .localhost as loopback. Exempt them from the SEP-2207 https token-endpoint guard (same as localhost / 127.0.0.1 / ::1) so host-based multi-tenant local dev works. Fixes modelcontextprotocol#2591
🦋 Changeset detectedLatest commit: a24ce17 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@modelcontextprotocol/client
@modelcontextprotocol/codemod
@modelcontextprotocol/core
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
|
Thanks for the work on this! Is there an update on the plan here, and any rough idea of when it might be merged? |
|
Requesting a review on this from the Inspector side, where the guard surfaces most often. We have the same request open as modelcontextprotocol/inspector#1944, filed independently of #2591 — so that's two unrelated reporters here, plus the three confirmations on #2591 including a non-Inspector desktop client hitting it against Keycloak. That last one is the useful signal: it's For what it's worth, the argument in #2591 holds up against the layers below. RFC 6761 §6.3 reserves any name ending in We can't work around it downstream: Happy to test a prerelease against the Inspector's OAuth suites if that helps move it. |
The migration guide's exempt-host list for the token endpoint TLS guard now includes *.localhost. The changeset says that the SDK does not resolve the name itself, so *.localhost reaches the local machine only if the system resolver follows RFC 6761.
Summary
isLoopbackHostso hostnames ending in.localhostare treated as loopback (RFC 6761 §6.3), in addition tolocalhost,127.0.0.1, and::1/[::1].assertSecureTokenEndpointallow plain-HTTP token endpoints on host-based multi-tenant local setups (e.g.http://tenant.example.localhost:3300/...) instead of throwingInsecureTokenEndpointError.application_typeinference for redirect URIs, so*.localhostredirects correctly default tonative.Fixes #2591
Motivation
.localhostis a reserved TLD: names under it resolve to loopback by specification (and by browsers / OS resolvers). The SDK’s https token-endpoint guard was stricter than that definition and blocked common local multi-tenant Host-header setups that cannot use barehttp://localhost.Test plan
packages/client/test/client/auth.test.ts:assertSecureTokenEndpointacceptstenant.example.localhostrefreshAuthorizationpermits*.localhosttoken endpointsresolveClientMetadataderivesapplication_type: 'native'for*.localhostredirects@modelcontextprotocol/clientpatchpnpm --filter @modelcontextprotocol/client test— 800/800 passedpnpm build:all/typecheck:all/lint:allgreenNotes
Straightforward bug fix + tests per CONTRIBUTING. Implementation assisted by tooling.
(Opened as draft due to API restriction converting to ready — please mark Ready for review if required.)