Summary
Authorizing with a CIMD client_id whose host is fronted by a CDN that routes by TLS SNI (e.g. Cloudflare) fails with:
{"error":"invalid_request","error_description":"Unable to fetch client metadata"}
This reproduces for client_id=https://claude.ai/oauth/claude-code-client-metadata, used by Claude Code / Claude Desktop, when authorizing against a self-hosted tableau-mcp server.
Root cause
In getClientFromMetadataDoc() (src/server/oauth/authorize.ts):
- The hostname of the client metadata URL is DNS-resolved to a raw IP address (DNS-rebinding SSRF mitigation).
- The URL's hostname is rewritten to that IP.
isSSRFSafeURL validates the IP-based URL.
axios.get() requests the IP-based URL, overriding only the Host header to the original hostname.
Step 4 connects to the raw IP over HTTPS. Node/axios derive the TLS SNI from the request URL's host — which is now the IP, not the original hostname. CDNs like Cloudflare route and select which certificate to present based on SNI, not the Host header. Since the SNI is an unrecognized IP, Cloudflare's edge rejects the request (403), which the server logs as e.g.:
Failed to fetch client metadata from https://claude.ai/oauth/claude-code-client-metadata
AxiosError: Request failed with status code 403
url: 'https://<resolved-ip>/oauth/claude-code-client-metadata'
...and surfaces to the OAuth caller as the generic Unable to fetch client metadata, indistinguishable from a real network/DNS failure.
This affects any CIMD client whose client_id domain sits behind an SNI-routing CDN — not specific to claude.ai — so it's likely to recur for other MCP clients adopting CIMD (a newer part of the MCP authorization spec).
Related: #193 (proposed disabling CIMD entirely as a workaround; closed without a fix for CIMD-enabled deployments).
Fix
Opened as # — pins the outbound TLS SNI (via https.Agent({ servername })) to the original hostname while still connecting to the pre-resolved IP, so the existing SSRF protection is unaffected but CDN-fronted client_id hosts resolve correctly.
Environment
- tableau-mcp: self-hosted, OAuth/CIMD enabled
- Reported via an enterprise deployment behind a corporate proxy; confirmed via manual
axios.get() against the raw resolved IP that the 403 is independent of proxy config — it reproduces identically from outside that network.
Summary
Authorizing with a CIMD
client_idwhose host is fronted by a CDN that routes by TLS SNI (e.g. Cloudflare) fails with:{"error":"invalid_request","error_description":"Unable to fetch client metadata"}This reproduces for
client_id=https://claude.ai/oauth/claude-code-client-metadata, used by Claude Code / Claude Desktop, when authorizing against a self-hosted tableau-mcp server.Root cause
In
getClientFromMetadataDoc()(src/server/oauth/authorize.ts):isSSRFSafeURLvalidates the IP-based URL.axios.get()requests the IP-based URL, overriding only theHostheader to the original hostname.Step 4 connects to the raw IP over HTTPS. Node/axios derive the TLS SNI from the request URL's host — which is now the IP, not the original hostname. CDNs like Cloudflare route and select which certificate to present based on SNI, not the
Hostheader. Since the SNI is an unrecognized IP, Cloudflare's edge rejects the request (403), which the server logs as e.g.:...and surfaces to the OAuth caller as the generic
Unable to fetch client metadata, indistinguishable from a real network/DNS failure.This affects any CIMD client whose
client_iddomain sits behind an SNI-routing CDN — not specific to claude.ai — so it's likely to recur for other MCP clients adopting CIMD (a newer part of the MCP authorization spec).Related: #193 (proposed disabling CIMD entirely as a workaround; closed without a fix for CIMD-enabled deployments).
Fix
Opened as # — pins the outbound TLS SNI (via
https.Agent({ servername })) to the original hostname while still connecting to the pre-resolved IP, so the existing SSRF protection is unaffected but CDN-fronted client_id hosts resolve correctly.Environment
axios.get()against the raw resolved IP that the 403 is independent of proxy config — it reproduces identically from outside that network.