FluxIP/app/auth/router.py

113 lines
3.5 KiB
Python

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