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

Блог

Половину IPSIE SL1 нельзя включить по умолчанию

IPSIE SL1 от OpenID Foundation задаёт правила корпоративного входа. Половину нужно соблюдать всегда, остальные ломают другие стандарты. Когда его включать.

Автор: FoxAuth ·

Рано или поздно в анкете по безопасности от корпоративного заказчика появится такая строка:

Требование Ответ
Соответствует ли ваш провайдер удостоверений профилю IPSIE SL1 для OpenID Connect? Да / Нет / Частично

Провайдером удостоверений (identity provider) называют сервер, который выполняет вход людей и выдаёт токены приложениям. Если вы держите такой сервер для бизнес-заказчиков, эта строка про вас. Прежде чем на неё отвечать, нужно понять, что такое SL1, каким его правилам ваш сервер уже следует и какие правила что-то сломают, если их включить.

Недавно я встроил SL1 в FoxAuth, и профиль разделился на две половины. Первой половине любой сервер должен следовать в любом случае. Вторая половина противоречит стандартам, на которые опираются другие клиенты, поэтому её нельзя включить по умолчанию. Её приходится делать переключателем. И самое трудное здесь понять, когда этот переключатель включать.

IPSIE SL1 превращает «поддерживает SSO» в правила, которые можно проверить

IPSIE расшифровывается как Interoperability Profile for Secure Identity in the Enterprise. Так называется рабочая группа OpenID Foundation. Её создала Okta в октябре 2024 года вместе с Microsoft, Ping Identity и другими компаниями (объявление Okta). Группа пишет профили. Профиль добавляет более строгие правила поверх существующих стандартов, таких как OpenID Connect, OAuth и SCIM. По профилю компания может проверить, что на деле значит «поддерживает единый вход (single sign-on)» у каждого SaaS-приложения, которое она покупает.

Свои требования группа раскладывает по уровням на двух направлениях (уровни IPSIE). Направление Session Lifecycle (SL) касается входа и сессий. Направление Account Lifecycle (AL) касается создания и удаления учётных записей, а этим занимается SCIM (о SCIM-профиле IPSIE я писал в статье что Entra и Okta отправляют SCIM-серверу). SL1 является первым уровнем сессий. Он требует единый вход с многофакторной аутентификацией и срок жизни сессии, который задаёт провайдер удостоверений, а приложение соблюдает. SL2 добавляет выход и сроки, к которым сессия должна быть завершена. SL3 добавляет постоянный обмен сигналами между двумя сторонами.

Конкретный текст называется IPSIE SL1 OpenID Connect Profile. Он по разделам перечисляет, что должен делать провайдер удостоверений и что должно делать приложение. Ещё он ссылается на второй документ, IPSIE Common Requirements. Там собраны правила, общие для всех профилей IPSIE, например какие алгоритмы подписи разрешены.

Оба документа пока остаются черновиками. Редакторский черновик, по которому я работал, датирован 29 сентября 2026 года. Профиль уже успел заметно измениться. В черновике 00 поле токена (claim) с концом сессии называлось session_lifetime и задавалось в секундах. В текущем тексте он называется session_expiry и содержит момент времени (timestamp). Серверу, который реализует SL1 сегодня, придётся догонять и следующий черновик.

Большую часть SL1 стоит соблюдать по умолчанию в любом случае

Прочитайте ту половину профиля, которая относится к провайдеру. Большинство её правил окажется обычной хорошей практикой. Они не противоречат ни одному другому стандарту, поэтому ничто не мешает серверу соблюдать их для всех клиентов:

  • Код авторизации живёт не больше 60 секунд и используется один раз.
  • PKCE (code challenge, который привязывает код к клиенту, запросившему этот код) использует S256.
  • Адрес возврата (redirect URI) должен в точности совпадать с одним из зарегистрированных.
  • Ответ на запрос авторизации содержит iss, чтобы клиент мог понять, какой сервер ответил (RFC 9207).
  • Каждый ID-токен сообщает, когда человек прошёл аутентификацию (auth_time), каким способом (acr и amr) и когда должна закончиться сессия приложения (session_expiry).

FoxAuth делает всё это при выключенном профиле. Вот раскодированный payload ID-токена после входа по паролю:

{
"iss": "https://auth.example.com",
"sub": "2f6c1b0e-7c55-4b8e-9d2a-3a1f0c9e8b71",
"aud": "orders-web",
"iat": 1792745400,
"exp": 1792749000,
"auth_time": 1792745391,
"acr": "urn:foxauth:acr:pwd",
"amr": ["pwd"],
"session_expiry": 1792831800
}

Новый claim здесь session_expiry. Он задаёт момент, после которого приложение должно снова отправить человека к провайдеру удостоверений. Провайдер при этом заново проверит, может ли человек по-прежнему входить. SL1 требует, чтобы этот момент наступал не раньше чем через 300 секунд после iat. Здесь он наступает через 24 часа: столько по умолчанию задаёт настройка idToken.relyingPartySessionLifetime. Приложение, которое не читает этот claim, просто его игнорирует. Поэтому session_expiry можно выдавать всем.

Другая половина ломает стандарт, на который опирается кто-то ещё

Четыре правила SL1 устроены иначе. Каждое из них противоречит другому документу, которому следует целый класс реальных клиентов. Если сделать любое из этих правил поведением по умолчанию, эти клиенты перестанут работать.

  • Никаких адресов возврата с http, даже http://127.0.0.1. Но RFC 8252, раздел 7.3, велит нативным приложениям получать ответ на запрос авторизации именно на такой loopback-адрес (адрес того же компьютера). Настольные приложения и утилиты командной строки делают так каждый день.
  • Только клиенты, зарегистрированные заранее, и никакой динамической регистрации без аутентификации. Но MCP-клиенты, то есть ИИ-инструменты, которые подключаются к MCP-серверам, обычно приходят без предупреждения. Спецификация авторизации MCP просит серверы принимать client id, который является URL документа с метаданными. Сервер скачивает этот документ при первом обращении клиента. Динамическую регистрацию спецификация оставляет как устаревший запасной вариант. Этот процесс я описал в статье MCP-сервер должен проверять, кому выдан токен.
  • acr_values становится обязательным. Этим параметром клиент просит определённый уровень аутентификации. В OpenID Connect Core, раздел 3.1.2.1, он служит добровольной подсказкой. Сервер может выполнить вход другим способом и сообщить об этом в acr. SL1 требует, чтобы сервер либо выполнил запрос, либо вернул ошибку.
  • Только подписи PS256, ES256 и EdDSA (Common Requirements, раздел 3.3). Но OpenID Connect Registration назначает RS256 алгоритмом по умолчанию для клиента, который не указал алгоритм. А проверка подписи, написанная под это умолчание, может не принимать ничего другого.

На схеме обе половины стоят рядом. Правила слева никому ничего не стоят. Под каждым правилом справа указан документ, которому это правило противоречит.

Правила SL1 для провайдера делятся на две части. Слева правила, включённые для всех клиентов: коды живут 60 секунд и используются один раз, PKCE S256, точное совпадение redirect URI, iss в ответе, auth_time, acr, amr и session_expiry в каждом ID-токене. Справа правила, которые включает ipsieSl1.enabled, и у каждого указан документ, которому оно противоречит: запрет http-адресов возврата противоречит RFC 8252, раздел 7.3; только заранее зарегистрированные клиенты противоречат авторизации MCP; обязательный acr_values противоречит OIDC Core 3.1.2.1; только PS256, ES256 или EdDSA противоречит умолчанию RS256 в OIDC RegistrationВключено для всех клиентовКод живёт 60 с, одноразовыйPKCE с S256Точное совпадение redirect URIiss в ответе на запрос авторизацииauth_time, acr, amr в каждом ID-токенеsession_expiry в каждом ID-токенене противоречит ни одному стандартуТолько с ipsieSl1.enabledНет адресов возврата с httpломает loopback-приложения RFC 8252 §7.3Только заранее зарегистрированныеломает MCP-клиентов без регистрацииacr_values обязателенв OIDC Core §3.1.2.1 это подсказкаТолько PS256, ES256 или EdDSAв OIDC Registration умолчание RS256по умолчанию каждое ломало бы часть клиентов

Ни одно из этих четырёх правил не ошибочно. Возьмём компанию, которая контролирует каждое приложение, куда входят через её сервер. Для неё loopback-адрес возврата или незарегистрированный клиент становится брешью в цепочке, которой она пытается доверять. А для публичного инструмента разработчика это обычный порядок вещей. Один сервер может обслуживать заказчиков обоих типов, поэтому эти четыре правила включаются отдельной настройкой.

Один переключатель включает правила и отклоняет всё, что их нарушает

В FoxAuth эта настройка называется ipsieSl1.enabled, по умолчанию она выключена. Каждое правило читает её там, где это правило применяется, через одну функцию в lib/sl1/rules.ts:

export function sl1(): boolean {
return ApplicationConfig['ipsieSl1.enabled'];
}

Поэтому профиль легко проверить: найдите в коде sl1(), и вы увидите каждое место, где профиль меняет поведение сервера. Хороший пример: token endpoint, адрес, по которому сервер выдаёт токены. SL1 говорит, что токены доступа следует привязывать к отправителю (sender-constrained) через DPoP. DPoP добавляет к запросу доказательство, которое привязывает каждый токен к ключу, хранящемуся у клиента (подробнее в статье от чего защищает DPoP). При включённом профиле проверка стоит перед обработкой любого grant type, поэтому ни один из них её не минует:

if (sl1() && !sl1Exempt(client)) {
if (client.tlsClientCertificateBoundAccessTokens) {
throw new InvalidClient('certificate-bound access tokens are not issued under the IPSIE SL1 profile');
}
if (!dPoP) {
throw new InvalidDpopProof('DPoP proof JWT not provided (IPSIE SL1 §3.2.1)');
}
}

Клиент, который просит токен без доказательства, получает такой ответ:

HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "invalid_dpop_proof",
"error_description": "DPoP proof JWT not provided (IPSIE SL1 §3.2.1)"
}

Ошибка называет раздел профиля. Я сделал так намеренно. Читать эту ошибку будет разработчик приложения из другой компании. Быстрее всего он исправит свой клиент, если показать ему само правило.

Кроме того, переключатель не включится, пока не готовы настройки, от которых зависит профиль. DPoP должен быть включён, а PKCE должен быть обязательным. Зашифрованные ID-токены и ответы должны быть доступны (encryption.enabled), потому что Common Requirements требуют предлагать шифрование. Динамическая регистрация должна быть выключена или требовать initial access token (токен, который администратор выдаёт заранее для регистрации). Каждому издателю (issuer) нужен ключ подписи PS256, потому что именно PS256 теперь будет получать приложение, которое не выбрало алгоритм. В новой установке DPoP и шифрование выключены, так что большинству серверов придётся поработать, прежде чем переключатель удастся включить.

Четыре клиента остаются вне правил: консоль администратора, MCP-клиент администраторов, страница учётной записи каждого проекта и подключения провижининга через SCIM. Это собственные клиенты сервера, и каждому из них нужно что-то, что профиль запрещает. MCP-клиент работает на машине оператора и использует loopback-адрес возврата. Страница учётной записи делает запрос с response_type=none. У каталога SCIM нет ключа DPoP, а его токен годится только для эндпоинта SCIM, на который SL1 не распространяется.

RS256 в сохранённой записи обычно означает умолчание, которое никто не выбирал

Больше всего риска при включении несут приложения, которые у вас уже есть. Первая версия моего переключателя отклонила бы почти все из них.

Правило выглядело простым. Common Requirements разрешают только PS256, ES256 и EdDSA, значит, клиент, зарегистрированный под RS256, отклоняется. Тесты с этим соглашались. Потом я попробовал правило на настоящей базе данных, и почти каждое приложение, созданное в консоли, оказалось отклонено.

Причина в том, как сохраняются записи. Когда FoxAuth сохраняет клиента, он сохраняет проверенную запись, в которой уже заполнены значения по умолчанию. Поэтому даже у клиента, владелец которого никогда не думал об алгоритмах, в записи стоит id_token_signed_response_alg: "RS256". Тестовые записи (fixtures) писали вручную, и значений по умолчанию в них нет. Поэтому тесты и проходили. По одной записи нельзя отличить RS256, который кто-то выбрал, от RS256, который подставил сервер.

Исправление считает сохранённый RS256 в этих атрибутах тем, чем он почти всегда и оказывается: значением по умолчанию. При включённом профиле такой клиент получает подписи PS256. В саму запись PS256 попадёт при её следующем сохранении. Новая регистрация с RS256 или смена алгоритма на RS256 по-прежнему отклоняется.

От приложения это требует другого поведения. Ключ для проверки подписи оно должно выбирать из набора ключей по kid и принимать алгоритм, указанный в ключе, а не жёстко заданный RS256. Большинство библиотек так и делает. Некоторые самописные проверки подписи так не делают. Перед включением проверка готовности перечисляет все приложения, которые будут получать PS256. Так вы будете знать, у кого спросить про проверку подписи:

Включение профиля в четыре шага: запустить проверку готовности, которая перечисляет невыполненные настройки, издателей без ключа PS256 и каждое приложение, которое профиль отклонит или изменит; исправить настройки и ключи; просмотреть перечисленные приложения; включить. После выключения отклонённое приложение снова работает со следующего запроса, потому что ничего не переписывалось.Проверка готовностиневыполненные настройкииздатели без PS256приложения и причиныИсправитьнастройки и ключиПросмотретьсписок приложенийВключитьвыключение возвращает отклонённые приложения

Проверка готовности выглядит как карточка в консоли: откройте Settings, затем Request security. Тот же ответ агент получает через инструмент settings_ipsie_profile_readiness_get. Для каждого приложения проверка называет правило и атрибут. http_redirect_uri означает, что этот адрес возврата будет отклонён, а адреса приложения с https продолжат работать. signing_algorithm означает, что приложение будет отклонено. default_algorithm означает, что приложение продолжит работать, но будет получать PS256.

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

Включайте профиль, когда SL1 требует заказчик, а не потому, что так кажется безопаснее

Та половина SL1, которая ничему не противоречит, уже включена. Поэтому включение профиля не делает обычный сервер заметно безопаснее. Оно ужесточает сервер в четырёх правилах, которые некоторые клиенты выполнить не могут. Это оправдано в одной ситуации: корпоративный заказчик требует SL1, и каждое приложение, которое вы обслуживаете, может жить по этим правилам.

Включайте, если:

  • договор с заказчиком или его проверка безопасности требует IPSIE SL1, и
  • каждое ваше приложение использует адреса возврата с https, умеет отправлять доказательства DPoP и проверяет подписи по набору ключей, и
  • приложения регистрируете вы сами, в консоли или с initial access token.

Оставьте выключенным, если вы обслуживаете хоть что-то из этого списка:

  • нативные приложения или утилиты командной строки, которые получают ответ на loopback-адрес с http;
  • MCP-клиентов, которые регистрируются сами или которых вы узнаёте по URL документа с метаданными;
  • партнёров, чья проверка подписи принимает только RS256;
  • всё, что зависит от response_type=none.

Если у вас заказчики обоих типов, разнесите их по разным экземплярам сервера: на одном профиль включён, на другом нет. Проект FoxAuth со своим адресом сам является издателем, но переключатель действует на весь экземпляр, а не на отдельный проект.

И отвечайте на анкету честно. Три требования SL1 здесь пока не выполнены. Приложение может попросить вход со вторым фактором. Если в проекте второй фактор не обязателен, такой запрос заканчивается ошибкой, а не просьбой к человеку пройти второй фактор. После входа через внешнего провайдера (upstream) ID-токен не несёт amr этого провайдера. А токены, которые этому серверу присылают другие издатели, например ID-токены внешних провайдеров, по-прежнему принимаются с RS256. Первый пункт закроет следующая доработка. Заявку на сертификацию никто не подавал.

Я поискал такой же переключатель у других. Auth0, Keycloak, Zitadel, authentik и Ory Hydra не документируют работу в роли SL1 OpenID Provider, по крайней мере по источникам, на которые ссылаются страницы сравнения. Профиль пока остаётся черновиком, так что это может измениться.

Чтобы попробовать, начните с карточки готовности и прочитайте включение профиля IPSIE SL1. Там перечислены все правила и все предварительные условия. Скорее всего, сначала вам понадобится ключ PS256. Как создать такой ключ и сделать его основным, описано в ротации ключей подписи.