Перейти к содержимому

Как подключить приложение (OAuth-клиент)

Инструкция для администратора Zentro и команды приложения: зарегистрировать одно приложение, которое входит через Zentro (SSO) или ходит в API без пользователя, и дать ему токены к API.

Подключаете целый сервис (свои роли, приложения, API) — удобнее мастер: Сервисы → Подключить сервис (Подключение сервиса). Эта инструкция — для приложения вне сервиса или когда нужна ручная настройка: сторонний портал, которому нужен только вход; интеграция, которая ходит в чужой API; отдельное мобильное приложение.

  • Нужна роль IDENTITY_ADMIN или права IDENTITY:OAUTH_CLIENT:* (и IDENTITY:OAUTH_RESOURCE:*, если заводите API).
  • Нужен второй фактор — панель попросит подтвердить себя и продолжит.
  • Узнайте у команды приложения:
    • что это за приложение (таблица ниже);
    • адреса возврата после входа — по одному на каждую среду, точно: https://portal.example.com/auth/callback; для мобильного — адрес его схемы (com.example.app:/oauth/callback);
    • куда вернуть после выхода; умеет ли приложение принимать сообщение о выходе (back-channel);
    • в какие API оно будет ходить и с какими правами.
  • Среды заводятся отдельно. Dev и prod — разные инсталляции Identity: в каждой свой client_id, свой секрет.
Приложение Что выбрать Секрет
Сайт или панель со своим сервером (BFF, серверный рендеринг) «Люди», не публичный есть — хранится на сервере
Приложение целиком в браузере (SPA без сервера) или мобильное «Люди», публичный клиент нет — вместо него PKCE
Бэкенд, фоновая задача, интеграция — без пользователя «Сервис — без пользователя» есть

Для SPA лучше свой небольшой сервер (BFF), который держит токены у себя: так токены не попадают в браузер. Публичный клиент — когда сервера нет совсем.

OAuth-клиенты → Зарегистрировать клиента. Сначала — «Кто будет входить».

Поле Что вписать
Название Его увидят пользователи на экране согласия: Портал поставщиков
Redirect URI Адреса возврата после входа, по одному в строке. Совпадение точное: схема, хост, порт, путь, слэш в конце
Публичный клиент Только для SPA без сервера и мобильных. Потом не меняется — только пересозданием
Scopes openid profile email: openid обязателен, profile — логин, email — почта. По умолчанию — только openid
Post-logout redirect URI Куда вернуть после выхода
Back-channel logout URI Адрес сервера приложения, куда Zentro сообщит о выходе пользователя. Нужен https и публичный адрес. Самый надёжный способ выхода
Разрешить доставку во внутреннюю сеть Если адрес выхода внутренний (http://portal-bff:3000/…). Работает, только когда в Настройках задано oidc.backchannel_private_cidrs
Front-channel logout URI Страница приложения, которую браузер откроет в скрытом окне при выходе. Браузеры часто это блокируют — лучше back-channel
Пространство прав Если приложение должно знать права пользователя в каком-то сервисе — его ключ (RHM). Права придут в id_token и userinfo. Пусто — прав приложение не получит
Приложение организации — не спрашивать согласие Только для своих приложений: пользователь не увидит «Разрешить». Стороннему — не ставьте
Тип subject public — у человека один идентификатор во всех приложениях (если приложение сопоставляет людей с другими системами); pairwise — у каждого приложения свой псевдоним (приватнее). Потом не меняется
Sector identifier Только для pairwise, если приложений с общим псевдонимом несколько; обычно пусто

Нужно только название: billing-sync. Адресов возврата, выхода и согласия у такого приложения нет.

Панель покажет client_id, секрет — один раз (у публичного клиента секрета нет) и сводку «Что передать команде сервиса».

Секрет больше не покажется нигде. Скопируйте сразу и передайте команде защищённым каналом, не открытым текстом в чат или тикет. Потеряли — «Новый секрет» (ниже).

Если приложение будет вызывать API сервиса от имени пользователя или само, API должен быть заведён как ресурс, а приложение — привязано к нему. Без привязки токен для этого API приложению не выдадут.

Если API ещё не заведён — Ресурсы OAuth → Завести ресурс:

Поле Что вписать
Название Delivery API
Адрес (audience) Идентификатор API в токене: https://delivery.example.com/api или rhm-delivery. API будет проверять его в каждом токене
Пространство прав Ключ сервиса заглавными: DELIVERY. Права пользователя DELIVERY:… попадут в токены этого API

Адрес и пространство прав после создания не меняются — только удалить ресурс и завести заново (привязки придётся настроить снова).

Привязать приложение — OAuth-клиенты → Ресурсы в строке приложения → Привязать ресурс, выбрать API и потолок прав — сколько из прав пользователя приложение может унести в этот API:

Потолок Что уйдёт в токене
DELIVERY:*:* все права пользователя в DELIVERY
DELIVERY:*:VIEW только чтение, даже если у человека есть запись
пусто прав нет, только доступ — так настраивают сервисные приложения

В токене — права пользователя ∩ потолок. Панель подсказывает права из ролей этого пространства. Потолок можно поменять в любой момент — действует со следующего токена.

Кто ходит в API и с каким потолком — Ресурсы OAuth → Клиенты в строке API.

Сводка «Что передать команде сервиса» — с кнопкой копирования. Для приложения для людей:

Приложение: Портал поставщиков
Issuer: https://id.example.com ← адрес Identity этой среды
Discovery: https://id.example.com/.well-known/openid-configuration
Client ID: cid_…
Client secret: передаётся отдельно (у публичного: «нет (публичный клиент, PKCE)»)
Redirect URI: https://portal.example.com/auth/callback
Пространство прав: RHM

Для сервисного — вместо адресов возврата Получение токена: client_credentials и Token endpoint.

Секрета в сводке нет намеренно — сводку можно переслать, секрет передайте отдельно. Допишите адрес (audience) каждого API, к которому привязали приложение.

Всё, что нужно, приложение берёт из Discovery — адреса не зашиваются.

Вход пользователя — Authorization Code + PKCE (S256, обязателен всем, и с секретом тоже):

  1. Браузер → authorize с client_id, redirect_uri, response_type=code, scope, code_challenge, code_challenge_method=S256, state, nonce. Пользователь входит на экране Zentro, при необходимости — «Разрешить».
  2. Возврат на redirect_uri с code → сервер приложения меняет его на token с code_verifier (и секретом — client_secret_basic или client_secret_post). Код живёт 60 секунд.
  3. Ответ: id_token (кто вошёл; permissions — права в пространстве приложения), access_token, refresh_token.

Полезное: prompt=login — всегда спросить пароль заново, max_age=N — если вход был дольше N секунд назад, prompt=none — тихая проверка входа без экрана.

Вызов API: access_token без resource= годен только для userinfo. Для API — токен resource=<адрес API> (при обмене кода, на каждом refresh заново — он не запоминается).

Сервисное приложение:

Окно терминала
curl -u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=client_credentials \
-d resource=https://delivery.example.com/api \
https://id.example.com/oidc/token
# → {"access_token":"…","expires_in":900,"token_type":"Bearer"}

Без resource= токен ни в какой API не годится. Токен живёт 15 минут (по умолчанию, см. «Сроки токенов» ниже; срок — в expires_in), refresh нет — за новым приходят так же. В токене sub = azp = client_id, прав нет: API пускает сервис по client_id.

Сроки приложение берёт из ответа: expires_in — сколько живёт access_token; refresh-токен действует, пока сессия приложения не простояла дольше своего срока или не прошёл предел — тогда token отвечает invalid_grant и пользователь входит заново. Свои сроки приложение не задаёт.

Проверка токена в API — локально по ключам из jwks_uri: подпись, exp, iss = issuer из Discovery, aud = свой адрес (чужой — отклонять), дальше permissions (пользователь) или azp (сервис).

Ключ берите по kid из заголовка токена и принимайте RS256, ES256 и EdDSA: по умолчанию Zentro подписывает RS256, но администратор может сменить алгоритм или ключ. Ключи кэшируйте (час — нормально), а на незнакомый kid перечитывайте jwks_uri — иначе после смены ключа API начнёт отклонять новые токены. Zentro публикует новый ключ за сутки до того, как начнёт им подписывать.

Выход: end_session_endpoint из Discovery с id_token_hint и post_logout_redirect_uri. Сервер приложения при выходе ещё и отзывает свой refresh-токен — revocation_endpoint (RFC 7009) с секретом клиента: гаснет сессия этого приложения, вход в Zentro и другие приложения остаются. Сервисное приложение так же отзывает свой токен, когда задача закончена.

Проверка «токен ещё жив» — introspection_endpoint (RFC 7662), только с секретом клиента: видны свои токены и токены, выданные для API своего сервиса; остальное — {"active": false}.

  1. «Проверить» в строке приложения (Приложения) — панель сверит настройки (секрет, PKCE, адреса по http) и попробует открыть адреса возврата и выхода с сервера. Ошибки — первыми. «Пропущено» — не ошибка: внутренний адрес или схема приложения (myapp://) открывается у пользователя, сервер их не видит. «С сервера недоступен» — тоже не обязательно беда: браузер пользователя может видеть адрес, который не видит сервер.
  2. Откройте приложение и нажмите «Войти» — откроется экран входа Zentro.
  3. Войдите. Если приложение не «приложение организации» — экран согласия с его названием.
  4. Вы вернулись в приложение, оно знает, кто вы (и ваши права, если задано пространство).
  5. Вызов API из приложения проходит; без привязки к API — invalid_target.
  6. Сервисное: запрос токена из примера выше отдаёт access_token.

По умолчанию приложение получает сроки установки (Настройки → Безопасность, «Сроки входа и токенов») или своего сервиса. Свои — в форме приложения:

Поле Что задаёт
Срок токена доступа (access_token_ttl) От 1 минуты до 24 часов
Простой сессии (refresh_idle_timeout) Сколько сессия живёт, если приложение не обновляет токены
Предел сессии (refresh_max_lifetime) Сколько сессия живёт от входа; 0 — без предела
  • «Наследуется» — значение берётся у сервиса, у сервиса — из настроек установки; рядом показано действующее значение.
  • Простой и предел есть только у приложения со входом пользователей и refresh_token. У сервисного приложения — только срок токена доступа.
  • У API (ресурса) — «Максимальный срок токена» (max_access_token_ttl): токены к этому API живут не дольше, даже если у приложения срок больше. Так владелец API решает, сколько живёт токен к нему.
  • Новый срок токена — со следующего токена; простой и предел — с ближайшего обновления токенов.
  • Сессия приложения, которая простояла дольше срока, завершается при следующей попытке обновить токены; уведомление о выходе (back-channel) приложению при этом не отправляется — оно и так получило отказ.

OAuth-клиенты → строка приложения:

  • «Изменить» — адреса, scopes, выход, пространство прав, согласие, статус. Секрет правкой не меняется. Тип subject и «публичный клиент» не меняются — только пересозданием (новый client_id, интеграцию придётся перенастроить). У сервисного меняются только название и статус.

  • Статус «Отключён» — временно остановить: вход, обмен кода, refresh и client_credentials прекращаются сразу. Включили — работает как было.

  • «Новый секрет» — выпустить новый секрет; client_id, привязки и согласия сохранятся:

    • «Действует ещё сутки» — плановая смена: старый секрет работает ещё 24 часа, команда успеет переключиться;
    • «Перестаёт работать сразу» — если секрет утёк.

    Новый секрет показывается один раз. У публичного клиента секрета нет.

  • «Ресурсы» — привязки к API и потолки прав (шаг 2).

  • «Отозвать токены» — экстренно, если приложение скомпрометировано или ведёт себя странно, а удалять его нельзя: все пользователи выходят из него (приложению уходит уведомление о выходе), всё выданное ему до этого момента перестаёт действовать, включая сервисные токены. Клиент, привязки и согласия остаются — дальше приложение получает токены как обычно. Утёк секрет — ещё и «Новый секрет» → «Перестаёт работать сразу». Нужен второй фактор.

  • «Удалить» — необратимо: вход и refresh прекращаются сразу, согласия пользователей и привязки к API снимаются, выданные токены отзываются. Вернуть нельзя — только регистрировать заново, с новым client_id и секретом.

Про отозванные токены: Zentro (userinfo, интроспекция) перестаёт их принимать сразу. API, которые проверяют токен у себя по ключам, пускают по уже выданному до его срока — до 15 минут; кому нужно сразу — пусть спрашивают Zentro (introspection_endpoint из Discovery).

Пользователь сам видит приложения, которым дал доступ, и может отозвать согласие — в кабинете, Приложения с доступом.

Что видно Причина Что сделать
Ошибка про redirect_uri при входе Адрес не совпал с зарегистрированным Сверить посимвольно: http/https, порт, слэш в конце; добавить адрес в «Изменить»
invalid_client Неверный client_id/секрет, клиент отключён или удалён; или прошли сутки после «Нового секрета» Проверить статус; выпустить новый секрет
invalid_target Приложение не привязано к API, ошибка в адресе API или API выключен Ресурсы в строке приложения; Ресурсы OAuth — адрес и статус
invalid_scope при входе Приложение просит scope, которого нет в его списке (или забыло openid) Добавить scope в «Изменить» или убрать из запроса
invalid_grant при обмене кода Код просрочен (60 с), уже использован или code_verifier не тот Повторить вход; проверить PKCE
API отвечает 401 на токен Токен без resource= или для другого API (aud не тот) Запрашивать токен с resource=<адрес этого API>
В токене нет прав Пустой потолок, нет пространства прав или у человека нет роли Потолок в Ресурсах; пространство — «Изменить»; роль — Пользователи
Пользователь каждый раз видит «Разрешить» Приложение стороннее — так и должно быть; своё — не помечено Для своего: «Приложение организации»
«Доставка во внутреннюю сеть не настроена…» Галка внутренней сети без oidc.backchannel_private_cidrs Задать сети в Настройках — ссылка в ошибке
Вход обрывается на пустой странице У Identity не настроен адрес экрана входа Вопрос к тем, кто разворачивал Identity
Секрет потерян Показывается один раз «Новый секрет»
Все пользователи разом вышли из приложения Кто-то нажал «Отозвать токены» или удалил клиента Проверить аудит; приложение просто запрашивает токены заново
API пускает по отозванному токену API проверяет токен у себя по ключам — отзыв увидит не позже чем через 15 минут Если нужно сразу — проверять через introspection_endpoint