# Настройка DiscourseConnect — официальный единый вход для Discourse (sso)

**URL:** https://meta.discourse.org/t/setup-discourseconnect-official-single-sign-on-for-discourse-sso/13045
**Category:** Integrations
**Tags:** sso, discourseconnect, configuring, how-to
**Created:** [25.Февраль.2014 08:30:34 UTC](https://meta.discourse.org/t/setup-discourseconnect-official-single-sign-on-for-discourse-sso/13045 "2014-02-25T08:30:34Z")
**Posts on this page:** 1
**Showing post:** 1

<div class="post-metadata">

### Author: ![Discourse](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/discourse/32/148734_2.png) [@Discourse](https://meta.discourse.org/u/Discourse)
#### Post date: [25.Февраль.2014 08:30:34 UTC](https://meta.discourse.org/t/setup-discourseconnect-official-single-sign-on-for-discourse-sso/13045/1 "2014-02-25T08:30:34Z")

</div>

[DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) — это ключевая функция Discourse, позволяющая настроить «Единый вход (SSO)» для полной делегации всей регистрации и входа пользователей из Discourse на другой сайт. Доступно для наших [клиентов с тарифами Pro, Business и Enterprise](https://discourse.org/pricing).

> ℹ (Февраль 2021) «[Discourse SSO](https://meta.discourse.org/t/13045?silent=true)» теперь называется «[DiscourseConnect](https://meta.discourse.org/t/13045?silent=true)». Если вы используете старую версию Discourse, настройки ниже будут называться `sso_...`, а не `discourse_connect_...`

 

### Проблема

Многие сайты, желающие интегрироваться с сайтом Discourse, хотят оставить всю регистрацию пользователей на отдельном сайте. В такой конфигурации все операции входа должны быть делегированы этому другому сайту.

### Что делать, если я хочу использовать SSO вместе с существующей аутентификацией?

Идея [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) заключается в замене аутентификации в Discourse. Если вы хотите добавить нового провайдера, обратитесь к существующим плагинам, например: [Discourse VK Authentication (vkontakte)](https://meta.discourse.org/t/vk-com-login-vkontakte/12987)

### Включение [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true)

Чтобы включить [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true), необходимо заполнить три настройки:

 ![Screenshot 2021-02-11 at 12.40.34](https://global.discourse-cdn.com/meta/original/3X/d/0/d00e1a088a93b334b9cd7285854d904ed57ec417.jpeg)

`enable_discourse_connect`: должно быть включено, глобальный переключатель  
`discourse_connect_url`: **внешний** URL, на который будут перенаправлены пользователи при попытке входа  
`discourse_connect_secret`: секретная строка, используемая для хеширования полезной нагрузки SSO. Гарантирует подлинность полезной нагрузки.

После установки `enable_discourse_connect` в значение true:

- Нажатие на кнопку входа или аватар перенаправит вас на `/session/sso`, который, в свою очередь, перенаправит пользователей на `discourse_connect_url` с подписанной полезной нагрузкой.
- Пользователям не будет разрешено «изменять пароль». Это поле удаляется из профиля пользователя.
- Пользователи больше не смогут использовать аутентификацию Discourse (имя пользователя/пароль, Google и т. д.).

### Что делать, если вы случайно отметили эту опцию?

См.: [Log back in as admin after locking yourself out with read-only mode or an invalid SSO configuration](https://meta.discourse.org/t/what-to-do-when-you-have-locked-yourself-out-by-invalid-sso-configuration-or-read-only-mode/89605)

### Реализация [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) на вашем сайте

> ⚠ Discourse использует адреса электронной почты для сопоставления внешних пользователей с пользователями Discourse и предполагает, что внешние адреса электронной почты защищены. **ЕСЛИ ВЫ НЕ ПРОВЕРЯЕТЕ АДРЕСА ЭЛЕКТРОННОЙ ПОЧТЫ ПЕРЕД ИХ ОТПРАВКОЙ В DISCOURSE, ВАШ САЙТ БУДЕТ ЧРЕЗВЫЧАЙНО УЯЗВИМ!**

В качестве альтернативы, если вы настаиваете на отправке непроверенных адресов электронной почты, **УБЕДИТЕСЬ** , что установлено значение `require_activation=true`. Это заставит Discourse проверить все адреса электронной почты. МЫ ВСЁ ЕЩЁ НАСТОЙЧИВО РЕКОМЕНДУЕМ НЕ ДЕЛАТЬ ЭТОГО, поэтому, если вы продолжите с включённой этой настройкой, вы берёте на себя значительный риск.

Discourse перенаправит клиентов на `discourse_connect_url` с подписанной полезной нагрузкой: (предположим, `discourse_connect_url` — это `https://somesite.com/sso`)

Вы получите входящий трафик следующего вида:

`https://somesite.com/sso?sso=PAYLOAD&sig=SIG`

Полезная нагрузка — это строка, закодированная в Base64, содержащая [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce) и `return_sso_url`. Полезная нагрузка всегда является допустимой строкой запроса.

Например, если nonce равен ABCD, то `raw_payload` будет:

`nonce=ABCD&return_sso_url=https%3A%2F%2Fdiscourse_site%2Fsession%2Fsso_login`, эта исходная полезная нагрузка закодирована в [Base64](http://en.wikipedia.org/wiki/Base64).

Вызываемая конечная точка должна:

1. Проверить подпись: убедиться, что HMAC-SHA256 от `PAYLOAD` (с использованием `discourse_connect_secret` в качестве ключа) равен `sig` (`sig` будет закодирован в шестнадцатеричном формате).
2. Выполнить необходимую аутентификацию.
3. Создать новую полезную нагрузку, закодированную в URL, содержащую как минимум **nonce** , **email** и **external\_id**. Вы также можете предоставить дополнительные данные; вот список всех ключей, которые понимает Discourse:
  - **nonce** должен быть скопирован из входной полезной нагрузки.
  - **email** должен быть проверенным адресом электронной почты. Если адрес электронной почты не был проверен, установите **require\_activation** в значение «true».
  - **external\_id** — это любая строка, уникальная для пользователя, которая никогда не изменится, даже если изменятся его адрес электронной почты, имя и т. д. Рекомендуемое значение — номер строки «id» в вашей базе данных.
  - **username** станет именем пользователя в Discourse, если пользователь новый или установлено значение `SiteSetting.auth_overrides_username`.
  - **name** станет полным именем в Discourse, если пользователь новый или установлено значение `SiteSetting.auth_overrides_name`.
  - **avatar\_url** будет загружен и установлен как аватар пользователя, если пользователь новый или установлено значение `SiteSetting.discourse_connect_overrides_avatar`.
  - **avatar\_force\_update** — это булево поле. Если установлено в true, оно заставит Discourse обновить аватар пользователя, независимо от того, изменился ли `avatar_url` или нет.
  - **bio** станет содержимым биографии пользователя, если пользователь новый, его биография пуста или установлено значение `SiteSetting.discourse_connect_overrides_bio`.
  - **title** установит должность пользователя.
  - **website** установит веб-сайт пользователя в его профиле.
  - **location** установит местоположение пользователя в его профиле.
  - **profile\_background\_url** будет загружен и установлен как фон профиля пользователя, если пользователь новый или установлено значение `SiteSetting.discourse_connect_overrides_profile_background`.
  - **card\_background\_url** будет загружен и установлен как фон карточки пользователя, если пользователь новый или установлено значение `SiteSetting.discourse_connect_overrides_card_background`.
  - **locale** установит локаль пользователя, если пользователь новый и включено значение `SiteSetting.allow_user_locale`.
  - **locale\_force\_update** — это булево поле. Если установлено в true вместе с **locale** , оно заставит обновить локаль для существующих пользователей (требуется `SiteSetting.allow_user_locale`).
  - Дополнительные булевы поля («true» или «false»): **admin** , **moderator** , **suppress\_welcome\_message** , **logout**.

4. Закодировать полезную нагрузку в Base64.
5. Рассчитать хеш HMAC-SHA256 от полезной нагрузки, используя `discourse_connect_secret` в качестве ключа и полезную нагрузку, закодированную в Base64, в качестве текста.
6. Перенаправить обратно на `return_sso_url` с параметрами запроса `sso` и `sig` (`http://discourse_site/session/sso_login?sso=payload&sig=sig`).

Discourse проверит, что nonce действителен, и если да, то сразу же истечёт его срок действия, чтобы его нельзя было использовать повторно. Затем он попытается:

1. Войти пользователя, найдя уже связанный **external\_id** в модели `SingleSignOnRecord`.
2. Войти пользователя, используя предоставленный адрес электронной почты (обновляя external\_id) ( **если require\_activation = true** ).
3. Создать новую учётную запись для пользователя, предоставив (email, username, name), обновляя external\_id.

### Вопросы безопасности

Nonce (одноразовый токен) автоматически истекает через 30 минут. Это означает, что как только пользователь будет перенаправлен на ваш сайт, у него есть 30 минут для входа или создания новой учётной записи.

Протокол защищён от атак повторного воспроизведения, так как nonce может быть использован только один раз. Nonce привязан к текущей сессии браузера для защиты от CSRF-атак.

### Указание членства в группах

Если указана опция _[discourse connect](https://meta.discourse.org/t/13045?silent=true) overrides groups_, Discourse будет учитывать список групп, разделённый запятыми, переданный в `groups`.

 ![Screenshot 2021-02-11 at 12.35.15](https://global.discourse-cdn.com/meta/original/3X/f/0/f044ed333fad01c7c7064807ef4305ec54c84e4c.jpeg)

Помимо `groups`, вы также можете указать членство в группах в своей полезной нагрузке SSO, используя атрибуты `add_groups` и `remove_groups`, независимо от опции _[discourse connect](https://meta.discourse.org/t/13045?silent=true) overrides groups_.

`add_groups` — это список названий групп, разделённый запятыми, в которых мы гарантируем, что пользователь является участником.  
`remove_groups` — это список названий групп, разделённый запятыми, в которых мы гарантируем, что пользователь **не** является участником.

### Референсная реализация

Discourse содержит референсную реализацию класса SSO:

> <https://github.com/discourse/discourse/blob/main/lib/discourse_connect_base.rb>

Тривиальная реализация может выглядеть так:

```ruby
class DiscourseSsoController < ApplicationController
  def sso
    secret = "MY_SECRET_STRING"
    sso = DiscourseApi::SingleSignOn.parse(request.query_string, secret)
    sso.email = "user@email.com"
    sso.name = "Bill Hicks"
    sso.username = "bill@hicks.com"
    sso.external_id = "123" # уникальный идентификатор для каждого пользователя вашего приложения
    sso.sso_secret = secret

    redirect_to sso.to_url("http://l.discourse/session/sso_login")
  end
end

```

### Переход к и от единого входа.

Пока параметр `require_activation` не установлен в `true` в полезной нагрузке запроса, система доверяет адресам электронной почты, предоставленным конечной точкой единого входа. Это означает, что если у вас ранее была существующая учётная запись в Discourse с отключённым [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true), [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) просто повторно использует её и избегает создания новой учётной записи.

Если вы когда-либо отключите [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true), пользователи смогут сбросить пароли и получить доступ к своим учётным записям.

### Пример из реальной жизни:

При следующих настройках:

Домен Discourse: `http://discuss.example.com`  
URL [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true): `http://www.example.com/discourse/sso`  
Секрет [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true): `d836444a9e4084d5b224a60c208dce14`  
Проверка электронной почты: Нет (добавьте `require_activation=true` в полезную нагрузку)

**Попытка пользователя войти в систему**

- Генерируется nonce: `cb68251eefb5211e58c00ff1395f0c0b`

- Генерируется исходная полезная нагрузка: `nonce=cb68251eefb5211e58c00ff1395f0c0b`

- Полезная нагрузка кодируется в Base64: `bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=`

- Полезная нагрузка кодируется в URL: `bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D`

- Генерируется HMAC-SHA256 для полезной нагрузки, закодированной в Base64: `1ce1494f94484b6f6a092be9b15ccc1cdafb1f8460a3838fbb0e0883c4390471`

Наконец, браузер перенаправляется на:

`http://www.example.com/discourse/sso?sso=bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D&sig=1ce1494f94484b6f6a092be9b15ccc1cdafb1f8460a3838fbb0e0883c4390471`

**На другом конце**

1. Полезная нагрузка **проверяется** с помощью HMAC-SHA256; если sig не совпадает, процесс прерывается.
2. Путём обратного выполнения вышеуказанных шагов извлекается nonce.

Пользователь входит в систему:

```plaintext
name: sam
external_id: hello123
email: test@test.com
username: samsam
require_activation: true

```

Генерируется неподписанная полезная нагрузка:

`nonce=cb68251eefb5211e58c00ff1395f0c0b&name=sam&username=samsam&email=test%40test.com&external_id=hello123&require_activation=true`

порядок не имеет значения, значения кодируются в URL

Полезная нагрузка кодируется в Base64:

`bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1zYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRlcm5hbF9pZD1oZWxsbzEyMyZyZXF1aXJlX2FjdGl2YXRpb249dHJ1ZQ==`

Полезная нагрузка кодируется в URL:

`bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1zYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRlcm5hbF9pZD1oZWxsbzEyMyZyZXF1aXJlX2FjdGl2YXRpb249dHJ1ZQ%3D%3D`

Полезная нагрузка, закодированная в Base64, подписывается:

`3d7e5ac755a87ae3ccf90272644ed2207984db03cf020377c8b92ff51be3abc3`

Браузер перенаправляется на:

`http://discuss.example.com/session/sso_login?sso=bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1zYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRlcm5hbF9pZD1oZWxsbzEyMyZyZXF1aXJlX2FjdGl2YXRpb249dHJ1ZQ%3D%3D&sig=3d7e5ac755a87ae3ccf90272644ed2207984db03cf020377c8b92ff51be3abc3`

### Синхронизация записей [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true)

Вы можете использовать POST-конечную точку администратора `/admin/users/sync_sso` для синхронизации записи [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true), передав ей ту же запись, которую вы бы передали конечной точке [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true); nonce не имеет значения.

Если вы вызываете `admin/users/sync_sso` с другого сайта, вам нужно включить в заголовки запроса действительный `api_key` администратора и действительное `api_username`. Подробнее о том, как структурировать запрос, см. на [Sync DiscourseConnect user data with the sync\_sso route](https://meta.discourse.org/t/sync-discourseconnect-user-data-with-the-sync-sso-route/84398).

### Очистка записей [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true)

Если значения `external_id` от вашего провайдера [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) изменились (возможно, вы изменили алгоритм генерации, возможно, это другая конечная точка), вы можете безопасно удалить все существующие записи, используя консоль Rails:

```ruby
SingleSignOnRecord.destroy_all

```

### Выход пользователей из системы

Вы можете использовать POST-конечную точку администратора `/admin/users/{USER_ID}/log_out` для выхода любого пользователя из системы при необходимости.

Чтобы настроить конечную точку, на которую перенаправляет Discourse при выходе, найдите настройку `logout redirect`. Если здесь не установлен URL, вы будете перенаправлены обратно на URL, настроенный в `discourse connect url`.

### Поиск пользователей по `external_id`

Данные профиля пользователя можно получить с помощью конечной точки `/users/by-external/{EXTERNAL_ID}.json`. Это вернёт полезную нагрузку JSON, содержащую информацию о пользователе, включая `user_id`, который можно использовать с конечной точкой `log_out`.

### Существующие реализации

- Гем [`discourse_api`](https://github.com/discourse/discourse_api) можно использовать для SSO. Посмотрите код SSO в его [каталоге примеров](https://github.com/discourse/discourse_api/tree/master/examples), чтобы увидеть базовую реализацию.

- Наш [плагин для WordPress](https://wordpress.org/plugins/wp-discourse/) упрощает настройку SSO между WordPress и Discourse. Подробные сведения о его настройке находятся на вкладке SSO на странице параметров плагина.

### Будущая работа

- Мы хотели бы собрать больше референсных реализаций SSO для других платформ. Если у вас есть такая, пожалуйста, опубликуйте её в [категории Dev / SSO](https://meta.discourse.org/c/sso/24).

### Расширенные функции

- Вы можете передавать пользовательские поля пользователей, добавляя префикс `custom` к имени поля. Например, `custom.user_field_1` можно использовать для установки значения `UserCustomField` с именем `user_field_1`.
- Вы можете передать `avatar_url` для переопределения аватара пользователя (должно быть включено `SiteSetting.discourse_connect_overrides_avatar`). Аватары кэшируются, поэтому передайте `avatar_force_update=true`, чтобы принудительно обновить их, если URL одинаков. В настоящее время вы не можете передать пустой URL для отключения аватара пользователя.
- По умолчанию всем новым пользователям, созданным через SSO, отправляется приветственное сообщение. Если вы хотите отключить это, передайте `suppress_welcome_message=true`.
- Чтобы настроить ваш экземпляр Discourse в качестве провайдера [Discourse Connect](https://meta.discourse.org/t/13045?silent=true), см.: [Использование DiscourseConnect в качестве провайдера идентификации](https://meta.discourse.org/t/use-discourse-as-an-identity-provider-sso-discourseconnect/32974).

### Отладка вашего провайдера [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true)

Чтобы помочь в отладке [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true), вы можете включить настройку сайта `verbose_discourse_connect_logging`. При включении этой настройки подробная диагностика будет отображаться в `YOURSITE.com/logs`. Обязательно ✅ поле `warnings` в нижней части `YOURSITE.com/logs`.

Мы будем записывать предупреждение в логи с полным дампом полезной нагрузки SSO:

- Каждый раз, когда инициируется процесс [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true), мы будем записывать предупреждение в лог с полным дампом полезной нагрузки [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true).

- Каждый раз, когда пользователю не удаётся завершить [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) (из-за истечения nonce или блокировки IP).

> 🗒 Нужно автоматизировать регистрацию пользователей? См. [Auto-provisioning user accounts when SSO is enabled](https://meta.discourse.org/t/auto-provisioning-user-accounts-when-sso-is-enabled/399917)

---

_[View the full topic](https://meta.discourse.org/t/setup-discourseconnect-official-single-sign-on-for-discourse-sso/13045)._
