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

Подсистемы RP

Несколько подсистем в рамках одного RP. RP регистрируется только администратором; подсистемы регистрируются автоматически на основе доверия к RP (find-or-create). На экране подтверждения в телефоне название подсистемы показывается КРУПНО, а название родительского RP — ниже МЕЛКИМ шрифтом.

Протокол (основная идея)

В вызов RP не добавляется новое поле — поле relyingPartyName в теле переиспользуется как название подсистемы. Для зарегистрированного RP это поле раньше перезаписывалось значением rp.Name и было бессмысленным (защита от подмены). Теперь:

POST /v3/auth/... { "relyingPartyUUID": "<RP UUID>", "relyingPartyName": "Хаан Банк Зээл" }

Сервер (newSession, store.go): 1. Определяет RP по секрету (Bearer) (CreatorRP) — UUID из тела не считается доверенным. 2. Разрешает relyingPartyName как подсистему внутри этого RP через resolveSubsystem (find-or-create). Ключ = lower(trim(name)) (name_key), уникален в рамках RP. 3. В сессии: RPName = rp.Name (авторитетное значение, мелкий текст), SubsystemName (крупный текст), SubsystemID.

Если relyingPartyName пусто или совпадает с именем RP → используется подсистема по умолчанию (с тем же именем, что и RP). Легаси-RP (например, веб-демо «Demo Bank») присылают собственное имя, поэтому напрямую попадают в подсистему по умолчанию — обратная совместимость сохранена.

У вызовов first-party (оператора) и dev ("") подсистемы нет (RP не зарегистрирован) — SubsystemName пусто, и телефон показывает только RPName.

Регистрация и защита

  • При регистрации RP всегда создаётся подсистема по умолчанию с тем же именем (Register).
  • Лимит: у каждого RP может быть SMARTID_RP_SUBSYSTEM_MAX (по умолчанию 50) подсистем. При превышении новое автосоздание отклоняется (ErrSubsystemCap → HTTP 429). Существующие подсистемы продолжают работать.
  • Администратор может только редактировать подсистемы: переименовать / объединить / деактивировать. Ручное создание не предусмотрено (их создаёт сам RP).

Счётчики

В строке подсистемы поля auth_count / sign_count / last_used_at инкрементируются атомарно при каждом успешном (OK) завершении сессии (recordSessionActivity, crypto_wired.go). Итог по RP = СУММА по его подсистемам. На панели администратора отображается «Подсистемы (активные/всего)» + счётчики по каждой подсистеме внутри списка RP.

Эндпойнты (админ, /v3/admin/*)

Метод Путь Описание
GET relying-parties/{id}/subsystems Подсистемы RP + счётчики (rp:read)
PATCH relying-parties/{id}/subsystems/{sid} переименование (rp:write)
POST relying-parties/{id}/subsystems/{sid}/deactivate | /activate состояние активности
POST relying-parties/{id}/subsystems/{sid}/merge {targetId} объединение счётчиков src→dst

Файлы

  • БД: server/migrations/V30__rp_subsystems.sql (+ backfill подсистемы по умолчанию для легаси-RP).
  • Модель/репозиторий: persistence/models.go (Subsystem, SubsystemID/Name в сессии), repo/repo.go (Subsystems), repo/inmem, repo/pg, postgres/subsystem_repo.go.
  • Логика: service/memory/store.go (resolveSubsystem, админские операции), crypto_wired.go (счётчики).
  • Телефон: iOS SessionInfo.subsystemName + ContentView/RequestView; Android SessionInfo + RequestView.kt — подсистема КРУПНО, RP ниже МЕЛКО (если имена совпадают — только RP).
  • Админка: admin/src/app/(app)/rps/page.tsx (раскрытие и управление подсистемами), page.tsx (панель).

Замечание по безопасности

Название подсистемы — свободный текст, задаваемый RP (RP теоретически может указать имя вроде «Google»). Защита: (а) подсистема ограничена рамками своего RP; (б) авторитетное имя RP всегда показывается ниже МЕЛКИМ шрифтом; (в) лимит + админские операции merge/rename/deactivate. Это соответствует модели «доверяем RP».

Push-уведомления показывают название подсистемы как основное (<подсистема> (<RP>) — когда имя отличается от RP); если оно пусто — показывается RP. Тело apns.go и payload включают subsystemName. При открытии телефона экран подтверждения по данным sessionInfo показывает подсистему КРУПНО, а RP ниже МЕЛКО.