fix: token_version NULL handling — normalize None→0 in JWT comparison

Direct DB inserts (e.g. install wizard, manual MySQL) may leave
token_version as NULL. JWT payload had tv:null which failed the
strict != comparison against DB value 0. Normalize both sides
with `or 0` for defensive coding.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-05-25 15:37:10 +08:00
parent 99201f48fd
commit deb3a94ee6
2 changed files with 8 additions and 5 deletions
+7 -4
View File
@@ -216,8 +216,10 @@ async def _verify_token(token: str, request: Request) -> Optional[Admin]:
# Primary check: token_version must match exactly.
# Changed on password change, TOTP disable, or token reuse detection.
# This invalidates ALL access tokens immediately — no grace window.
token_tv = payload.get("tv", -1)
if token_tv != admin.token_version:
# Normalize None → 0 for safety (DB may have NULL from direct inserts)
token_tv = payload.get("tv") or 0
admin_tv = admin.token_version or 0
if token_tv != admin_tv:
logger.debug(f"Token invalidated: token_version mismatch (token={token_tv}, db={admin.token_version})")
return None
@@ -271,8 +273,9 @@ async def get_optional_admin(
return None
# Primary check: token_version must match exactly
token_tv = payload.get("tv", -1)
if token_tv != admin.token_version:
token_tv = payload.get("tv") or 0
admin_tv = admin.token_version or 0
if token_tv != admin_tv:
return None
# Secondary check: updated_at timestamp (defense in depth)
+1 -1
View File
@@ -424,7 +424,7 @@ class AuthService:
"iat": now,
"exp": now + datetime.timedelta(minutes=JWT_ACCESS_TOKEN_EXPIRE_MINUTES),
"updated": int(admin.updated_at.timestamp()) if admin.updated_at else 0,
"tv": admin.token_version,
"tv": admin.token_version or 0,
}
return jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256")