Подсистемы RP¶
Несколько подсистем в рамках одного RP. RP регистрируется только администратором; подсистемы регистрируются автоматически на основе доверия к RP (find-or-create). На экране подтверждения в телефоне название подсистемы показывается КРУПНО, а название родительского RP — ниже МЕЛКИМ шрифтом.
Протокол (основная идея)¶
В вызов RP не добавляется новое поле — поле relyingPartyName в теле переиспользуется как название
подсистемы. Для зарегистрированного RP это поле раньше перезаписывалось значением rp.Name и было
бессмысленным (защита от подмены). Теперь:
Сервер (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; AndroidSessionInfo+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 ниже МЕЛКО.