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

**URL:** https://meta.discourse.org/t/mapping-claims-from-jwt-to-a-discourse-user-via-login-plugin/299334
**Category:** SSO
**Tags:** oauth2, openid-connect
**Created:** [14.Март.2024 12:40:23 UTC](https://meta.discourse.org/t/mapping-claims-from-jwt-to-a-discourse-user-via-login-plugin/299334 "2024-03-14T12:40:23Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![jrack](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/jrack/32/326934_2.png) [@jrack](https://meta.discourse.org/u/jrack)
#### Post date: [14.Март.2024 12:40:23 UTC](https://meta.discourse.org/t/mapping-claims-from-jwt-to-a-discourse-user-via-login-plugin/299334/1 "2024-03-14T12:40:23Z")

</div>

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

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

Я вернулся к [плагину oauth](https://github.com/discourse/discourse-oauth2-basic), но на первый взгляд у него те же базовые ограничения: он зависит от информации о пользователе из конкретного конечного пункта. Я пытаюсь найти решение, которое возвращало бы содержимое JWT-утверждений, но это не совсем типичный сценарий использования, поэтому я не жду больших успехов.

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

Я рассматриваю [jwt](https://github.com/discourse/discourse-jwt/tree/main), но в нём, похоже, вообще нет маппинга утверждений на пользователя. По сути, если вы получаете 200, значит всё в порядке, что тоже не то, что я искал.

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

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

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [14.Март.2024 18:22:15 UTC](https://meta.discourse.org/t/mapping-claims-from-jwt-to-a-discourse-user-via-login-plugin/299334/2 "2024-03-14T18:22:15Z")

</div>

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

---

<div class="post-metadata">

### Author: ![jrack](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/jrack/32/326934_2.png) [@jrack](https://meta.discourse.org/u/jrack)
#### Post date: [14.Март.2024 21:17:02 UTC](https://meta.discourse.org/t/mapping-claims-from-jwt-to-a-discourse-user-via-login-plugin/299334/3 "2024-03-14T21:17:02Z")

</div>

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

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

---

<div class="post-metadata">

### Author: ![jrack](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/jrack/32/326934_2.png) [@jrack](https://meta.discourse.org/u/jrack)
#### Post date: [19.Март.2024 13:32:22 UTC](https://meta.discourse.org/t/mapping-claims-from-jwt-to-a-discourse-user-via-login-plugin/299334/4 "2024-03-19T13:32:22Z")

</div>

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

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

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [19.Март.2024 14:48:09 UTC](https://meta.discourse.org/t/mapping-claims-from-jwt-to-a-discourse-user-via-login-plugin/299334/5 "2024-03-19T14:48:09Z")

</div>

> [@Джастин Раклифф](#):
>
> Даже если у них одинаковый атрибут электронной почты, я думаю, что для одного и того же пользователя Discourse переход между провайдерами невозможен.

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

---

<div class="post-metadata">

### Author: ![jrack](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/jrack/32/326934_2.png) [@jrack](https://meta.discourse.org/u/jrack)
#### Post date: [19.Март.2024 18:40:56 UTC](https://meta.discourse.org/t/mapping-claims-from-jwt-to-a-discourse-user-via-login-plugin/299334/6 "2024-03-19T18:40:56Z")

</div>

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

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