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 провайдера и извлекает роли.
Порядок извлечения ролей:
- роли из
realm_access.roles(realm-роли) иresource_access.<clientId>.roles(client-роли) — изid_token; - если в
id_tokenролей нет — берутся изaccess_token; - имена нормализуются в верхний регистр и фильтруются по известным:
ADMIN,USER,LEAD,AUDITOR; - если известных ролей нет вовсе — пользователь получает
USER.
Настройка Keycloak¶
Realm¶
Заведите отдельный realm (например, synth). Issuer для Synth:
Клиент¶
| Параметр | Значение | Зачем |
|---|---|---|
| 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.
Выдавать роли можно напрямую или через группы (рекомендуется при синхронизации из каталога пользователей):
- группа каталога синхронизируется в Keycloak (например,
synth_admins); - в Keycloak: группа → Role mapping → назначить realm-роль
admin; - участники группы получают роль автоматически.
Проверка: у пользователя в 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 должен
быть блок:
Параметры 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:
Проверка, что роли попадают в 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 |