ac31290a87
Tokens issued before 'id' was added to the JWT payload pass signature verification but cause a KeyError when endpoints access user['id']. verify_token() now returns None for any token missing id/username/role, triggering a 401 → the frontend interceptor clears localStorage and redirects to /login so a fresh token is issued automatically. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
36 lines
1.3 KiB
Python
36 lines
1.3 KiB
Python
import os
|
||
from flask import request
|
||
from jwt import decode, ExpiredSignatureError, InvalidTokenError
|
||
from util.logger import logger
|
||
|
||
# SECRET_KEY must be provided via environment for production safety
|
||
SECRET_KEY = os.environ.get("SECRET_KEY")
|
||
if not SECRET_KEY:
|
||
logger.error("SECRET_KEY environment variable is not set – authentication disabled until configured.")
|
||
|
||
|
||
def verify_token():
|
||
if not SECRET_KEY:
|
||
return None
|
||
|
||
auth_header = request.headers.get("Authorization", "")
|
||
if not auth_header.startswith("Bearer "):
|
||
logger.warning("🔐 Invalid Bearer header")
|
||
return None
|
||
|
||
token = auth_header[7:] # len("Bearer ") == 7; safe because startswith is verified above
|
||
try:
|
||
decoded = decode(token, SECRET_KEY, algorithms=["HS256"])
|
||
# Reject tokens that are missing required fields (e.g. issued before
|
||
# 'id' was added to the payload) so callers never get a KeyError.
|
||
if not all(k in decoded for k in ("id", "username", "role")):
|
||
logger.warning("🔐 Token missing required fields — forcing re-login")
|
||
return None
|
||
return decoded
|
||
except ExpiredSignatureError:
|
||
logger.warning("🔐 Token expired")
|
||
return None
|
||
except InvalidTokenError:
|
||
logger.warning("🔐 Invalid token")
|
||
return None
|