# Аутентификация: gnexus-auth (SSO)

Все пользовательские сервисы Gnexus подключаются к центральному SSO — gnexus-auth
(Laravel). Сервис не создаёт собственную систему пользователей и не имеет собственной
«страницы входа» — в сервисе только гейт (заглушка с кнопкой **Войти** по OAuth PKCE).

## Правила
- Регистрация OAuth-клиента в gnexus-auth (client_id + client_secret) — владелец делает в админке `/admin/clients`.
- redirect_uri — постоянный HTTPS-адрес сервиса (`<сервис>/auth/callback`).
- Секреты клиента — в `.env` сервиса и в gnexus-creds, не в git.
- Готовые клиентские библиотеки: `gnexus-auth-client-py` (Python), `gnexus-auth-client-php` (PHP).
- Личность пользователя — `sub` из gnexus-auth; локальная запись пользователя зеркалируется (`user_id`, email, имя, профиль), профиль gnexus-auth — источник истины.

## Что у gnexus-auth есть для сервиса
- OAuth 2.0 PKCE (`/auth/login` → callback), scopes `openid email profile roles permissions`.
- Webhook-события `user.*` с HMAC-подписью (см. ниже).
- Язык аккаунта (`profile.locale`) — источник языка интерфейса по умолчанию (см. [i18n.md](i18n.md)).
- Опциональный allowlist сервиса: пусто = пускаем любого залогиненного, иначе список email через запятую.

## Каркас клиента (паттерн gnexus-creds / hard-panel)
- Вход через `/auth/login` только **по явному клику** — никаких авто-редиректов на загрузку страницы.
- SPA-гейт: splash → `GET /me` → `denied` (гейт с кнопкой входа) или `ready` (интерфейс).
- Сессия — httpOnly-cookie своего имени (например `ghard_session`) из SQLite, TTL ~7 суток; state/PKCE тоже в SQLite (`oauth_states`).
- `401` из API → ивент `unauthenticated` → сессии нет → гейт (не редирект).
- `GET /me` возвращает `{auth_enabled, user: {user_id, email, display_name, avatar_url}, locale…}`.
- Личная настройка пользователя (`PATCH /me`) — **только по cookie-сессии**: у Bearer-токенов нет личности (Bearer-админ → 401).
- При auth-off (client_id пуст) панель/сервис открыты без логина, личные настройки — в localStorage браузера.

## Вариант Synapse (SPA держит Bearer, без cookie-сессии)
Допустимый вариант каркаса для SPA без серверной cookie-сессии (референс
gnexus-synapse): access/refresh-токены хранит сам SPA (localStorage),
каждый API-запрос — `Authorization: Bearer <access>`, и личность на сервере
устанавливается **call-home в `/oauth/userinfo` на каждый запрос** (токены
opaque; локальной проверки нет). Отличия от cookie-паттерна:
- Никакой локальной записи пользователя и httpOnly-cookie; сессий на сервере нет —
  отзыв видит call-home сразу. Личная настройка пользователя (`PATCH /me`) работает
  по Bearer: личность у Bearer-токена есть, пока проходит userinfo.
- Per-user override локали хранится в минимальной таблице `user_prefs` (ПК `sub`,
  locale + служебные флаги) — не полноценная зеркальная запись, а микрорекомендация.
- `401` из API тот же сигнал гейта: SPA делает один тихий refresh, после
  повторного `401` — гейт, poll `/me` держит сессию живой.
- Single sign-out по-прежнему нужен: выданные tokens кэшируются на стороне
  сервиса (`store_login` по user_id) и отзываюся вебхуком `auth.logout`/
  `global_logout`/`session.revoked`, т.к. SSO-сервер не отзывает access-токены
  по прямому logout.

## Вебхуки gnexus-auth (обязательно для клиента)
Роут `/webhooks/gnexus-auth`, подпись HMAC в заголовках (`t=<ts>,v1=<hmac>`), секрет — `*AUTH_WEBHOOK_SECRET`:
- **profile.update** — обновить в локальной зеркальной записи имя, аватарку и профиль (`profile` хранится verbatim JSON). Не должен затирать локальные override-ы пользователя (например override языка).
- **global_logout** — удалить все сессии сервиса, чей user_id совпал (авто-логаут после смены пароля/выхода).
- Роут регистрируется со двумя спеллингами (со слэшем и без) — защита от SPA catch-all.

## Матрица доступа сервиса
| Кто | Как | Для чего |
|---|---|---|
| Браузер | cookie-сессия через SSO | UI, `PATCH /me` |
| Скрипты, MCP, ИИ-агенты | Bearer API-токен сервиса (см. [mcp.md](mcp.md)) | REST API |
| Машинные агенты | ключи агрегатов (`ghm_…`, `X-Server-Key`) | отдельные каналы (ingest) |

## Ссылки
- Репозиторий: https://git.gnexus.space/root/gnexus-auth
- Клиенты: `gnexus-auth-client-py`, `gnexus-auth-client-php`
- Факты о сервисе: gnexus-book → `10-systems/services/gnexus-auth.md`