import os from datetime import datetime, timedelta, timezone from itsdangerous import URLSafeTimedSerializer, BadSignature, SignatureExpired from fastapi import Request, Response from db import get_user_by_id SECRET_KEY = os.environ.get("SECRET_KEY", "dev-secret-key-change-in-production") COOKIE_NAME = "session" DEFAULT_SESSION_MAX_AGE_DAYS = 7 SECONDS_PER_DAY = 24 * 3600 def session_max_age_seconds() -> int: raw_days = os.environ.get("SESSION_MAX_AGE_DAYS", "").strip() if not raw_days: return DEFAULT_SESSION_MAX_AGE_DAYS * SECONDS_PER_DAY try: days = int(raw_days) except ValueError: return DEFAULT_SESSION_MAX_AGE_DAYS * SECONDS_PER_DAY if days < 1: return DEFAULT_SESSION_MAX_AGE_DAYS * SECONDS_PER_DAY return days * SECONDS_PER_DAY MAX_AGE = session_max_age_seconds() _serializer = URLSafeTimedSerializer(SECRET_KEY) def create_session_token(user_id: int) -> str: return _serializer.dumps({"user_id": user_id}) def verify_session_token(token: str) -> int | None: try: data = _serializer.loads(token, max_age=MAX_AGE) return data.get("user_id") except (BadSignature, SignatureExpired): return None def set_session_cookie(response: Response, user_id: int): token = create_session_token(user_id) response.set_cookie( COOKIE_NAME, token, max_age=MAX_AGE, httponly=True, samesite="lax", secure=os.environ.get("SECURE_COOKIES", "false").lower() == "true", ) def clear_session_cookie(response: Response): response.delete_cookie(COOKIE_NAME) def get_current_user(request: Request) -> dict | None: token = request.cookies.get(COOKIE_NAME) return get_current_user_from_token(token) def get_current_user_from_token(token: str | None) -> dict | None: if not token: return None user_id = verify_session_token(token) if not user_id: return None return get_user_by_id(user_id)