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

Эксплуатационный контракт единого входа

В «Пропуске» есть связный маршрут: известный provider, code и PKCE, проверяемый callback, новая локальная сессия, роли, защищённая форма, второй клиент, выход, refresh и passkey у Keycloak. Последний урок фиксирует, что потребуется изменить перед настоящим выпуском. Его результат — эксплуатационный контракт, который отделяет учебную лабораторию от работающего сервиса. Мы не публикуем приложение, не создаём внешние аккаунты и не считаем прочитанный исходник доказательством завершённых браузерных сценариев.

Состояние lesson-14 содержит все модули и handoff.md. Выбранные версии Node.js 22.14.0, openid-client 6.8.8 и Keycloak 26.5.2 дают конкретную основу объяснений. Они не заменяют проверку актуальных исправлений перед реальным развёртыванием. В архиве отсутствуют зависимости, Docker-образы, пользовательские токены и база провайдера. Сессии и заявки живут в памяти, а все входы, обновления и WebAuthn-действия остаются not_run. Эти сведения являются частью результата, а не мелким примечанием после обещания готовой безопасности.

Точный внешний адрес

В публичном профиле issuer и оба приложения должны использовать HTTPS. AllowInsecureRequests удаляется, cookie получает Secure, redirect URI и post-logout URI регистрируются точно. Внешний origin определяется доверенной конфигурацией с учётом принятого reverse proxy, а не любой строкой Host или X-Forwarded-Host. Подмена такой строки способна изменить callback или возвращение после logout. Переезд с localhost требует согласовать обе стороны протокола, CSP и browser origin, а не лишь заменить порт в ссылке меню.

RP ID passkeys тоже требует отдельного плана. Credential, зарегистрированный для localhost, не становится ключом нового публичного домена автоматически. При изменении домена пользователю может понадобиться новая регистрация. Поэтому настоящий identity-домен следует выбирать до массового выпуска passkeys, учитывая срок его жизни и доверие. Это ещё одна связь с сохранением URL: адрес провайдера является техническим основанием credential, а не просто удобной маркетинговой надписью. Лабораторные ключи нельзя переносить обещанием «всё продолжит работать» без принятой политики.

Хранение локального состояния

Map подходит одному процессу и исчезает при рестарте. Для нескольких экземпляров нужен внешний store с TTL, ограниченным доступом и согласованной схемой. Сессия должна содержать минимально необходимый профиль, токены и состояние refresh, защищённые от публичного чтения. Promise из памяти нельзя просто сериализовать в общую базу: координация refresh станет отдельным механизмом. Идентификаторы должны по-прежнему обновляться после входа, а удаление записи должно действовать для всех обслуживающих процессов, если продукт обещает общий локальный отзыв.

Заявки сейчас лежат в массиве и ограничены ста элементами. Настоящее редакционное действие потребует базы, транзакции, даты, устойчивого owner и идемпотентности. Одна роль editor не создаёт историю, резервирование или атомарность выпуска. Название route publish в лаборатории обозначает учебную заявку, а не deployment. Если привязать его к рабочему сайту, это будет новый вид действия с другими полномочиями и последствиями. Такое изменение должно быть предметным, а не случайным вызовом команды внутри прежнего обработчика формы.

Полномочия и актуальность

В нашей схеме роли приходят из realm mapper и пересматриваются при новом входе или refresh. Между этими моментами локальная запись может хранить прежние права. Пятнадцать минут фиксированного срока являются учебной политикой, а не универсальной допустимой задержкой отзыва. Для некоторых операций нужны более короткие сроки или синхронное решение о праве. Нельзя увеличить сессию до месяца только ради удобства, забыв, что old claims будут использоваться в пределах выбранного механизма. Время полномочий является частью бизнес-контракта.

Общий выход также не реализован мгновенно. RP-initiated logout завершает provider-состояние, но другой клиент может сохранять Map до своего срока или отказа refresh. Если интерфейс обещает «выйти на всех устройствах», необходим механизм back-channel/front-channel либо другая проверенная координация сессий. Учебный код такого обещания не делает. Полезно сначала определить ожидаемый эффект, затем выбрать реализацию и наблюдения, а не добавлять громкую кнопку поверх операции, которая затрагивает лишь одну запись текущего процесса.

Конфигурация и секреты

Public client с PKCE выбран для изучения. Настоящий server-side BFF может использовать confidential client и отдельную аутентификацию token endpoint. Тогда secret хранится вне Git и передаётся предусмотренным методом, а не в authorization URL. Нужно изменить регистрацию и библиотечную конфигурацию вместе. Если оставить клиента confidential, но использовать None, обмен не станет правильным от простого наличия переменной SECRET. Согласованность протокола важнее набора знакомых имён параметров в окружении.

Keycloak должен иметь собственную устойчивую базу, резервные копии и порядок обновлений. Start-dev и фиктивные пароли не являются production-профилем. Импорт realm из JSON тоже не заменяет миграции живой конфигурации: существующие данные сохраняются. Любое изменение mapper, client policy или required action необходимо проследить до фактического realm. При восстановлении следует возвращать совместимую конфигурацию и данные. Копия одного файла compose не доказывает, что учётные записи и ключи провайдера могут быть восстановлены в ожидаемом состоянии.

Журнал и восстановление пользователя

События должны помогать отличить начало входа, отказ callback, успешную сессию, refresh, отказ роли и завершение. Не нужно сохранять password, refresh token, cookie и полный callback URL. Пользовательское имя тоже является данными человека; для технической корреляции часто достаточно безопасного идентификатора и request id в ограниченном журнале. В статье отсутствуют выдуманные метрики ошибок или проведённые incident drills. При реальном сопровождении такие наблюдения потребуется собрать в своей среде с принятой политикой хранения и доступа.

Passkey-регистрация зависит от браузера, ОС и устройства. Восстановление требует независимого запасного способа и точного отзыва утраченных состояний. SMTP и recovery codes не настроены, а второй ключ с оставленным паролем является только учебным маршрутом. В production слабый fallback может снизить защиту сильного основного способа. Поэтому recovery надо рассматривать вместе с идентификацией заявителя, полномочиями оператора и журналом, а не добавлять после публикации как скрытый endpoint с удобным сбросом для любого username.

Полный handoff.md ниже фиксирует эти границы. Перед самостоятельным расширением выберите один новый результат: внешний store, HTTPS-профиль или корректный механизм общего отзыва. Не меняйте все участки сразу, иначе источник нового поведения станет неясен. Сохраняйте исходный развивающийся пример и точные версии. В серии мы подготовили основу понимания связей; реальный выпуск остаётся отдельной работой, которая потребует выполнения и наблюдения сценариев в разрешённой пользователем среде.

Полный файл handoff.md из состояния lesson-14:

# Границы выпуска

Лаборатория не опубликована. Перед реальным выпуском необходимы HTTPS у issuer и приложений, точные redirect URI, запрет allowInsecureRequests, Secure cookies, внешний store с TTL, ключи и секреты вне Git, отдельная база Keycloak и резервное копирование, актуальные исправления пакетов. Public client с PKCE выбран для учебного протокола; production BFF может использовать confidential client и отдельную аутентификацию token endpoint.

Локальная сессия — 15 минут. Она хранится в памяти процесса, пропадает при рестарте, не синхронизируется с другим приложением. Refresh не обеспечивает мгновенный отзыв; роль пересматривается только при получении нового ID Token. Back-channel logout, key rotation drills, HA, ограничение по IP и аудит администраторов не реализованы. Публикуемые заявки — в памяти процесса, не настоящая редакционная база.

События журнала должны содержать время, тип, request ID и результат, не callback query, токены, пароли или полные cookies. Пользовательские имена сами являются персональными данными; их хранение нужно обосновать отдельно. Все сценарии запуска, входа, refresh и WebAuthn остаются not_run.

Оглавление серии · Исходники всех шагов · Учебные маршруты

Пример остаётся локальным черновиком. При подготовке он не выполнялся; все описанные эффекты являются ожидаемыми, а не результатами запущенных проверок.