feat(auth): move JWT blacklist to Redis with TTL [FASE-0.2]

Revoke tokens by jti in a dedicated Redis DB, honor TTL from JWT exp on logout, reject revoked tokens in get_current_user, and add FakeRedis-backed API tests.
This commit is contained in:
2026-05-18 13:19:15 +02:00
parent 9b70655b7e
commit c5eb6f6dc1
5 changed files with 104 additions and 13 deletions

View File

@@ -96,13 +96,26 @@ def logout(
The token's ``jti`` is added to the Redis blacklist so it cannot
be reused even if the cookie has already been copied elsewhere.
The blacklist entry auto-expires when the token's ``exp`` is reached.
When both HttpOnly cookie and ``Authorization: Bearer`` are present
(typical for API clients), **both** are revoked so the session cannot
survive on whichever credential the next request prefers.
"""
# Attempt to blacklist the token's jti
token = aegis_token or request.headers.get("Authorization", "").removeprefix("Bearer ").strip()
if token:
bearer = (
request.headers.get("Authorization")
or request.headers.get("authorization")
or ""
)
bearer = bearer.removeprefix("Bearer ").removeprefix("bearer ").strip()
seen: set[str] = set()
for raw in (aegis_token, bearer):
if not raw or raw in seen:
continue
seen.add(raw)
try:
payload = jwt.decode(
token,
raw,
settings.SECRET_KEY,
algorithms=[settings.ALGORITHM],
)
@@ -111,7 +124,7 @@ def logout(
if jti:
blacklist_token(jti, float(exp))
except JWTError:
pass # token already invalid — nothing to revoke
pass # token already invalid — nothing to revoke for this raw value
response.delete_cookie(
key=_COOKIE_NAME,