fix(mcp): rewrite multi-tenant OAuth issuer mismatches in metadata#3423
fix(mcp): rewrite multi-tenant OAuth issuer mismatches in metadata#3423pshickeydev wants to merge 1 commit into
Conversation
Multi-tenant providers like Atlassian use a tenant-specific path for discovery but return a shared, top-level issuer in the metadata response, causing the SDK's strict RFC 8414 validation to reject the mismatch. Extend metadataFixupRoundTripper to rewrite the issuer when it shares the same origin and is a path ancestor of the discovery URL, with security boundaries preventing cross-host, cross-scheme, and non-ancestor rewrites. Co-Authored-By: Crush <crush@charm.land>
|
All contributors have signed the CLA ✍️ ✅ |
|
I have read the Contributor License Agreement (CLA) and hereby sign the CLA. |
|
there was a pr merged in right before this about an hour ago. can you check if that fixed the issue? very possible that it didnt but this sounded pretty similar |
|
ah nvm that was a slightly different issue |
|
okay did some investigating! it looks like your mcp server is out of compliance with RFC 8414 §3.3. Will investigate a bit more when I properly get back next week
|
Fixes #3419.
The Jira MCP server (
https://mcp.atlassian.com/v1/mcp/authv2) requiresOAuth, which v0.87.0 added support for. But the OAuth 2.1 implementation's
strict RFC 8414 issuer validation is incompatible with Atlassian's
multi-tenant authorization server: discovery resolves a tenant-specific URL
(
https://auth.atlassian.com/VCeDs...), while the metadata response returnsa shared, top-level issuer (
https://auth.atlassian.com). The mismatch failsbefore authorization can complete:
calling "initialize": sending "initialize": rejected by transport: failed to
get authorization server metadata: metadata issuer
"https://auth.atlassian.com" does not match issuer URL
"https://auth.atlassian.com/VCeDsk..."
This is a common pattern for multi-tenant providers that front many tenants
with one authorization server: the tenant-specific path is used for
discovery and routing, but the canonical issuer stays at the top-level
domain.
Fix
Extends
metadataFixupRoundTripper(already normalizing trailing-slashissuers per #3396) to also rewrite the issuer when:
https://x.comis anancestor of
https://x.com/tenant1;https://x.com/ais not anancestor of
https://x.com/ab).Both conditions must hold before the rewrite fires, so a server can't use
this to impersonate a different issuer — cross-host, cross-scheme, and
non-ancestor-path mismatches all pass through untouched and are still
rejected by the SDK's validation as before. Protected resource metadata
(which has no
issuerfield) is never touched.Test plan
New unit tests, all passing:
TestHandler_MultiTenantIssuerRewrite— end-to-end handler test againsta fake multi-tenant AS (tenant-specific discovery, shared issuer)
TestIssuerFromMetadataURL,TestSameOrigin,TestIsAncestorPath—table-driven unit tests for the new helpers
TestMetadataFixup_RewritesSameOriginAncestor— proves the rewritefires for the intended Atlassian-shaped case
TestMetadataFixup_SecurityBoundaries— proves it does not fire forcross-host, cross-scheme, non-ancestor, or descendant issuers
TestMetadataFixup_ProtectedResourceUntouched,TestMetadataFixup_TrailingSlashStillNormalized— regression guards forexisting behavior
go build ./...,go vet ./...(no new findings; one pre-existingunrelated warning in
internal/csync/maps.go),gofmt -lcleango test ./...— all oauth/MCP packages pass; the only repo-widefailures are pre-existing environment issues (missing GCP ADC
credentials in
internal/backend,internal/server,internal/workspace), confirmed present and identical on the parentcommit (
e68041a6), so this change introduces no regressionsManual: built the binary and ran the real OAuth flow end-to-end
against the live Atlassian Rovo MCP server
(
https://mcp.atlassian.com/v1/mcp/authv2) withoauth: true. Debuglogs confirmed the rewrite fired
(
Rewrote OAuth metadata issuer for multi-tenant provider), the browserconsent flow completed, and the token was persisted and used
successfully — where the same flow fails with the issuer-mismatch error
above on
main.I have read
CONTRIBUTING.md.I have created a discussion that was approved by a maintainer (for new features).