from __future__ import annotations from fastapi import APIRouter, Depends, Request, Response from app.auth.repository import SessionRecord from app.auth.schemas import LoginRequest, PasswordChangeRequest, SetupRequest from app.auth.service import AuthService, SessionTokens from app.core.config import AppSettings from app.core.network import client_ip, is_loopback_client, use_secure_cookie from app.dependencies import get_app_settings, get_auth_service, require_session router = APIRouter(prefix="/api/v1/auth", tags=["auth"]) def _set_auth_cookies( request: Request, response: Response, tokens: SessionTokens, settings: AppSettings, ) -> None: max_age = settings.session_days * 86400 secure = use_secure_cookie(request, settings) response.set_cookie( "fluxip_session", tokens.session_token, max_age=max_age, httponly=True, secure=secure, samesite="strict", path="/", ) response.set_cookie( "fluxip_csrf", tokens.csrf_token, max_age=max_age, httponly=False, secure=secure, samesite="strict", path="/", ) def _clear_auth_cookies(request: Request, response: Response, settings: AppSettings) -> None: secure = use_secure_cookie(request, settings) response.delete_cookie("fluxip_session", path="/", secure=secure, samesite="strict") response.delete_cookie("fluxip_csrf", path="/", secure=secure, samesite="strict") @router.get("/bootstrap") def bootstrap(request: Request, service: AuthService = Depends(get_auth_service)) -> dict: return {"data": service.bootstrap_state(request.cookies.get("fluxip_session"))} @router.post("/setup", status_code=201) def setup( payload: SetupRequest, request: Request, response: Response, service: AuthService = Depends(get_auth_service), settings: AppSettings = Depends(get_app_settings), ) -> dict: tokens = service.setup(payload, loopback_client=is_loopback_client(request, settings)) _set_auth_cookies(request, response, tokens, settings) return {"data": {"username": payload.username}} @router.post("/login") def login( payload: LoginRequest, request: Request, response: Response, service: AuthService = Depends(get_auth_service), settings: AppSettings = Depends(get_app_settings), ) -> dict: client_key = client_ip(request, settings) tokens = service.login(payload, client_key) _set_auth_cookies(request, response, tokens, settings) return {"data": {"username": payload.username}} @router.post("/logout", status_code=204) def logout( request: Request, response: Response, _: SessionRecord = Depends(require_session), service: AuthService = Depends(get_auth_service), settings: AppSettings = Depends(get_app_settings), ) -> Response: service.logout(request.cookies.get("fluxip_session")) _clear_auth_cookies(request, response, settings) response.status_code = 204 return response @router.get("/me") def me(session: SessionRecord = Depends(require_session)) -> dict: return {"data": {"username": session.username}} @router.put("/password", status_code=204) def change_password( payload: PasswordChangeRequest, request: Request, response: Response, _: SessionRecord = Depends(require_session), service: AuthService = Depends(get_auth_service), settings: AppSettings = Depends(get_app_settings), ) -> Response: service.change_password(payload) _clear_auth_cookies(request, response, settings) response.status_code = 204 return response