71 lines
2.5 KiB
Python
71 lines
2.5 KiB
Python
"""FastAPI routes for the same-origin management BFF."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, HTTPException, Request, Response, status
|
|
from fastapi.responses import RedirectResponse
|
|
|
|
from .bff import BFFService
|
|
from .errors import AuthError
|
|
|
|
|
|
def _safe_error(exc: AuthError) -> HTTPException:
|
|
return HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail={"code": exc.code, "message": exc.public_message},
|
|
headers={"Cache-Control": "no-store"},
|
|
)
|
|
|
|
|
|
def build_auth_router(bff: BFFService) -> APIRouter:
|
|
router = APIRouter(prefix="/auth", tags=["auth"])
|
|
|
|
@router.get("/login", include_in_schema=False)
|
|
def login() -> RedirectResponse:
|
|
try:
|
|
return RedirectResponse(
|
|
bff.begin_login(), status_code=status.HTTP_307_TEMPORARY_REDIRECT
|
|
)
|
|
except AuthError as exc:
|
|
raise _safe_error(exc) from exc
|
|
|
|
@router.get("/callback", include_in_schema=False)
|
|
def callback(code: str, state: str) -> RedirectResponse:
|
|
try:
|
|
session = bff.complete_login(code=code, state=state)
|
|
except AuthError as exc:
|
|
raise _safe_error(exc) from exc
|
|
response = RedirectResponse("/", status_code=status.HTTP_303_SEE_OTHER)
|
|
response.headers["Set-Cookie"] = session.set_cookie
|
|
response.headers["Cache-Control"] = "no-store"
|
|
return response
|
|
|
|
@router.get("/session")
|
|
def session(request: Request, response: Response) -> dict[str, object]:
|
|
cookie = request.cookies.get(bff.settings.cookie_name)
|
|
if cookie is None:
|
|
response.headers["Cache-Control"] = "no-store"
|
|
return {"authenticated": False}
|
|
try:
|
|
metadata = bff.session_metadata(cookie)
|
|
record = bff.sessions.get(cookie)
|
|
except AuthError as exc:
|
|
raise _safe_error(exc) from exc
|
|
return {
|
|
"authenticated": True,
|
|
"csrf_token": record.csrf_token,
|
|
"session": metadata.model_dump(mode="json"),
|
|
}
|
|
|
|
@router.post("/logout", status_code=status.HTTP_204_NO_CONTENT, response_model=None)
|
|
def logout(request: Request, response: Response) -> None:
|
|
try:
|
|
cookie = request.cookies.get(bff.settings.cookie_name)
|
|
clear_cookie = bff.logout(cookie, request.headers.get("X-CSRF-Token"))
|
|
except AuthError as exc:
|
|
raise _safe_error(exc) from exc
|
|
response.headers["Set-Cookie"] = clear_cookie
|
|
response.headers["Cache-Control"] = "no-store"
|
|
|
|
return router
|