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

Интеграция с identity

Зачем нужно

Документ описывает, как 6 не-identity доменов потребляют аутентификацию и авторизацию из identity, как распространяется токен пользователя и как передаётся контекст действия (ребёнок, организация).

Главная схема

  • identity — единственный authorization server экосистемы.
  • Все frontend-приложения логинят пользователя через identity (OIDC).
  • Все backend-сервисы валидируют access tokens через JWKS identity (offline) и опционально через introspection.
  • Service-to-service вызовы используют OAuth client_credentials grant с scoped permissions.
  • Действие в контексте ребёнка/организации передаётся отдельным заголовком X-Actor-Context, не подменяя sub.

Поток входа пользователя в frontend

  1. lms-web редиректит неаутентифицированный запрос на https://id.systematika.tld/authorize?... (OIDC + PKCE).
  2. identity показывает экран входа.
  3. После успешного входа возвращается authorization code.
  4. Code exchange выполняет доверенный backend-компонент: BFF/Next server route приложения или identity callback flow. Browser JS не получает refresh token.
  5. Refresh token хранится только в HttpOnly Secure SameSite=Strict cookie на домене identity или в защищённом storage backend-приложения. Access token хранится в memory и обновляется через backend-controlled refresh endpoint.
  6. Refresh endpoint защищён CSRF-механизмом, rotation и device binding; cross-subdomain refresh требует отдельного ADR с точной cookie/session моделью.

Access token

  • Формат: JWT, RS256.
  • Контракт access token закреплён в ADR-033.
  • Время жизни: 15 минут.
  • iss = https://id.systematika.tld.
  • aud — список audience (домены, для которых токен валиден).
  • subuser_id.
  • scope — пространство-разделённые scopes.
  • Дополнительные claims: auth_method, permission_version или bounded permissions summary. family_ids/organization_ids допускаются только как bounded context summary для first-party apps. PII such as email belongs to ID token/userinfo and must not be required in API access token.

Refresh token

  • Хранится только в HttpOnly cookie или в защищённом storage backend-приложения.
  • Ротируется при каждом обновлении.
  • Хранится с device binding и метаданными (см. identity/security.md).

Actor context

Контекст действия (actor_context) — отдельный заголовок:

X-Actor-Context: {"kind":"child_delegated","studentProfileId":"...","familyGroupId":"..."}

Возможные значения kind:

  • personal — пользователь действует от себя (по умолчанию, заголовок можно опускать);
  • child_delegated — взрослый действует от имени student_profile ребёнка;
  • family_adult — взрослый смотрит или управляет данными student_profile ребёнка без подмены;
  • organization_member — пользователь действует от организации;
  • team_member — пользователь действует от команды;
  • admin — администратор;
  • oauth_client — внешний сервис;
  • plugin — плагин.

Сервер обязан проверить, что пользователь имеет право на этот контекст (через identity API или claims в токене), прежде чем выполнять действие. X-Actor-Context является заявленным намерением клиента, а не доверенным фактом. Backend вычисляет effective actor context из JWT sub, permission checks identity и ownership целевого объекта. Поле initiatedBy не принимается от клиента: оно всегда выводится из JWT sub. При конфликте JWT, actor context и ownership целевого объекта запрос отклоняется 403.

Для family contexts главным subject является studentProfileId. linkedUserId/subjectUserId может использоваться только как optional linked user reference, если у student_profile есть linked_user_id. Авторизация ребёнка на другом устройстве через child_device_authorization не передаётся через X-Actor-Context: она завершает pre-auth flow и создаёт обычную user_session ребёнка.

Service-to-service

  • Каждый backend-сервис регистрируется как oauth_client с client_credentials grant.
  • client_id и client_secret хранятся в секретах окружения сервиса.
  • Получает короткоживущий access token со своими scopes.
  • Использует токен в заголовке Authorization: Bearer ....

Пример scopes для service tokens:

  • service:crm.entitlements.read
  • service:crm.entitlements.consume
  • service:competitions.results.read

Формат service token:

  • JWT RS256, sub = oauth_client.client_id;
  • actor.serviceClient = client_id;
  • aud содержит только целевой backend-сервис;
  • TTL 5 минут;
  • scopes только в формате service:<permission> из permissions-model.md;
  • генерация только через OAuth client_credentials; ручные long-lived bearer tokens запрещены.

Проверка токена в backend-сервисе

Backend-сервис не доменa identity:

  1. Получает JWKS с identity по https://id.systematika.tld/.well-known/jwks.json, кеширует.
  2. Валидирует подпись, iss, aud, exp, nbf.
  3. Проверяет наличие нужного scope.
  4. Извлекает sub (user_id) и claims.
  5. Проверяет X-Actor-Context через permissions check.
  6. Прокидывает correlation: X-Request-Id, traceparent.

Реализация — общий пакет packages/auth-client.

Получение профиля и ролей

Backend-сервис не дублирует пользовательские данные. Если нужны атрибуты, не входящие в access token:

  • GET https://id.systematika.tld/userinfo — стандартный OIDC userinfo;
  • GET https://id.systematika.tld/api/v2/identity/admin/users/{id} — расширенный профиль (требует scope);
  • кеш ответов: до 60 секунд по user_id.

Пользовательские роли в чужом домене

Роли пользователя глобальные, но пермишны конкретного домена интерпретируются доменом. Подробности — в permissions-model.md.

Logout

  • Frontend вызывает identity end_session.
  • Identity отзывает refresh tokens сессии.
  • Backend-сервисы реагируют на событие identity.session.revoked (см. events-bus.md) и инвалидируют локальный кеш user_id.

Запрещено

  • Реализовывать собственный логин в любом домене кроме identity.
  • Хранить копию пароля.
  • Использовать sub чужого пользователя для подмены.
  • Выдавать access tokens вне identity.
  • Игнорировать X-Actor-Context для действий, требующих контекста.
  • Передавать токены через query string.

Связанные документы