""" Cookie Utilities for Authentication Handles HttpOnly cookie creation for JWT tokens. Supports both access and refresh tokens with appropriate security settings. Author: Claude (Jarvis) Date: 2025-11-27 """ from fastapi import Response from app.config import get_settings settings = get_settings() # Cookie configuration ACCESS_TOKEN_COOKIE = "pd_access_token" REFRESH_TOKEN_COOKIE = "pd_refresh_token" ACCESS_TOKEN_MAX_AGE = 60 * 60 # 1 hour REFRESH_TOKEN_MAX_AGE = 60 * 60 * 24 * 7 # 7 days def is_secure_context() -> bool: """ Check if cookies should use Secure flag. Returns True if: - APP_ENV is 'production', OR - FRONTEND_URL starts with 'https://' This ensures cookies work correctly when accessing via HTTPS even in development mode. """ if getattr(settings, "app_env", "development") == "production": return True # Also enable Secure if frontend URL is HTTPS (common in dev with tunnels/proxies) frontend_url = getattr(settings, "frontend_url", "") return frontend_url.startswith("https://") def set_auth_cookies( response: Response, access_token: str, refresh_token: str, ) -> None: """ Set both access and refresh token cookies on response. Security settings: - HttpOnly: Prevents XSS access to tokens - Secure: HTTPS only in production - SameSite=Lax: CSRF protection while allowing top-level navigations - Path: Limits cookie scope Args: response: FastAPI Response object access_token: JWT access token refresh_token: JWT refresh token """ # Access token - short-lived, sent to all requests (needed for SSR cookie forwarding) response.set_cookie( key=ACCESS_TOKEN_COOKIE, value=access_token, max_age=ACCESS_TOKEN_MAX_AGE, httponly=True, secure=is_secure_context(), samesite="lax", path="/", # Root path so cookies are sent with all requests including SSR ) # Refresh token - long-lived, restricted to auth endpoints only response.set_cookie( key=REFRESH_TOKEN_COOKIE, value=refresh_token, max_age=REFRESH_TOKEN_MAX_AGE, httponly=True, secure=is_secure_context(), samesite="lax", path="/api/auth", ) def clear_auth_cookies(response: Response) -> None: """ Clear all auth cookies (logout). Args: response: FastAPI Response object """ response.delete_cookie(key=ACCESS_TOKEN_COOKIE, path="/") response.delete_cookie(key=REFRESH_TOKEN_COOKIE, path="/api/auth")