Vulnerabilities in pkg:pypi/fastapi-users

PACKAGE
Advisory Identifier: GHSA-5j53-63w8-8625
Advisory Summary

CVE-2025-68481 is a MODERATE (CVSS 5.9) fastapi users vulnerable to 1-click vulnerability (CWE-285: Improper Authorization) affecting Fastapi users libraries in the PyPI ecosystem. The issue involves fastapi users vulnerable to 1-click account takeover in apps using fastapi sso. Remediation is available by upgrading affected packages to version 15.0.2.

1 Known Advisory Record(s)
Package Vulnerability Intelligence

pkg:pypi/fastapi-users

1 Known Advisory Record(s)

Vulnerability advisories affecting package pkg:pypi/fastapi-users. Select an advisory below to view technical details or fixes.

Advisory ID CVE / NVD Severity Summary
GHSA-5j53-63w8-8625 CVE-2025-68481 MODERATE FastAPI Users Vulnerable to 1-click Account Takeover in Apps Using FastAPI SSO

CVE-2025-68481 at a Glance

CVE Identifier CVE-2025-68481
GHSA Identifier GHSA-5j53-63w8-8625
Severity MODERATE
CVSS v3.1 Base Score 5.9
CWE Classification CWE-285: Improper Authorization
Affected Product Fastapi users
Package Ecosystem PyPI
Published Date
Last Updated

Security Impact

CVSS: 5.9

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:L/A:N).

Attack Vector Network (AV:N)
Attack Complexity High (AC:H)
Privileges Required None (PR:N)
User Interaction Required (UI:R)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity Low (I:L)
Availability None (A:N)

How to Fix CVE-2025-68481

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

fastapi-users fixed: 15.0.2
pip install fastapi-users>=15.0.2
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 fastapi-users

How to Check if You Are Affected

To determine whether your Python environment is affected by CVE-2025-68481, inspect your installed packages using pip:

pip show fastapi-users

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

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

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-285: Improper Authorization, where the product does not perform or incorrectly performs an authorization check when an actor attempts to access a resource or perform an action. In Fastapi users, 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

**Description** The OAuth login state tokens are completely stateless and carry no per-request entropy or any data that could link them to the session that initiated the OAuth flow. `generate_state_token()` is always called with an empty `state_data` dict, so the resulting JWT only contains the fixed audience claim plus an expiration timestamp. \[1\] ```py state_data: dict[str, str] = {} state = generate_state_token(state_data, state_secret) authorization_url = await oauth_client.get_authorization_url( authorize_redirect_url, state, scopes, ) ``` *fastapi\_users/router/oauth.py:65-71* On callback, the library merely checks that the JWT verifies under `state_secret` and is unexpired; there is no attempt to match the state value to the browser that initiated the OAuth request, no correlation cookie, and no server-side cache. \[2\] ```py try: decode_jwt(state, state_secret, [STATE_TOKEN_AUDIENCE]) except jwt.DecodeError: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=ErrorCode.ACCESS_TOKEN_DECODE_ERROR, ) except jwt.ExpiredSignatureError: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=ErrorCode.ACCESS_TOKEN_ALREADY_EXPIRED, ) ``` *fastapi\_users/router/oauth.py:130-141* Any attacker can hit `/authorize`, capture the server-generated state, finish the upstream OAuth flow with their own provider account, and then trick a victim into loading `.../callback?code=<attacker_code>&state=<attacker_state>`. Because the state JWT is valid for any client for \~1 hour, the victim’s browser will complete the flow. This leads to login CSRF. Depending on the app’s logic, the login CSRF can lead to an account takeover of the victim account or to the victim user getting logged in to the attacker's account. \[1\] [https://github.com/fastapi-users/fastapi-users/blob/bcee8c9b884de31decb5d799aead3974a0b5b158/fastapi\_users/router/oauth.py\#L57](https://github.com/fastapi-users/fastapi-users/blob/bcee8c9b884de31decb5d799aead3974a0b5b158/fastapi_users/router/oauth.py#L57) \[2\] [https://github.com/fastapi-users/fastapi-users/blob/bcee8c9b884de31decb5d799aead3974a0b5b158/fastapi\_users/router/oauth.py\#L111](https://github.com/fastapi-users/fastapi-users/blob/bcee8c9b884de31decb5d799aead3974a0b5b158/fastapi_users/router/oauth.py#L111) **Proof of Concept** Let’s think of an app \- AwesomeFastAPIApp. Let’s assume that the AwesomeFastAPIApp has internal logic that uses a `UserManager` different from the default `BaseUserManager.` With this `manager,` when an already logged-in user performs a callback request, the newly provided SSO identity gets linked to the already existing user that made the request. Then, an attacker can get account takeover inside the app by performing the following actions: 1\. They start an SSO OAuth flow, but stop it right before making the callback call to AwesomeFastAPIApp; 2\. The attacker tricks a logged-in user (via phishing, a drive-by attack, etc.) to perform a GET request with the attacker's state value and grant code to the AwesomeFastAPIApp callback. Because the library doesn’t check whether the state token is linked to the session performing the callback, the callback is processed, the grant code is sent to the provider, and the account linking takes place. After the GET request is performed, the attacker's SSO account is linked with the victim's AwesomeFastAPIApp account permanently. **Suggested Fix** Make the state a value tied to the session of the user that initiated the OAuth flow, as recommended by the official RFC. \[3\] \[3\] [https://www.rfc-editor.org/rfc/rfc6749\#section-10.12](https://www.rfc-editor.org/rfc/rfc6749#section-10.12)

CVE-2025-68481 Affected Fastapi users Versions

1 component(s)
Fixed Versions:
15.0.2
Product / Package Ecosystem Affected Versions Fixed Version
fastapi-users
PyPI
< 15.0.2
15.0.2

References

Frequently Asked Questions

What is CVE-2025-68481?
CVE-2025-68481 is a security vulnerability in Fastapi users involving FastAPI Users Vulnerable to 1-click Account Takeover in Apps Using FastAPI SSO under CWE-285: Improper Authorization.
Which Fastapi users versions are affected?
Affected versions include releases prior to 15.0.2 across affected packages: fastapi-users.
What version fixes CVE-2025-68481?
Upgrading to 15.0.2 (or the latest patched release for your respective branch) resolves the vulnerability.
Is fastapi-users affected?
Yes, fastapi-users is affected in releases prior to 15.0.2. Upgrade your dependency to 15.0.2 or later.
What is the CVSS score of CVE-2025-68481?
CVE-2025-68481 has a CVSS v3.1 base score of 5.9 (MODERATE) with vector string CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:L/A:N.
What CWE is associated with CVE-2025-68481?
CVE-2025-68481 is classified under CWE-285: Improper Authorization (The product does not perform or incorrectly performs an authorization check when an actor attempts to access a resource or perform an action.).
How can I check PyPI dependencies for CVE-2025-68481?
Run the dependency tree command: 'pip show fastapi-users'. 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-5j53-63w8-8625",
  "modified" : "2025-12-20T05:46:19Z",
  "published" : "2025-12-19T21:10:40Z",
  "aliases" : [ "CVE-2025-68481" ],
  "summary" : "FastAPI Users Vulnerable to 1-click Account Takeover in Apps Using FastAPI SSO",
  "details" : "**Description**\n\nThe OAuth login state tokens are completely stateless and carry no per-request entropy or any data that could link them to the session that initiated the OAuth flow. `generate_state_token()` is always called with an empty `state_data` dict, so the resulting JWT only contains the fixed audience claim plus an expiration timestamp. \\[1\\]\n\n```py\n        state_data: dict[str, str] = {}\n        state = generate_state_token(state_data, state_secret)\n        authorization_url = await oauth_client.get_authorization_url(\n            authorize_redirect_url,\n            state,\n            scopes,\n        )\n```\n\n*fastapi\\_users/router/oauth.py:65-71*\n\nOn callback, the library merely checks that the JWT verifies under `state_secret` and is unexpired; there is no attempt to match the state value to the browser that initiated the OAuth request, no correlation cookie, and no server-side cache. \\[2\\]\n\n```py\n        try:\n            decode_jwt(state, state_secret, [STATE_TOKEN_AUDIENCE])\n        except jwt.DecodeError:\n            raise HTTPException(\n                status_code=status.HTTP_400_BAD_REQUEST,\n                detail=ErrorCode.ACCESS_TOKEN_DECODE_ERROR,\n            )\n        except jwt.ExpiredSignatureError:\n            raise HTTPException(\n                status_code=status.HTTP_400_BAD_REQUEST,\n                detail=ErrorCode.ACCESS_TOKEN_ALREADY_EXPIRED,\n            )\n```\n\n*fastapi\\_users/router/oauth.py:130-141*\n\nAny attacker can hit `/authorize`, capture the server-generated state, finish the upstream OAuth flow with their own provider account, and then trick a victim into loading `.../callback?code=<attacker_code>&state=<attacker_state>`. Because the state JWT is valid for any client for \\~1 hour, the victim’s browser will complete the flow. This leads to login CSRF. Depending on the app’s logic, the login CSRF can lead to an account takeover of the victim account or to the victim user getting logged in to the attacker's account.\n\n\\[1\\] [https://github.com/fastapi-users/fastapi-users/blob/bcee8c9b884de31decb5d799aead3974a0b5b158/fastapi\\_users/router/oauth.py\\#L57](https://github.com/fastapi-users/fastapi-users/blob/bcee8c9b884de31decb5d799aead3974a0b5b158/fastapi_users/router/oauth.py#L57)\n\n\\[2\\]  \n[https://github.com/fastapi-users/fastapi-users/blob/bcee8c9b884de31decb5d799aead3974a0b5b158/fastapi\\_users/router/oauth.py\\#L111](https://github.com/fastapi-users/fastapi-users/blob/bcee8c9b884de31decb5d799aead3974a0b5b158/fastapi_users/router/oauth.py#L111)\n\n**Proof of Concept**\n\nLet’s think of an app \\- AwesomeFastAPIApp. Let’s assume that the AwesomeFastAPIApp has internal logic that uses a `UserManager` different from the default `BaseUserManager.` With this `manager,` when an already logged-in user performs a callback request,  the newly provided SSO identity gets linked to the already existing user that made the request.\n\nThen, an attacker can get account takeover inside the app by performing the following actions:\n\n1\\. They start an SSO OAuth flow, but stop it right before making the callback call to AwesomeFastAPIApp;  \n2\\. The attacker tricks a logged-in user (via phishing, a drive-by attack, etc.) to perform a GET request with the attacker's state value and grant code to the AwesomeFastAPIApp callback. Because the library doesn’t check whether the state token is linked to the session performing the callback, the callback is processed, the grant code is sent to the provider, and the account linking takes place.\n\nAfter the GET request is performed, the attacker's SSO account is linked with the victim's AwesomeFastAPIApp account permanently.\n\n**Suggested Fix**\n\nMake the state a value tied to the session of the user that initiated the OAuth flow, as recommended by the official RFC. \\[3\\]\n\n\n\\[3\\] [https://www.rfc-editor.org/rfc/rfc6749\\#section-10.12](https://www.rfc-editor.org/rfc/rfc6749#section-10.12)",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:L/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "PyPI",
      "name" : "fastapi-users"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "15.0.2"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/fastapi-users/fastapi-users/security/advisories/GHSA-5j53-63w8-8625"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2025-68481"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/fastapi-users/fastapi-users/commit/7cf413cd766b9cb0ab323ce424ddab2c0d235932"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/fastapi-users/fastapi-users"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/fastapi-users/fastapi-users/blob/bcee8c9b884de31decb5d799aead3974a0b5b158/fastapi_users/router/oauth.py#L111"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/fastapi-users/fastapi-users/blob/bcee8c9b884de31decb5d799aead3974a0b5b158/fastapi_users/router/oauth.py#L57"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-285", "CWE-352" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2025-12-19T21:10:40Z",
    "nvd_published_at" : "2025-12-19T21:15:54Z"
  }
}