CVE-2026-46715
Flask-Security-Too OAuth reauthentication freshness bypass via cross- user OAuth identity acceptance
Flask-Security-Too allows users to add security features to their Flask applicationa. Version 5.8.0's OAuth reauthentication flow can mark a session as fresh after verifying an OAuth account that belongs to a different user. If an attacker can operate an already-authenticated but stale victim session, they can complete OAuth verification using their own OAuth identity. The victim session is then treated as recently reauthenticated, allowing freshness-protected account actions to proceed. Version 5.8.1 contains a fix for this issue.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- Low
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
5.3
Flask-Security-Too OAuth reauthentication freshness bypass via cross- user OAuth identity acceptance
Flask-Security-Too allows users to add security features to their Flask applicationa. Version 5.8.0's OAuth reauthentication flow can mark a session as fresh after verifying an OAuth account that belongs to a different user. If an attacker can operate an already-authenticated but stale victim session, they can complete OAuth verification using their own OAuth identity. The victim session is then treated as recently reauthenticated, allowing freshness-protected account actions to proceed. Version 5.8.1 contains a fix for this issue.
- Record
CVE-2026-46715- CVSS 4.0
- 5.3 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-287
- Updated
- 2026-11-02
EUVDEUVD-2026-46039
5.3
Flask-Security-Too allows users to add security features to their Flask applicationa. Version 5.8.0's OAuth reauthentication flow can mark a session as fresh after verifying an OAuth account that belongs to a different user. If an attacker can operate an already-authenticated but stale victim session, they can complete OAuth verification using their own OAuth identity. The victim session is then treated as recently reauthenticated, allowing freshness-protected account actions to proceed. Version 5.8.1 contains a fix for this issue.
- Record
EUVD-2026-46039- CVSS 4.0
- 5.3 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-97r5-pg8x-p63p No score
Flask-Security-Too OAuth reauthentication freshness bypass via cross- user OAuth identity acceptance
Summary
Flask-Security-Too 5.8.0's OAuth reauthentication flow can mark a session as fresh after verifying an OAuth account that belongs to a different user.
If an attacker can operate an already-authenticated but stale victim
session, they can complete OAuth verification using their own OAuth
identity. The victim session is then treated as recently
reauthenticated, allowing freshness-protected account actions to
proceed. This was reproduced against the built-in /change-username
route.
Details
The issue is in the OAuth verification callback.
_oauth_response_common() resolves the OAuth provider identity to a
Flask-Security user:
flask_security/oauth_glue.py:101-108
oauth_verify_response() then accepts any resolved user and updates
the current session freshness timestamp:
flask_security/oauth_glue.py:182-214flask_security/oauth_glue.py:201-204
The missing check is that the OAuth-resolved user must match the current authenticated session user. In the failing case:
- current session user:
[email protected] - OAuth verified user:
[email protected] - session marked fresh: yes
So the attacker is not logging in as the victim, but they are satisfying the victim session's reauthentication requirement with a different account.
PoC
Tested version:
Flask-Security-Too 5.8.0- tag
5.8.0 - commit
08288dff6907e413d848a16aaf43fc2c2b2a3b72
Used a minimal Flask app with:
SECURITY_OAUTH_ENABLE = True
SECURITY_OAUTH_BUILTIN_PROVIDERS = ["github"]
SECURITY_FRESHNESS = timedelta(seconds=1)
SECURITY_FRESHNESS_GRACE_PERIOD = timedelta(seconds=0)
SECURITY_USERNAME_ENABLE = True
SECURITY_CHANGE_USERNAME = True
The OAuth provider was replaced with a localhost mock provider
returning [email protected]. This avoids hitting a live third-party
provider while still exercising Flask-Security-Too's real OAuth
verification handler.
Reproduction steps:
1. Log in as [email protected].
2. Wait until the session is no longer fresh.
3. Confirm POST /change-username is blocked with 401 and
reauth_required=true.
4. Start OAuth verification with POST /login/oauth-verify-start/
github.
5. Complete the callback with an OAuth identity for
[email protected].
6. Confirm the session is still for [email protected], but fs_paa has
been updated.
7. Retry POST /change-username.
8. The victim user's username is changed successfully.
Observed result:
{
"pre_bypass_status": 401,
"pre_bypass_reauth_required": true,
"attacker_identity": "[email protected]",
"oauth_verify_response_status": 302,
"post_bypass_change_username_status": 200,
"final_email": "[email protected]",
"final_username": "victimowned1777878574",
"direct_impact_verified": true
}
Note: CSRF was disabled in the local harness only to keep the test
focused on the reauthentication check. This is not a CSRF bypass
report.
This bypasses Flask-Security-Too's freshness/reauthentication
boundary.
Applications using OAuth verification together with freshness-
protected account operations may allow a stale victim session to be
refreshed using a different user's OAuth account. In my test, this
allowed the victim account's username to be changed through Flask-
Security-Too's built-in /change-username route.
A likely fix is to reject OAuth verification unless the resolved OAuth
user matches current_user before updating session["fs_paa"].
- Record
GHSA-97r5-pg8x-p63p- Weakness
- CWE-287
- Updated
- 2026-11-02
NVD
5.3
Flask-Security-Too allows users to add security features to their Flask applicationa. Version 5.8.0's OAuth reauthentication flow can mark a session as fresh after verifying an OAuth account that belongs to a different user. If an attacker can operate an already-authenticated but stale victim session, they can complete OAuth verification using their own OAuth identity. The victim session is then treated as recently reauthenticated, allowing freshness-protected account actions to proceed. Version 5.8.1 contains a fix for this issue.
- Record
CVE-2026-46715- CVSS 4.0
- 5.3 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Weakness
- CWE-287
- Updated
- 2026-11-02
OSV
5.3
Flask-Security-Too OAuth reauthentication freshness bypass via cross- user OAuth identity acceptance
Flask-Security-Too allows users to add security features to their Flask applicationa. Version 5.8.0's OAuth reauthentication flow can mark a session as fresh after verifying an OAuth account that belongs to a different user. If an attacker can operate an already-authenticated but stale victim session, they can complete OAuth verification using their own OAuth identity. The victim session is then treated as recently reauthenticated, allowing freshness-protected account actions to proceed. Version 5.8.1 contains a fix for this issue.
- Record
CVE-2026-46715- CVSS 4.0
- 5.3 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-287
- Updated
- 2026-11-02
OSVGHSA-97r5-pg8x-p63p No score
Flask-Security-Too OAuth reauthentication freshness bypass via cross- user OAuth identity acceptance
Summary
Flask-Security-Too 5.8.0's OAuth reauthentication flow can mark a session as fresh after verifying an OAuth account that belongs to a different user.
If an attacker can operate an already-authenticated but stale victim
session, they can complete OAuth verification using their own OAuth
identity. The victim session is then treated as recently
reauthenticated, allowing freshness-protected account actions to
proceed. This was reproduced against the built-in /change-username
route.
Details
The issue is in the OAuth verification callback.
_oauth_response_common() resolves the OAuth provider identity to a
Flask-Security user:
flask_security/oauth_glue.py:101-108
oauth_verify_response() then accepts any resolved user and updates
the current session freshness timestamp:
flask_security/oauth_glue.py:182-214flask_security/oauth_glue.py:201-204
The missing check is that the OAuth-resolved user must match the current authenticated session user. In the failing case:
- current session user:
[email protected] - OAuth verified user:
[email protected] - session marked fresh: yes
So the attacker is not logging in as the victim, but they are satisfying the victim session's reauthentication requirement with a different account.
PoC
Tested version:
Flask-Security-Too 5.8.0- tag
5.8.0 - commit
08288dff6907e413d848a16aaf43fc2c2b2a3b72
Used a minimal Flask app with:
SECURITY_OAUTH_ENABLE = True
SECURITY_OAUTH_BUILTIN_PROVIDERS = ["github"]
SECURITY_FRESHNESS = timedelta(seconds=1)
SECURITY_FRESHNESS_GRACE_PERIOD = timedelta(seconds=0)
SECURITY_USERNAME_ENABLE = True
SECURITY_CHANGE_USERNAME = True
The OAuth provider was replaced with a localhost mock provider
returning [email protected]. This avoids hitting a live third-party
provider while still exercising Flask-Security-Too's real OAuth
verification handler.
Reproduction steps:
1. Log in as [email protected].
2. Wait until the session is no longer fresh.
3. Confirm POST /change-username is blocked with 401 and
reauth_required=true.
4. Start OAuth verification with POST /login/oauth-verify-start/
github.
5. Complete the callback with an OAuth identity for
[email protected].
6. Confirm the session is still for [email protected], but fs_paa has
been updated.
7. Retry POST /change-username.
8. The victim user's username is changed successfully.
Observed result:
{
"pre_bypass_status": 401,
"pre_bypass_reauth_required": true,
"attacker_identity": "[email protected]",
"oauth_verify_response_status": 302,
"post_bypass_change_username_status": 200,
"final_email": "[email protected]",
"final_username": "victimowned1777878574",
"direct_impact_verified": true
}
Note: CSRF was disabled in the local harness only to keep the test
focused on the reauthentication check. This is not a CSRF bypass
report.
This bypasses Flask-Security-Too's freshness/reauthentication
boundary.
Applications using OAuth verification together with freshness-
protected account operations may allow a stale victim session to be
refreshed using a different user's OAuth account. In my test, this
allowed the victim account's username to be changed through Flask-
Security-Too's built-in /change-username route.
A likely fix is to reject OAuth verification unless the resolved OAuth
user matches current_user before updating session["fs_paa"].
- Record
GHSA-97r5-pg8x-p63p- Weakness
- CWE-287
- Updated
- 2026-11-02
OSVPYSEC-2026-2478 No score
Flask-Security-Too OAuth reauthentication freshness bypass via cross- user OAuth identity acceptance
Summary
Flask-Security-Too 5.8.0's OAuth reauthentication flow can mark a session as fresh after verifying an OAuth account that belongs to a different user.
If an attacker can operate an already-authenticated but stale victim
session, they can complete OAuth verification using their own OAuth
identity. The victim session is then treated as recently
reauthenticated, allowing freshness-protected account actions to
proceed. This was reproduced against the built-in /change-username
route.
Details
The issue is in the OAuth verification callback.
_oauth_response_common() resolves the OAuth provider identity to a
Flask-Security user:
flask_security/oauth_glue.py:101-108
oauth_verify_response() then accepts any resolved user and updates
the current session freshness timestamp:
flask_security/oauth_glue.py:182-214flask_security/oauth_glue.py:201-204
The missing check is that the OAuth-resolved user must match the current authenticated session user. In the failing case:
- current session user:
[email protected] - OAuth verified user:
[email protected] - session marked fresh: yes
So the attacker is not logging in as the victim, but they are satisfying the victim session's reauthentication requirement with a different account.
PoC
Tested version:
Flask-Security-Too 5.8.0- tag
5.8.0 - commit
08288dff6907e413d848a16aaf43fc2c2b2a3b72
Used a minimal Flask app with:
SECURITY_OAUTH_ENABLE = True
SECURITY_OAUTH_BUILTIN_PROVIDERS = ["github"]
SECURITY_FRESHNESS = timedelta(seconds=1)
SECURITY_FRESHNESS_GRACE_PERIOD = timedelta(seconds=0)
SECURITY_USERNAME_ENABLE = True
SECURITY_CHANGE_USERNAME = True
The OAuth provider was replaced with a localhost mock provider
returning [email protected]. This avoids hitting a live third-party
provider while still exercising Flask-Security-Too's real OAuth
verification handler.
Reproduction steps:
1. Log in as [email protected].
2. Wait until the session is no longer fresh.
3. Confirm POST /change-username is blocked with 401 and
reauth_required=true.
4. Start OAuth verification with POST /login/oauth-verify-start/
github.
5. Complete the callback with an OAuth identity for
[email protected].
6. Confirm the session is still for [email protected], but fs_paa has
been updated.
7. Retry POST /change-username.
8. The victim user's username is changed successfully.
Observed result:
{
"pre_bypass_status": 401,
"pre_bypass_reauth_required": true,
"attacker_identity": "[email protected]",
"oauth_verify_response_status": 302,
"post_bypass_change_username_status": 200,
"final_email": "[email protected]",
"final_username": "victimowned1777878574",
"direct_impact_verified": true
}
Note: CSRF was disabled in the local harness only to keep the test
focused on the reauthentication check. This is not a CSRF bypass
report.
This bypasses Flask-Security-Too's freshness/reauthentication
boundary.
Applications using OAuth verification together with freshness-
protected account operations may allow a stale victim session to be
refreshed using a different user's OAuth account. In my test, this
allowed the victim account's username to be changed through Flask-
Security-Too's built-in /change-username route.
A likely fix is to reject OAuth verification unless the resolved OAuth
user matches current_user before updating session["fs_paa"].
- Record
PYSEC-2026-2478- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
flask-security-tooPyPI |
5.8.0 up to 5.8.1 | 5.8.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-46715 is a medium vulnerability (CVSS 5.3) in Flask-middleware Flask-security-too, published May 22, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.35%, the 28th percentile. Fixed in flask-security-too 5.8.1.
References
github.com/pallets-eco/flask-security/security/advisories/GHSA-97r5-pg8x-p63p
github.com/pallets-eco/flask-security/commit/8e69f3a94a463c0e8ddc46e31743717559fd8d48
github.com/advisories/GHSA-97r5-pg8x-p63p
github.com/CVEProject/cvelistV5/tree/main/cves/2026/46xxx/CVE-2026-46715.json
nvd.nist.gov/vuln/detail/CVE-2026-46715
github.com/pallets-eco/flask-security
pypi.org/project/flask-security-too