preserve Tower Authentik operator login WIP
GeoIntel release gates / Compile, test, contracts and builds (push) Failing after 1m51s
GeoIntel release gates / Python and npm vulnerability policy (push) Failing after 40s
GeoIntel release gates / GIS image, SBOM and container scan (push) Failing after 2m18s

This commit is contained in:
Jens
2026-08-30 03:05:29 +02:00
parent 3627a05bfe
commit 4fdc3aa11b
18 changed files with 744 additions and 201 deletions
+36
View File
@@ -3,6 +3,7 @@ from __future__ import annotations
from datetime import UTC, datetime
from fastapi import APIRouter, Depends, Request, Response, status
from fastapi.responses import RedirectResponse
from sqlalchemy.orm import Session
from app.core.config import get_settings
@@ -10,6 +11,7 @@ from app.core.errors import AppError
from app.db.session import get_db
from app.schemas.auth import AuthLoginRequest, AuthSession, AuthSessionEnvelope
from app.services.auth_service import AuthPrincipal, AuthService
from app.services.authentik_oidc_service import AuthentikOidcService
from app.services.demo_workflow_service import DemoWorkflowService
@@ -18,6 +20,7 @@ COOKIE_NAME = "geointel_session"
def _session_from_principal(principal: AuthPrincipal, *, guest_access_enabled: bool) -> AuthSession:
settings = get_settings()
return AuthSession(
authentication_required=True,
authenticated=True,
@@ -25,6 +28,7 @@ def _session_from_principal(principal: AuthPrincipal, *, guest_access_enabled: b
expires_at=datetime.fromtimestamp(principal.expires_at, tz=UTC),
role=principal.role,
guest_access_enabled=guest_access_enabled,
authentik_enabled=AuthentikOidcService(settings).enabled,
guest_project_id=principal.project_id,
)
@@ -32,11 +36,13 @@ def _session_from_principal(principal: AuthPrincipal, *, guest_access_enabled: b
def _session_payload(request: Request) -> AuthSession:
settings = get_settings()
guest_access_enabled = settings.auth_enabled and settings.guest_access_enabled
authentik_enabled = AuthentikOidcService(settings).enabled
if not settings.auth_enabled:
return AuthSession(
authentication_required=False,
authenticated=True,
guest_access_enabled=False,
authentik_enabled=False,
)
principal = AuthService.verify_session_token(request.cookies.get(COOKIE_NAME), settings)
if principal is None:
@@ -44,6 +50,7 @@ def _session_payload(request: Request) -> AuthSession:
authentication_required=True,
authenticated=False,
guest_access_enabled=guest_access_enabled,
authentik_enabled=authentik_enabled,
)
return _session_from_principal(
principal,
@@ -124,6 +131,35 @@ def login(payload: AuthLoginRequest, request: Request, response: Response) -> Au
)
@router.get("/authentik/start")
def authentik_start(request: Request) -> RedirectResponse:
settings = get_settings()
service = AuthentikOidcService(settings)
try:
location, flow = service.start()
except Exception as exc:
raise AppError(code="AUTHENTIK_UNAVAILABLE", message="Authentik is momenteel niet beschikbaar.", status_code=status.HTTP_503_SERVICE_UNAVAILABLE) from exc
response = RedirectResponse(location, status_code=status.HTTP_302_FOUND)
forwarded_proto = request.headers.get("x-forwarded-proto", "").split(",", 1)[0].strip().lower()
response.set_cookie("geointel_oidc_flow", flow, max_age=600, httponly=True, secure=forwarded_proto == "https" or request.url.scheme == "https", samesite="lax", path=f"{settings.api_prefix}/auth/authentik")
return response
@router.get("/authentik/callback")
def authentik_callback(request: Request, code: str = "", state: str = "") -> RedirectResponse:
settings = get_settings()
service = AuthentikOidcService(settings)
try:
service.finish(code=code, state=state, flow_cookie=request.cookies.get("geointel_oidc_flow", ""))
token = AuthService.create_session_token(settings.auth_username or "operator", settings)
except Exception:
return RedirectResponse(f"{settings.public_base_url.rstrip('/')}?authentik=error", status_code=status.HTTP_302_FOUND)
response = RedirectResponse(settings.public_base_url.rstrip("/") + "/", status_code=status.HTTP_302_FOUND)
_set_session_cookie(request=request, response=response, token=token, max_age=settings.auth_session_ttl_seconds)
response.delete_cookie("geointel_oidc_flow", path=f"{settings.api_prefix}/auth/authentik")
return response
@router.post("/guest", response_model=AuthSessionEnvelope)
def guest_login(
request: Request,