KelesidaLab Оставить заявку →

Архитектура единого входа

Надёжно внутри · проект архитектуры, редакция 15.10.2026

Как устроен вход в системы, которые мы делаем: кто где хранит данные, через что входят пользователи и почему один сбой не останавливает всё. Схемы — в нотации C4: от общей картины к деталям.

Скачать PDF

Принципы

Кубики, а не монолитВход собирается из готовых проверенных блоков: пароль, второй фактор, телефон, MAX, внешние системы. Проекту — только нужные.
Данные — у владельцаПрофили пользователей и роли хранит сам проект. У системы заказчика — его пользователи и его база, без адресов Kelesida.
Никаких скрытых зависимостейВсё, куда проект обращается наружу, записано в его сценарии со ссылкой на требование. Остальное не пройдёт проверку перед выкладкой.
Только расширяемНовый способ входа — новый кубик. Ядро и подключённые проекты не меняются, поэтому ничего не ломается.
Один человек — один входПароль сегодня, MAX завтра — система понимает, что это один пользователь. Связывает только тот, кто доказал владение обоими входами.
Сбой не останавливает работуКто уже вошёл, продолжает работать. Для администратора есть аварийный вход без узла.
Уровень 1 · контекст

Кто участвует

Пользователь[человек]браузер, телефонАдминистратор области[человек: мы или заказчик]настраивает входПроект[система]кабинеты, роли, данныеLab, сайты и системы клиентовУзел входа[система из кубиков]протокол OIDC, связи, журналвстроенный или общийВнешние провайдеры[чужие системы]СУДИР, Госуслуги, Яндекс ID,MAX, Keycloak заказчикаКаталог пользователей[у нас, у заказчика или нет]свой (пароли, TOTP),Active Directory, KeycloakШлюзы доставки[чужие системы]SMS, звонок-сброс, почтаработаетв кабинетевходит: пароль, телефон,MAX, СУДИР…включает способы,уровни, клиентовкто вошёл:JWT (OIDC)вход черезчужую системупроверкалогинакоды иуведомления
Пользователь работает в проекте, а входит через узел. Узел проверяет его сам (пароль, телефон) или через чужую систему (СУДИР, Госуслуги, MAX) и сообщает проекту, кто вошёл.
Уровень 2 · контейнеры

Из чего состоят проект и узел

Проект [наш или заказчика]Веб-приложение[Symfony, из сценария]кабинеты, роли (access),scope, where, whenКлиент входа[kelesida/auth]проверяет JWT по ключам узлаБаза проекта[PostgreSQL]учётка по sub, роли,профиль: ФИО, ИНН…← данные владельца проектаУзел входа [кубики kelesida/auth]Протокол OIDC[authorize, token, JWKS]выдаёт JWT, ключи подписиСпособы входа[кубики]пароль, TOTP, телефон,MAX, внешний OIDC…Связи[person ← identity]один человек — много входовКабинеты узла[«Способы входа», «Область»]пользователь и администраторХранилище узла[PostgreSQL]клиенты, связи (HMAC),журнал входовбез профилей и ПДнКаталог области[local | ldap | keycloak | none]пароли и TOTP — если local;иначе у заказчикаВнешние провайдеры[OIDC, SAML, ЕСИА]СУДИР, Госуслуги, Яндекс,MAX, Keycloak заказчикаКаталог заказчика[LDAP, Keycloak]если область — у негоШлюзы доставки[SMS, звонок, почта]коды и уведомленияJWT, ключи(JWKS)внешний входLDAPкоды
Узел хранит только служебное: клиентов, связи входов (в обезличенном виде) и журнал. Профили и роли — в базе проекта.
Уровень 3 · компоненты

Кубики узла

Ядро узла[не меняется]протокол · связи · журналинтерфейс кубикаСпособы входапарольTOTPтелефонMAXdevвнешний OIDCLDAPpasskeysКЭПЕСИАSAMLДоставка кодовSMSзвонок-сброспочтаголосомКаталоги областейnonelocalldapkeycloakПодпись и уровни входаRS256basicmfaES256ГОСТ 34.10qualifiedв v1добавится кубикомновый кубик подключается к ядру, не меняя ни ядро, ни проекты
Каждый кубик подключается к ядру через один интерфейс. Добавить Keycloak, LDAP или вход по электронной подписи — значит добавить кубик, не трогая остальное.

Два варианта сборки

Встроенный узел — проект заказчикаПроект заказчика[приложение + кубики входа внутри]вход через СУДИР — по ТЗадминка: пароль + TOTPникаких адресов KelesidaСУДИР[провайдер заказчика]OIDCБаза проекта[у заказчика]учётки, роликаталог админкиОбщий узел — наши проектыLab[проект]Secret[проект]ai, сайты[проекты]Узел входа Kelesida[те же кубики, отдельно]один вход во все наши проектыпароль + TOTP, MAX, телефонкод проектов одинаковый — отличается строка providerв сценарии и адрес провайдера в .env
Системе заказчика — встроенный узел: всё у него, вход по его требованиям. Нашим проектам — общий узел: один вход во все.
Динамика

Вход по телефону

ПользовательПроектУзел входаШлюз SMS1. открывает кабинет2. перенаправляет на вход3. выбор способа входа4. номер телефона5. отправить код6. SMS с кодом7. вводит код8. identity найдена → её person; нет → новый9. код → токен: sub, acr (JWT)10. кабинет: учётка проекта по sub
Номер уходит в шлюз SMS и не хранится ни в узле, ни в проекте, если область этого не требует.

Несколько входов — один пользователь

ПользовательУзел входаMAXПроекты1. «Способы входа» → «Добавить MAX» (вход свежий)2. вход через MAX3. внешний id4. ни к кому → привязать; к другому → объединить5. уведомление на все способы входа6. если объединение: событие person.merged7. проект сливает свои записи
Узел никогда не объединяет учётки сам по совпадению почты или телефона — только после входа обоими способами. Обо всех привязках пользователь получает уведомление.

Кто за что отвечает

ЧастьГдеЧто хранитЧьи данные
Учётка, роли, профильпроектsub, роли, ФИО, ИНН…владельца проекта
Клиент входапроектничего—
Протокол OIDCузелклиенты, ключи подписи, короткие коды—
Связи входовузелхеши внешних id, маски («MAX ···1234»)обезличено
Журналузелкто, когда, каким способомобезличено
Каталог «свой»узел областилогины, хеши паролей, TOTPвладельца области
Каталог заказчикау заказчикавсё своёзаказчика
Внешние провайдеры, шлюзытретьи сторонысвоёпровайдера

Токены

ТокенФорматЗачем
id_tokenJWT, подпись узлакто вошёл и как: sub, уровень входа
access_tokenJWT, 5–15 минутдоступ к API; проверяется подписью, без обращения к узлу
refresh_tokenслучайная строкапродлить вход; хранится в узле и может быть отозван
сессия в кабинетеcookie, на сервереработа в браузере — не JWT, чтобы её нельзя было украсть и нельзя было не отозвать

Подпись — RS256, при необходимости ES256 или ГОСТ 34.10-2012. Алгоритм «none» и общие секреты клиент не принимает.

Если узел недоступен