"""Аутентификация через gnexus-auth и гейт админки по ролям.
Токены gnexus-auth — opaque (не JWT), валидация только «call home»:
GET /oauth/userinfo с Bearer-токеном. Отзыв токена и блокировка
пользователя учитываются сервером auth мгновенно. Сам OAuth-поток
(state, PKCE, обмен кода) — в app/api/auth_routes.py через gnexus-gauth.
Доступ к админке — system_role in ("admin", "superadmin").
"""
from fastapi import Depends, HTTPException, Request, status
from gnexus_gauth.dto import AuthenticatedUser
from app.auth.client import get_gauth_client
from app.config import get_settings
ADMIN_ROLES = ("admin", "superadmin")
def get_current_user(
request: Request,
settings=Depends(get_settings),
) -> AuthenticatedUser:
"""Валидирует Bearer-токен через userinfo gnexus-auth."""
auth = request.headers.get("Authorization", "")
if not auth.startswith("Bearer "):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Требуется Bearer-токен gnexus-auth",
headers={"WWW-Authenticate": "Bearer"},
)
try:
# TODO: короткий TTL-кэш userinfo по токену (до 60 c) — после появления нагрузки.
return get_gauth_client().fetch_user(auth.removeprefix("Bearer "))
except Exception:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Токен невалиден или истёк",
headers={"WWW-Authenticate": "Bearer"},
) from None
def require_admin(
user: AuthenticatedUser = Depends(get_current_user),
) -> AuthenticatedUser:
"""Гейт админки: system_role admin или выше, остальным — 403."""
if (user.system_role or "") not in ADMIN_ROLES:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Недостаточно прав: админ-панель доступна роли admin и выше",
)
return user