62 lines
2.4 KiB
Python
62 lines
2.4 KiB
Python
# backend/routers/sso.py
|
|
# Fusion ACC-3.1 (#216) — Points d'entrée SSO de l'émettrice.
|
|
#
|
|
# GET /sso/authorize?return_to=<url> : point d'entrée des autres
|
|
# applications — valide le return_to (liste blanche *.dracodev.net,
|
|
# anti open-redirect) puis redirige vers la page de connexion
|
|
# d'ObsiGate en conservant le paramètre ; après connexion, le
|
|
# frontend repart vers l'application demanderesse (le cookie
|
|
# draco_session posé à la connexion couvre tout le domaine parent).
|
|
#
|
|
# GET /api/sso/revocations : liste des jti révoqués, réservée aux
|
|
# autres applications (clé machine DRACO_SSO_MACHINE_KEY, en-tête
|
|
# X-Draco-Machine-Key) qui la mettent en cache 60 s (design §5.2).
|
|
#
|
|
# GET /sso/logout?return_to=<url> : symétrique de /sso/authorize —
|
|
# point d'entrée navigable de la déconnexion globale (BUG-122,
|
|
# fusion #141). Les autres applications y naviguent depuis leur menu
|
|
# compte ; la déconnexion exécutée est celle de POST /api/auth/logout
|
|
# (logique partagée : jetons natifs révoqués, jti draco révoqué,
|
|
# cookie effacé sur le domaine parent), puis redirection vers le
|
|
# return_to validé (même liste blanche ; repli « / »).
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from urllib.parse import quote
|
|
|
|
from fastapi import APIRouter, HTTPException, Request
|
|
from fastapi.responses import RedirectResponse
|
|
|
|
from backend import draco_session
|
|
from backend.auth.router import apply_global_logout
|
|
|
|
router = APIRouter(tags=["SSO"])
|
|
|
|
|
|
@router.get("/sso/authorize")
|
|
async def sso_authorize(return_to: str = ""):
|
|
target = draco_session.validate_return_to(return_to)
|
|
if target:
|
|
return RedirectResponse(
|
|
url=f"/?return_to={quote(target, safe='')}", status_code=302)
|
|
return RedirectResponse(url="/", status_code=302)
|
|
|
|
|
|
@router.get("/sso/logout")
|
|
async def sso_logout(request: Request, return_to: str = ""):
|
|
target = draco_session.validate_return_to(return_to)
|
|
response = RedirectResponse(url=target or "/", status_code=302)
|
|
apply_global_logout(request, response)
|
|
return response
|
|
|
|
|
|
@router.get("/api/sso/revocations")
|
|
async def sso_revocations(request: Request):
|
|
machine_key = os.environ.get("DRACO_SSO_MACHINE_KEY", "")
|
|
if not machine_key:
|
|
raise HTTPException(404, "Non configuré")
|
|
if request.headers.get("X-Draco-Machine-Key") != machine_key:
|
|
raise HTTPException(401, "Clé machine invalide")
|
|
return {"revoked": draco_session.revoked_list()}
|