fix: close refresh token session binding bypass
Add require_session parameter to validate_token to enforce session_id matching for refresh operations. Attacker with stolen refresh token can no longer bypass session binding by omitting session_id from request. Also adds backend guard against deleting builtin admin user (was only blocked at Flask blueprint layer), and removes unused _ALL_RW variable.
This commit is contained in:
+12
-3
@@ -289,6 +289,7 @@ def validate_token(
|
||||
token_string: str,
|
||||
token_type: str = "access",
|
||||
session_id: str | None = None,
|
||||
require_session: bool = False,
|
||||
) -> dict[str, Any] | None:
|
||||
"""Validate a JWT token and check it against the blacklist.
|
||||
|
||||
@@ -298,8 +299,12 @@ def validate_token(
|
||||
session_id: Must match the ``session_id`` claim in the token payload.
|
||||
When provided, enforces session binding to prevent a stolen token
|
||||
from being usable without the originating session. When ``None``,
|
||||
the check is skipped (used by WebSocket auth which cannot carry
|
||||
the session ID header).
|
||||
the check is skipped unless ``require_session`` is True.
|
||||
require_session: If True and the token payload contains a ``session_id``,
|
||||
the request must provide a matching ``session_id``. Used by the
|
||||
refresh handler to prevent session binding bypass. When False
|
||||
(default), omitting ``session_id`` is acceptable even if the token
|
||||
contains one.
|
||||
|
||||
Returns:
|
||||
Payload dict including permissions, or None if invalid/blacklisted.
|
||||
@@ -309,7 +314,11 @@ def validate_token(
|
||||
return None
|
||||
if payload.get("type") != token_type:
|
||||
return None
|
||||
if session_id is not None and session_id != payload.get("session_id"):
|
||||
token_session_id = payload.get("session_id")
|
||||
if require_session and token_session_id is not None:
|
||||
if session_id is None or session_id != token_session_id:
|
||||
return None
|
||||
elif session_id is not None and session_id != token_session_id:
|
||||
return None
|
||||
|
||||
jti = payload.get("jti")
|
||||
|
||||
Reference in New Issue
Block a user