diff --git a/.env.example b/.env.example index 568112a..efa2c11 100644 --- a/.env.example +++ b/.env.example @@ -13,7 +13,7 @@ REDIS_URL=redis://redis:6379/0 # --- gnexus-auth (SSO) --- -# Базовый URL сервера gnexus-auth (сейчас 192.168.1.167, протокол уточнить). +# Базовый URL сервера gnexus-auth (сейчас 192.168.1.167). GAUTH_BASE_URL=https://192.168.1.167 # id/secret OAuth-клиента — зарегистрировать приложение Synapse в gnexus-auth и подставить сюда. GAUTH_CLIENT_ID=set-me @@ -23,9 +23,11 @@ GAUTH_REDIRECT_URI=http://localhost:8001/auth/callback # Проверять ли TLS-сертификат gnexus-auth (в LAN с self-signed — false). GAUTH_VERIFY_TLS=false +# Секрет подписи вебхуков gnexus-auth — показывается один раз при создании/ротации клиента. +GAUTH_WEBHOOK_SECRET=set-me # Публичный URL SPA для редиректа после callback. Пусто = тот же origin (прод). SPA_PUBLIC_URL=http://localhost:5173 # --- Misc --- -# Явные URL входа, если SPA запускается в dev-режиме на отдельном порту. +# Порт API на хосте. SYNAPSE_PORT=8000 \ No newline at end of file diff --git a/app/api/auth_routes.py b/app/api/auth_routes.py index ed4f506..7327540 100644 --- a/app/api/auth_routes.py +++ b/app/api/auth_routes.py @@ -5,6 +5,7 @@ токена пользователю. """ +import logging from urllib.parse import quote, urlencode from fastapi import APIRouter, Depends, HTTPException, Request @@ -16,6 +17,8 @@ from app.auth.deps import ADMIN_ROLES from app.config import Settings, get_settings +logger = logging.getLogger("synapse.auth") + router = APIRouter(prefix="/auth") @@ -106,4 +109,26 @@ if not auth.startswith("Bearer "): raise HTTPException(status_code=401, detail="Требуется Bearer-токен") get_gauth_client().revoke_token(auth.removeprefix("Bearer ")) - return {"revoked": True} \ No newline at end of file + return {"revoked": True} + + +@router.post("/webhooks") +async def webhooks(request: Request) -> dict: + """Приём вебхуков gnexus-auth (user.*, role.*, client.*, webhook.test). + + Подпись HMAC (`t=...,v1=...`) проверяется через gnexus-gauth с секретом + из env; уведомления о смене пользователя/ролей нужны для мгновенной + инвалидации (TODO: кэш userinfo в задаче #30-#31). + """ + secret = get_settings().gauth_webhook_secret + if not secret: + raise HTTPException(status_code=503, detail="GAUTH_WEBHOOK_SECRET не настроен") + + raw = (await request.body()).decode("utf-8", errors="replace") + try: + event = get_gauth_client().verify_and_parse_webhook(raw, dict(request.headers), secret) + except Exception: + raise HTTPException(status_code=400, detail="Некорректная подпись вебхука") from None + + logger.info("gauth webhook: %s id=%s target=%s", event.event_type, event.event_id, event.target_identifiers) + return {"received": True, "event_type": event.event_type} \ No newline at end of file diff --git a/app/config.py b/app/config.py index 84ffb65..18798c8 100644 --- a/app/config.py +++ b/app/config.py @@ -26,6 +26,8 @@ # gnexus-auth в LAN может сидеть за self-signed TLS (по умолчанию не проверяем), # в проде с нормальным сертификатом выставить GAUTH_VERIFY_TLS=true. gauth_verify_tls: bool = False + # Секрет подписи вебхуков gnexus-auth (виден только при создании/ротации клиента). + gauth_webhook_secret: str = "" # Публичный URL SPA для редиректа после callback. Пусто = тот же origin, # что и API (прод: SPA раздаётся этим же контейнером). Dev: vite :5173.