From 78610ae19f90c41a292d4bc616ad884a7ad7708a Mon Sep 17 00:00:00 2001 From: K4bain <296577378+K4bain@users.noreply.github.com> Date: Thu, 27 Aug 2026 20:56:37 +0500 Subject: [PATCH] fix(auth): stop leaking oauth client secrets via fake dynamic registration The fake dynamic client registration endpoint echoed back the pre-registered client_secret to any caller, exposing it to every MCP client. Register the caller as a public client instead: omit client_secret from the response and force token_endpoint_auth_method to none. Adopted from #327 (original patch by @failsafesecurity). --- fastapi_mcp/auth/proxy.py | 6 +++--- tests/test_auth_proxy.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 tests/test_auth_proxy.py diff --git a/fastapi_mcp/auth/proxy.py b/fastapi_mcp/auth/proxy.py index 3cf3fa5e..f84ad679 100644 --- a/fastapi_mcp/auth/proxy.py +++ b/fastapi_mcp/auth/proxy.py @@ -244,7 +244,7 @@ def setup_oauth_fake_dynamic_register_endpoint( But, this is an overcomplication for most use cases. So instead of actually implementing dynamic client registration, we just echo back the pre-registered - client ID and secret. + client ID and register the caller as a public client. Use this if you don't need dynamic client registration, or if your OAuth provider doesn't support it. """ @@ -252,15 +252,15 @@ def setup_oauth_fake_dynamic_register_endpoint( @app.post( path, response_model=ClientRegistrationResponse, + response_model_exclude_none=True, include_in_schema=include_in_schema, ) async def oauth_register_proxy(request: ClientRegistrationRequest) -> ClientRegistrationResponse: client_response = ClientRegistrationResponse( client_name=request.client_name or "MCP Server", # Name doesn't really affect functionality client_id=client_id, - client_secret=client_secret, redirect_uris=request.redirect_uris, # Just echo back their requested URIs grant_types=request.grant_types or ["authorization_code"], - token_endpoint_auth_method=request.token_endpoint_auth_method or "none", + token_endpoint_auth_method="none", ) return client_response diff --git a/tests/test_auth_proxy.py b/tests/test_auth_proxy.py new file mode 100644 index 00000000..1b72f51c --- /dev/null +++ b/tests/test_auth_proxy.py @@ -0,0 +1,31 @@ +from fastapi.testclient import TestClient + +from fastapi import FastAPI + +from fastapi_mcp.auth.proxy import setup_oauth_fake_dynamic_register_endpoint + + +def test_fake_dynamic_registration_does_not_return_client_secret(): + app = FastAPI() + setup_oauth_fake_dynamic_register_endpoint( + app=app, + client_id="test-client-id", + client_secret="super-secret-value", + ) + + client = TestClient(app) + response = client.post( + "/oauth/register", + json={ + "redirect_uris": ["https://client.example.com/callback"], + "client_name": "Test Client", + "token_endpoint_auth_method": "client_secret_post", + }, + ) + + assert response.status_code == 200 + payload = response.json() + + assert payload["client_id"] == "test-client-id" + assert "client_secret" not in payload + assert payload["token_endpoint_auth_method"] == "none"