Спецификация интеграции SSO·SCIM
Спецификация значений и поведения для интеграции OIDC, SAML 2.0 и SCIM 2.0.
Порядок настройки на экране см. в разделе SSO и SCIM.
В таблицах {API} указан адрес API Keural для используемой среды (например, https://api.keural.ai). Используйте адрес, который показан на экране Управление организацией → SSO.
Сводка значений для регистрации
| Параметр | Значение |
|---|---|
| URI перенаправления OIDC | {API}/api/sso/callback |
| Начало входа OIDC·SAML | {API}/api/sso/{orgId}/login |
| SAML SP EntityID | {API}/api/sso/saml/metadata |
| SAML ACS URL | {API}/api/sso/saml/callback |
| SCIM Base URL | {API}/scim/v2 |
Вход всегда начинается со стороны Keural (SP-initiated). Если на экране входа ввести адрес электронной почты из зарегистрированного домена, выполняется переход на адрес начала входа. При успехе открывается главный экран Keural, при ошибке происходит возврат на /login?error=sso.
OIDC
| Параметр | Поведение |
|---|---|
| Поток | Authorization Code |
| Запрос authorize | response_type=code, client_id, redirect_uri, scope=openid email profile, state, nonce |
| state · nonce | Одноразовые, действительны 10 минут. Если окно входа открыто дольше 10 минут, вход начинается заново. |
| Обмен токена | POST на token endpoint, application/x-www-form-urlencoded. Аутентификация клиента: client_secret_post (client_id и client_secret в теле запроса). |
| Подпись ID-токена | Принимается только RS256. Ключ ищется в jwks_uri по kid. |
| Проверка ID-токена | Подпись, exp, aud содержит client_id, nonce совпадает, iss совпадает с Issuer URL (завершающий / игнорируется) |
Утверждение email в ID-токене. Если его нет, userinfo endpoint вызывается с Authorization: Bearer <access_token>, и email читается оттуда. | |
| Имя | Утверждение name в ID-токене |
| Endpoints | Ищутся в /.well-known/openid-configuration по адресу Issuer URL. Если IdP не удаётся найти, адреса authorization, token, JWKS и userinfo вводятся напрямую в Показать расширенные настройки. |
| Ограничение по времени | 15 секунд на каждый вызов IdP |
Приложение IdP должно разрешать client_secret_post. Если разрешены только client_secret_basic или private_key_jwt, обмен токена завершится ошибкой.
SAML 2.0
| Параметр | Поведение |
|---|---|
| Запрос | AuthnRequest отправляется через привязку HTTP-Redirect. Запрос не подписывается. |
| Содержимое запроса | Destination = IdP SSO URL, AssertionConsumerServiceURL = ACS URL, ProtocolBinding = HTTP-POST, Issuer = SP EntityID, NameIDPolicy Format=…:nameid-format:unspecified, AllowCreate=true |
| Ответ | Привязка HTTP-POST, SAMLResponse и RelayState. RelayState должен возвращаться в том же значении, которое отправил Keural. |
| Подпись | В Response или Assertion должна быть действительная подпись. Для проверки используется только сертификат, зарегистрированный на экране. |
| Assertion | В ответе должно быть ровно одно утверждение (Assertion). Зашифрованные утверждения (EncryptedAssertion) не принимаются. |
| Статус | StatusCode: urn:oasis:names:tc:SAML:2.0:status:Success |
| Issuer | Если указан IdP EntityID, он должен совпадать с Issuer в утверждении. |
| Условия | NotBefore и NotOnOrAfter (допуск 300 секунд), Audience = SP EntityID |
| SubjectConfirmationData | Recipient = ACS URL, NotOnOrAfter (допуск 300 секунд), InResponseTo = ID запроса, который отправил Keural |
Атрибут, у которого Name или FriendlyName имеет одно из следующих значений (без учёта регистра): email, mail, emailAddress, E-Mail-Addresses, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress. Если таких нет, используется NameID, содержащий @. | |
| Имя | displayName, name, cn, http://schemas.microsoft.com/identity/claims/displayname |
| Сертификат | Один X.509-сертификат, в формате PEM или Base64 (DER). Для замены введите новый сертификат и сохраните. |
| Метаданные | Вместо загрузки XML метаданных IdP введите три значения: IdP EntityID, IdP SSO URL и сертификат. |
Сопоставление пользователя
| Параметр | Поведение |
|---|---|
| Условие перенаправления в SSO | Часть адреса после @ точно совпадает с одним из значений списка Домены электронной почты (без учёта регистра). Поддомены регистрируются отдельно. |
| Поиск аккаунта | Аккаунт Keural ищется по адресу электронной почты (в нижнем регистре), который вернул IdP. |
| Если аккаунта нет | Если включено Автоматически создавать пользователей при первом входе (JIT), аккаунт создаётся. Если выключено, вход отклоняется. |
| Вступление в организацию | Если пользователь не является участником организации, он вступает как Член. Аккаунт, у которого заняты все места или который принадлежит другой организации, может войти, но не вступает в организацию. |
| Условия сохранения домена электронной почты | Домены, подтверждённые через DNS TXT. Публичные почтовые домены исключаются. |
Подтверждение домена
| Параметр | Значение |
|---|---|
| Тип записи | TXT |
| Хост | Зарегистрированный домен как есть (например, acme.com). Префиксный поддомен не используется. |
| Значение | keural-verify=<UUID>, точно как показано на экране |
| Момент проверки | Проверяется один раз при нажатии Подтвердить проверку. |
SCIM 2.0
| Параметр | Значение |
|---|---|
| Base URL | {API}/scim/v2 |
| Аутентификация | Authorization: Bearer <SCIM-токен>. Токен начинается с scim_ и показывается только один раз при выпуске. |
| Content-Type | Запрос: application/scim+json или application/json. Ответ: application/scim+json |
| Коды состояния | Возвращаются реальные коды HTTP. Тело ошибки имеет формат SCIM Error (schemas, status, detail, scimType). |
Endpoints
| Метод | Путь | Поведение |
|---|---|---|
GET | /Users | Список. filter=userName eq "…", startIndex (по умолчанию 1), count (по умолчанию 100, максимум 200) |
GET | /Users/{id} | Получить одного пользователя |
POST | /Users | Создать (201). Если такой email уже есть, возвращается 409. |
PATCH | /Users/{id} | Изменить active, displayName, name.formatted, name.givenName |
PUT | /Users/{id} | Применяет displayName, name и active. Если active не указан, пользователь считается активным. |
DELETE | /Users/{id} | Приостанавливает аккаунт (204). Данные не удаляются. |
GET | /Groups, /Groups/{id} | Получить две фиксированные группы. filter=displayName eq "…" |
PATCH | /Groups/{id} | Добавить, удалить или заменить участников |
GET | /ServiceProviderConfig, /ResourceTypes, /Schemas | Получить поддерживаемые возможности |
Атрибуты пользователя
| Атрибут | Назначение |
|---|---|
userName | Email. Если его нет, используется emails[0].value. |
displayName · name.formatted · name.givenName+name.familyName | Отображаемое имя |
active | false: приостановка (блокирует вход), true: восстановление |
id | ID пользователя Keural. Содержится в ответе. |
externalId не сохраняется. Чтобы найти пользователя в IdP, используйте фильтр userName.
Группы
| id | displayName | При добавлении участника | При удалении участника |
|---|---|---|---|
keural-admins | Keural Admins | Администратор | Член |
keural-members | Keural Members | Член | Без изменений |
Роль владельца организации через SCIM не меняется. Создание и удаление групп (POST и DELETE /Groups) возвращает 501. value участника группы: ID пользователя Keural (id из /Users).
Поддерживаемые возможности
| Возможность | Поддержка |
|---|---|
| PATCH | Да |
| Фильтры | userName eq, displayName eq |
| Bulk · сортировка · ETag · смена пароля | Нет |
Ошибки
| Статус | scimType | Причина |
|---|---|---|
400 | invalidValue | Отсутствуют и userName, и emails |
401 | — | Токен отсутствует или неверен, либо SCIM-подготовка отключена |
404 | — | Пользователь или группа не принадлежат этой организации |
409 | uniqueness | Аккаунт с таким email уже существует, или превышено число мест |
501 | — | Создание или удаление групп |