Перейти к содержанию

SSO / Keycloak

Synth использует OpenID Connect (OIDC) для корпоративного входа: backend выступает OIDC-клиентом Keycloak, получает токены, извлекает из них роли и проверяет доступ на каждом запросе.

Плейсхолдеры

В примерах ниже <keycloak-host>, <realm>, <synth-host> и <client-secret> — условные значения. Подставьте адреса и секреты своего контура; секреты храните вне репозитория.

Как Synth работает с Keycloak

Backend подключается к Keycloak как confidential-клиент (аутентификация по client_secret). Поддерживаются два способа входа:

Способ Эндпоинт Когда используется
Authorization Code GET /auth/login → редирект на Keycloak → GET /auth/callback Стандартный браузерный вход
ROPC (логин/пароль) POST /auth/login с username и password Вход прямо в интерфейсе, без редиректа

После обмена кода или пароля backend верифицирует id_token и access_token через JWKS провайдера и извлекает роли.

Порядок извлечения ролей:

  1. роли из realm_access.roles (realm-роли) и resource_access.<clientId>.roles (client-роли) — из id_token;
  2. если в id_token ролей нет — берутся из access_token;
  3. имена нормализуются в верхний регистр и фильтруются по известным: ADMIN, USER, LEAD, AUDITOR;
  4. если известных ролей нет вовсе — пользователь получает USER.

Настройка Keycloak

Realm

Заведите отдельный realm (например, synth). Issuer для Synth:

https://<keycloak-host>/realms/<realm>

Клиент

Параметр Значение Зачем
Client ID synth Должен совпадать с clientId в конфиге Synth
Client authentication ON (confidential) Аутентификация по client_secret
Standard flow ON Для authorization code (GET /auth/callback)
Direct Access Grants ON Для входа по логину/паролю (ROPC)
Valid redirect URIs https://<synth-host>/auth/callback Куда Keycloak возвращает пользователя
Web origins https://<synth-host> Разрешённые CORS-источники

Роли

Создайте realm-роли с точными именами в нижнем регистре: admin, user, lead, auditor — backend сам приведёт их к верхнему регистру.

Имя роли важно

Роль должна входить в известный список. administrator, synth_admin, admins и подобные будут отброшены, и пользователь получит USER.

Выдавать роли можно напрямую или через группы (рекомендуется при синхронизации из каталога пользователей):

  1. группа каталога синхронизируется в Keycloak (например, synth_admins);
  2. в Keycloak: группа → Role mapping → назначить realm-роль admin;
  3. участники группы получают роль автоматически.

Проверка: у пользователя в Role mapping должны отображаться эффективные роли.

Client scopes и мапперы ролей

Роли попадают в токены через протокольные мапперы client scopes (обычно скоуп roles): мапперы realm roles (realm_access.roles) и client roles (resource_access.<clientId>.roles). У обоих должны быть включены опции:

Опция маппера Значение Эффект
id.token.claim true Роли попадают в id_token
access.token.claim true Роли попадают в access_token
multivalued true Роли передаются массивом

Типовая проблема

Если у маппера выключен id.token.claim, роли есть только в access_token. Backend умеет брать их оттуда, но при выключенном и access.token.claim пользователь всегда получает USER, и админские разделы не появляются, даже если роль выдана корректно.

Проверьте id_token (например, декодировав его вручную) — в payload должен быть блок:

{
  "realm_access": {
    "roles": ["user", "admin"]
  }
}

Параметры Synth

config.yaml

auth:
  oidc:
    enabled: true
    issuer: "https://<keycloak-host>/realms/<realm>"
    clientId: "synth"
    clientSecret: "<client-secret>"
    redirectUri: "https://<synth-host>/auth/callback"

Переменные окружения

Переменная Назначение
SYNTH_OIDC_ENABLED true — включить OIDC
SYNTH_OIDC_ISSUER Issuer: https://<keycloak-host>/realms/<realm>
SYNTH_OIDC_CLIENT_ID Client ID (например, synth)
SYNTH_OIDC_CLIENT_SECRET Секрет клиента — хранить в секретах, не в конфиге
SYNTH_OIDC_REDIRECT_URI Redirect URI: https://<synth-host>/auth/callback

Приоритет источников — как для остальных настроек: переменные окружения выше config.yaml (см. Конфигурация).

Helm-развёртывание

backend:
  oidc:
    enabled: true
    issuer: "https://<keycloak-host>/realms/<realm>"
    clientId: "synth"
    redirectUri: "https://<synth-host>/auth/callback"
  • clientSecret не указывается в values — он кладётся в секрет как SYNTH_OIDC_CLIENT_SECRET;
  • если Keycloak подписан внутренним CA, укажите backend.extraEnv.NODE_EXTRA_CA_CERTS с путём к CA-сертификату и смонтируйте его. Иначе backend не сможет проверить JWT и обратиться к issuer.

Проверка и диагностика

Доступность issuer:

curl -s https://<keycloak-host>/realms/<realm>/.well-known/openid-configuration | jq .issuer

Проверка, что роли попадают в id_token:

curl -s -X POST "https://<keycloak-host>/realms/<realm>/protocol/openid-connect/token" \
  --data-urlencode "grant_type=password" \
  --data-urlencode "client_id=synth" \
  --data-urlencode "client_secret=<client-secret>" \
  --data-urlencode "username=<user>" \
  --data-urlencode "password=<password>" \
  --data-urlencode "scope=openid" \
  | jq -r .id_token | cut -d. -f2 | base64 -d 2>/dev/null | jq .

В декодированном id_token должны присутствовать realm_access.roles с ожидаемой ролью.

Симптомы и причины

Симптом Причина Решение
Все получают USER, админки нет Маппер не включает роли в id_token / access_token Включить id.token.claim и access.token.claim у мапперов ролей
Роль есть в Keycloak, но в UI её нет Имя роли не из известного списка Переименовать роль в admin / user / lead / auditor
Роль выдана как client-роль другому клиенту resource_access.<clientId> не совпадает с clientId Synth Выдать роль клиенту synth или назначить realm-роль
401 при входе, backend не проверяет JWT Backend не доверяет CA Keycloak Настроить NODE_EXTRA_CA_CERTS

Что дальше