feat: implement sliding session - extend TTL on each authenticated request

This commit is contained in:
Agent Zero
2026-07-18 18:48:21 +02:00
parent b1153e0c54
commit ad41771b10
4 changed files with 25 additions and 2 deletions
+6
View File
@@ -108,6 +108,12 @@ async def get_session_data(redis: aioredis.Redis, session_id: str) -> dict[str,
return json.loads(raw)
async def refresh_session_ttl(redis: aioredis.Redis, session_id: str) -> None:
"""Extend the Redis session TTL on activity (sliding session)."""
settings = get_settings()
await redis.expire(f"session:{session_id}", settings.session_ttl_seconds)
async def invalidate_session(redis: aioredis.Redis, session_id: str) -> None:
"""Delete a session from Redis (logout). PostgreSQL record persists."""
await redis.delete(f"session:{session_id}")
+3
View File
@@ -83,6 +83,9 @@ class CSRFMiddleware(BaseHTTPMiddleware):
status_code=status.HTTP_403_FORBIDDEN,
content={"detail": "CSRF token mismatch", "code": "csrf_token_mismatch"},
)
# Sliding session: also extend TTL on CSRF-validated unsafe requests
await redis.expire(f"session:{session_id}", settings.session_ttl_seconds)
finally:
await redis.close()
+4 -1
View File
@@ -10,7 +10,7 @@ from fastapi import Depends, HTTPException, Request, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.config import get_settings
from app.core.auth import get_redis, get_session_data
from app.core.auth import get_redis, get_session_data, refresh_session_ttl
from app.core.db import get_db, set_tenant_context
@@ -44,6 +44,9 @@ async def get_current_user(
detail={"detail": "Session expired or invalid", "code": "session_invalid"},
)
# Sliding session: extend TTL on each authenticated request
await refresh_session_ttl(redis, session_id)
if not session_data.get("is_active", True):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
+12 -1
View File
@@ -133,7 +133,18 @@ async def me(
detail={"detail": "Session expired or invalid", "code": "session_invalid"},
)
return info
from fastapi.responses import JSONResponse
resp = JSONResponse(status_code=200, content=info)
resp.set_cookie(
key=settings.session_cookie_name,
value=session_id,
httponly=settings.session_cookie_httponly,
secure=settings.session_cookie_secure,
samesite=settings.session_cookie_samesite,
max_age=settings.session_ttl_seconds,
path="/",
)
return resp
@router.get("/me/permissions")