Bind CIMD assertion audience to the advertised token endpoint (#4659)

A bare-authority base_url renders with a trailing slash, so the expected aud was https://host//token while the metadata advertised https://host/token.
This commit is contained in:
Jeremiah Lowin 2026-07-27 11:46:03 -04:00 committed by GitHub
commit 0c1c42f151
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 50 additions and 1 deletions

View file

@ -10,6 +10,7 @@ from key_value.aio.stores.memory import MemoryStore
from mcp.shared.auth import OAuthClientInformationFull
from pydantic import AnyUrl
from starlette.applications import Starlette
from starlette.testclient import TestClient
from fastmcp.server.auth.oauth_proxy import OAuthProxy
from fastmcp.server.auth.oauth_proxy.models import OAuthTransaction
@ -618,3 +619,38 @@ class TestIdpCallbackSuccessForwarding:
# byte-for-byte.
assert "flag" in query
assert "sig=%FF%FE" in query
class TestCIMDTokenEndpointAudience:
"""The `aud` expected on a CIMD assertion matches the advertised token endpoint."""
@pytest.mark.parametrize(
"base_url",
["https://api.example.com", "https://api.example.com/api"],
)
def test_token_endpoint_url_matches_advertised_metadata(
self, jwt_verifier, base_url: str
):
"""A bare-authority base_url must not expect `aud` of `https://host//token`.
CIMD is enabled by default, and a spec-following client binds its
private_key_jwt assertion to the `token_endpoint` the metadata
advertises. If the proxy expects a different string, every such client
is rejected with invalid_client.
"""
proxy = OAuthProxy(
upstream_authorization_endpoint="https://auth.example.com/authorize",
upstream_token_endpoint="https://auth.example.com/token",
upstream_client_id="client-123",
upstream_client_secret="secret-456",
token_verifier=jwt_verifier,
base_url=base_url,
jwt_signing_key="test-secret",
client_storage=MemoryStore(),
)
app = Starlette(routes=proxy.get_routes(mcp_path="/mcp"))
with TestClient(app) as client:
metadata = client.get("/.well-known/oauth-authorization-server").json()
assert proxy.token_endpoint_url == metadata["token_endpoint"]