Сопоставление утверждений из JWT пользователю Discourse через плагин входа

Я уже давно пытаюсь найти плагин SSO, который позволял бы использовать утверждения (claims) от моего IdP для пользователей Discourse.

В плагине OIDC вы фактически привязаны к конечной точке userinfo. Это не так уж плохо, но я пытался использовать другую информацию из нашего хранилища идентификации, а результат userinfo нельзя настроить. Я пробовал заставить плагин OIDC использовать id_token_info для создания пользователя, но безуспешно.

Я вернулся к плагину oauth, но на первый взгляд у него те же базовые ограничения: он зависит от информации о пользователе из конкретного конечного пункта. Я пытаюсь найти решение, которое возвращало бы содержимое JWT-утверждений, но это не совсем типичный сценарий использования, поэтому я не жду больших успехов.

Изначально я думал, что “callback userinfo paths” в oauth basic можно использовать для маппинга утверждений в пользователя, но в ответе всегда возвращаются null, и вставка не удаётся. Я могу декодировать токен от IdP и вижу правильные утверждения, в данном случае на корневом уровне JSON, такие как iss, exp и т. д., но не могу связать их со стороной ActiveRecord.

Я рассматриваю jwt, но в нём, похоже, вообще нет маппинга утверждений на пользователя. По сути, если вы получаете 200, значит всё в порядке, что тоже не то, что я искал.

Также я нашёл omniauth-jwt](GitHub - discourse/discourse-omniauth-jwt: An OmniAuth strategy that uses JSON Web Token for Single Sign-On · GitHub), который кажется ближе к моей цели, хотя и не выглядит как плагин и, похоже, не поддерживается активно. Возможно, я мог бы это исправить, но это слишком сложная задача для моих текущих планов.

Не могли бы вы подсказать направление для поддерживаемого плагина, который маппит JWT-утверждения на пользователя, или указать, где я могу ошибаться при использовании одного из существующих плагинов?

У меня нет никакой идеи, но я бы поступил так: зашел бы на GitHub - discourse/all-the-plugins · GitHub и выполнил бы grep по OmniAuth. Это даст вам кучу плагинов, использующих Это даст вам GitHub - discourse/discourse-jwt: Discourse Auth support for JSON Web Tokens (JWT) · GitHub, что может быть тем, что вы ищете.

Да, я уже проходил через это.

Если только я сегодня не стал особенно тупым, это касается только аутентификации с фиксированными атрибутами. Я не нашёл способа сделать маппинг утверждений. Если бы я решил форкнуть что-то, то, скорее всего, начал бы именно с этого, но хотел убедиться, что не упускаю ничего очевидного.

И просто для добавления немного контекста тем, кто наткнётся на эту тему.

OAuth Basic, похоже, поддерживает извлечение атрибутов только из определённой конечной точки данных пользователя и не поддерживает сопоставление утверждений (claims) JWT. Это похоже на плагин OIDC, но вместо расположения userinfo, предоставляемого документом обнаружения (discovery doc), вы можете указать любую JSON-конечную точку и выполнить сопоставление по пути. Добавив настройки для включения аутентификации в заголовок, можно обращаться к таким адресам, как https://graph.microsoft.com/v1.0/me, которые мы используем для OAuth на основе Entra.

Теперь Entra ID предлагает некоторую расширяемость для основных объектов, но это не так просто, как добавление необязательного утверждения (claim), и, насколько я вижу, это потребует глобальных изменений. Поэтому я не уверен, что это действительно вариант для нас. Отойдя ещё дальше по пути зрелости и просто используя плагин JWT, кажется, остаётся единственный путь, но для этого потребуется работа над пул-реквестами (PR) для добавления поддержки сопоставления утверждений. По сути, так же, как существующий oauth2-basic позволяет настраивать сопоставление поля Discourse с атрибутом JSON, потребуется обеспечить более гибкую настройку сопоставления полей с утверждениями JWT.

Это, конечно, возможный путь для нас, но я столкнулся с другой проблемой, которая, как мне кажется, полностью блокирует его.

После настройки oauth2-basic система запрашивает регистрацию нового пользователя и обнаруживает, что уже существует пользователь с тем же адресом электронной почты, который использовался в существующем плагине OIDC. Несмотря на то, что атрибут email совпадает, похоже, что переход между провайдерами для одного и того же пользователя Discourse невозможен. Заставлять всех начинать с нуля — это не реальный вариант, и хотя я, возможно, смог бы взломать систему, перенеся запись провайдера OAuth из таблицы user_associated_accounts с OIDC-провайдера, это, как мне кажется, просто приглашение к проблемам. Таким образом, даже если бы JWT имел поддержку сопоставления утверждений, все существующие пользователи, похоже, останутся привязанными к провайдеру OIDC, если не прибегнуть к манипуляциям с базой данных или не создать обработчик для «существующих пользователей», позволяющий изменять связанную учётную запись. В противном случае вы, кажется, застрянете.

Это странно. Похоже, он должен сопоставлять адрес электронной почты и подключаться к той же учётной записи. Вход через OAuth работает именно так.

Я надеялся, что они смогут объединиться либо путем разрешения наличия >1 записи user_associated на одного пользователя Discourse (по provider_id), либо с помощью запроса «связать», позволяющего переключиться с одного провайдера на новый. Однако в ходе моего тестирования этого не произошло: система просто отмечает, что email уже используется, и предлагает создать нового пользователя.

Если бы я оставался в рамках того же плагина, всё, безусловно, прошло бы гладко, но в данном случае у каждого провайдера свой уникальный provider_id со своими собственными метаданными.