Базовый OAuth2 для Discourse

:discourse2: Краткое содержание Discourse OAuth2 Basic поддерживает базовых провайдеров OAuth2, предполагая наличие у них JSON-эндпоинта, через который можно получить данные пользователя по токену.
:open_book: Руководство по установке Этот плагин включён в ядро Discourse. Отдельная установка плагина не требуется.

Возможности

Этот плагин позволяет использовать базового провайдера OAuth2 для аутентификации в Discourse. Он должен работать со многими провайдерами при условии, что они предоставляют JSON-эндпоинт для получения информации о пользователе, который входит в систему.

Это в основном полезно для тех, кто использует провайдеров входа, которые не очень популярны. Если вы хотите использовать Google, Facebook или Twitter, они уже включены по умолчанию, и вам не нужен этот плагин. Вы также можете поискать других провайдеров входа в нашем репозитории на Github.

Настройка

Базовая настройка

  1. Сначала зарегистрируйте ваше приложение Discourse у вашего провайдера OAuth2. Вам потребуется указать Redirect URI, который будет следующим:

    http://DISCOURSE_HOST/auth/oauth2_basic/callback

:information_source: Замените DISCOURSE_HOST на соответствующее значение и убедитесь, что вы используете https, если он включён. Провайдер OAuth2 должен предоставить вам client ID и secret, а также несколько URL-адресов.

  1. Перейдите в АдминистрированиеНастройкиВход через OAuth2 и заполните базовую настройку для провайдера OAuth2:
  • oauth2_enabled - отметьте этот пункт, чтобы включить функцию
  • oauth2_client_id - идентификатор клиента от вашего провайдера
  • oauth2_client_secret - секретный ключ клиента от вашего провайдера
  • oauth2_authorize_url - URL авторизации вашего провайдера
  • oauth2_token_url - URL токена вашего провайдера.

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

Настройка JSON-эндпоинта пользователя

Теперь Discourse может получать токен авторизации от вашего провайдера OAuth2. К сожалению, для завершения аутентификации Discourse требует дополнительной информации.

Нам нужен API-эндпоинт, через который можно получить информацию о пользователе на основе токена.

Например, провайдер OAuth2 SoundCloud предоставляет такой URL. Если у вас есть токен OAuth2 для SoundCloud, вы можете отправить GET-запрос к https://api.soundcloud.com/me?oauth_token=A_VALID_TOKEN и получите JSON-объект с информацией о пользователе.

Чтобы настроить это в Discourse, нам нужно установить значение настройки oauth2_user_json_url. В данном случае мы введем следующее значение:

https://api.soundcloud.com/me?oauth_token=:token

Часть с :token указывает Discourse, что это значение нужно заменить на токен авторизации, полученный после завершения аутентификации.

Остался один последний шаг. Нам нужно сообщить Discourse, какие атрибуты доступны в полученном JSON. Вот пример ответа от SoundCloud:

{
  "id": 3207,
  "permalink": "jwagener",
  "username": "Johannes Wagener",
  "uri": "https://api.soundcloud.com/users/3207",
  "permalink_url": "http://soundcloud.com/jwagener",
  "avatar_url": "http://i1.sndcdn.com/avatars-000001552142-pbw8yd-large.jpg?142a848",
  "country": "Germany",
  "full_name": "Johannes Wagener",
  "city": "Berlin"
}

Переменные oauth2_json_user_id_path, oauth2_json_username_path, oauth2_json_name_path и oauth2_json_email_path должны быть установлены так, чтобы указывать на соответствующие атрибуты в JSON.

Единственным обязательным атрибутом является id — он нам нужен, чтобы при последующем входе пользователя мы могли найти правильный аккаунт. Остальные атрибуты полезны, если они доступны — они ускорят процесс регистрации пользователя, так как поля будут автоматически заполнены в форме.

Вот как я настроил параметры путей JSON:

  oauth2_json_user_id_path: 'id'
  oauth2_json_username_path: 'permalink'
  oauth2_json_name_path: 'full_name'

Я использовал permalink, потому что он кажется более похожим на то, что Discourse ожидает в качестве имени пользователя, чем поле username в их JSON. Обратите внимание, что я не указал путь к электронной почте: SoundCloud не предоставляет email, поэтому пользователю придётся ввести и подтвердить его при первой регистрации в Discourse.

Если нужные вам свойства в JSON-объекте вложены друг в друга, вы можете использовать точки. Например, если API возвращает структуру такого вида:

{
  "user": {
    "id": 1234,
    "email": {
      "address": "test@example.com"
    }
  }
}

Вы можете использовать user.id для oauth2_json_user_id_path и user.email.address для oauth2_json_email_path.

Если сам ключ содержит точки, его нужно заключить в двойные кавычки или экранировать точки обратным слэшем. Например, для следующего JSON:

{
  "example.com/uid": "myuid"
}

вы укажете путь как example\.com/uid или "example.com/uid".

Синхронизация групп и полей пользователя

  • oauth2_json_groups_path: путь в JSON, содержащий группы пользователя в виде массива строк. После первого входа эти группы станут доступны для выбора в разделе «Связанные группы» настроек групп Discourse. Оставьте пустым, чтобы отключить синхронизацию групп.

  • oauth2_user_field_mappings: сопоставление путей в JSON, которые будут сохранены в пользовательских полях Discourse. Пользовательские поля идентифицируются по их числовому ID, который можно найти в URL при их редактировании через панель администратора.

:warning: Если вы установили oauth2_json_email_path, провайдер OAuth2 должен подтвердить, что пользователь владеет этим адресом электронной почты. Невыполнение этого требования может привести к захвату аккаунта в Discourse!

:discourse2: Размещено у нас? Этот плагин доступен в наших тарифах Business и Enterprise. OAuth 2.0 & OpenID Connect Support | Discourse - Civilized Discussion

:spiral_notepad: Нужно автоматизировать регистрацию пользователей? См. Auto-provisioning user accounts when SSO is enabled

28 лайков
Keycloak with Discourse
Discourse SSO with OAuth2
Login from another user database
Shopify Integration
OAuth2 integration with Drupal
How can we enable Auth0 SSO in Discourse
Is "partial" SSO possible?
How to login to discourse from external website
OAuth connection of discourse
How to use Oauth2 service provided by discourse?
How to force users link phone number when they using Discourse?
Set up Salesforce auth using OAuth2 basic support plugin
OAuth2 Custom Redirects Plugin
Login flow (Flask -> Discourse -> Flask) with OAuth
Auto-provisioning user accounts when SSO is enabled
Login on discourse using mastodon credentials
Custom Login / Registration from another API
Oauth2 with fusionauth cert issues
Open source will support customized provider SSO
SSO with TownNews CMS
What is supposed to go in “DISCOURSE_HOST”?
Custom Provider log-in with OAuth only sign-up/log-in
Discord, Google and Microsoft login, is oAuth2 enough?
Drupal 8 and Discourse shared SSO
Discourse for self hosting
Gate our community to just members of our Shopify site?
Discourse + Intercom (Current User Id)
Integration into custom auth system where emails are not unique?
Twitter login doesn't work on meta
ADFS Authentication
Question about Docker Manager?
Discourse OpenID Connect (OIDC)
🧩 How to Build an Android App User Community with Discourse? [HeyApks Project]
Bundling more popular plugins with Discourse core
Pulling user auth0 sub from OAuth2.0 plugin
Github and Twitter Login/Sign-Up Functionality?
Error during SSO integration - Wholistic Minds
Automatically creating a user when logging in with Webflow/Memberspace
Switching out authentication for a passwordless alternative
Setup DiscourseConnect - Official Single-Sign-On for Discourse (sso)
Intergrate Discourse with keycloak
CodeBerg support
Removing Yahoo login from Core, and deprecating OpenID 2.0
Configure sign up and log in with Auth0 using the OAuth2 Basic Plugin
Populating email field on login page
Migrate a Jive Clearspace forum to Discourse
SSO and Auth0
Suggestion for improving Integrated Authentication development
Shopify Integration

Здравствуйте,
Мы пытаемся интегрировать Discourse с нашим приложением, используя OAuth2 Basic, но в логах возникает следующая ошибка:
Примечание: Мы используем NGROK для отладки соединения.

Отладка OAuth2: запрос POST https://formshare.ngrok.io/oauth2/token

Заголовки: {"User-Agent"=>"Faraday v1.9.3", "Content-Type"=>"application/x-www-form-urlencoded", "Authorization"=>"Basic S2k2SFZtTVpuSTFHUExiRXVlWVJDNENiOkNvb1k0anlQemt3dWNRV21Sa2FWOVNnbHZLbjJFT3cxc3BIMmtMck9yY21vNDM4Tg=="}

Тело: {"client_id"=>"Ki6HVmMZnI1GPLbEueYRC4Cb", "client_secret"=>"...some_secret_...", "grant_type"=>"authorization_code", "code"=>"5pPCrsp0pZ84373MNaHh2cuskfc8AlbfmdwMBFIVW4n4z9aX", :redirect_uri=>"https://community.formshare.org/auth/oauth2_basic/callback"}

------------------

Отладка OAuth2: статус ответа 200

От POST https://formshare.ngrok.io/oauth2/token

Заголовки: {"content-length"=>"108", "content-type"=>"text/html; charset=UTF-8", "date"=>"Thu, 01 Sep 2022 21:42:08 GMT", "ngrok-trace-id"=>"79cdc3f1c3eae5e37a30796aebbf9bd6", "server"=>"gunicorn"}

Тело: {"token_type": "Bearer", "access_token": "p0FVuwjSXL1ZINEklMAVqUlpZxSll1SgnbpE8YWP4C", "expires_in": 864000}

-----------------------------------

(oauth2_basic) Ошибка аутентификации! invalid_credentials: OAuth2::Error, {"token_type": "Bearer", "access_token": "p0FVuwjSXL1ZINEklMAVqUlpZxSll1SgnbpE8YWP4C", "expires_in": 864000}

Мы оставили параметры «путь к ID пользователя в обратном вызове OAuth2» и «пути к информации о пользователе в обратном вызове OAuth2» пустыми.

Будем благодарны за любые предложения.

Могу ли я использовать это для аутентификации в службе XBL от Microsoft?

Предполагаю, что логика будет аналогична этой?

Всем привет. Я пытаюсь настроить этот плагин с нашим внутренним сервером OAuth2, используя поток с кодом авторизации.

Когда пользователь нажимает «Подключиться через OAuth», эндпоинт /authorize работает, и код возвращается в обратный вызов. Однако затем Discourse показывает общую ошибку 500: «Ой. Программное обеспечение, управляющее этим форумом, столкнулось с неожиданной проблемой», и эндпоинт /token не вызывается.

В логе ошибок указано следующее:
OAuth2::ConnectionError (FinalDestination: все разрешенные IP-адреса были запрещены) lib/final_destination/ssrf_detector.rb:74:in lookup_and_filter_ips' lib/final_destination/http.rb:13:in connect’ lib/midd

hostname discourse-app
process_id 653
application_version 702f27e6ee10ac257f5fee3f331d05f5fa5d7a45
HTTP_HOST *****
REQUEST_METHOD GET
HTTP_USER_AGENT Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
HTTP_ACCEPT text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
HTTP_REFERER *****
HTTP_X_FORWARDED_FOR *****
HTTP_X_REAL_IP *****
time 10:25 pm
params
code def50200babf84f7376f99fefa34369d876566b6bc0a341d8fba431999a72549ac06f6aad01df6fa43061707c525ba5d725ad
state 20139e0a134a5972566d4ddb6f7f9092a2cddb9e5216973a

Как я понимаю, проблема связана с каким-то IP-адресом? В данный момент сервер OAuth2 работает в моей среде разработки (localhost), и эндпоинты authorize и token настроены соответствующим образом. Является ли это проблемой?

Нашел проблему:

  1. По какой-то причине конечная точка /token никогда не вызывалась. После заполнения максимального количества параметров в админ-панели, связанных с OAuth, конечная точка вызывалась без ответа.
  2. Я забыл, что конечную точку /token должен вызывать сервер Discourse, а не веб-клиент. Следовательно, сервер не мог обратиться к моему локальному серверу OAuth2. Размещение нашего сервера OAuth2 за доменом решило проблему.

Теперь я могу подключать существующих пользователей, но не понимаю, как регистрировать новых пользователей через этот плагин.
Если пользователь входит через OAuth, появляется ошибка о том, что у него нет активной учетной записи на сервере Discourse. Это нормально, так как это новый пользователь.

Существует ли отдельный обратный вызов для регистрации пользователя вместо входа? Или есть специальный параметр, который нужно установить для разрешения создания учетной записи?

OAuth-сервер моей компании возвращал JSON-ответ по адресу /profile с небольшой опечаткой в одном из полей. После исправления опечатки всё заработало корректно.
Однако хочу отметить, что логи Discourse могут вводить в заблуждение! С самим колбэком всё было в порядке.

Привет, команда,

У меня возникла проблема с извлечением ID, необходимого для моего JSON-запроса пользователя, из ответа авторизации. Согласно документации, ID аккаунта передается в вложенном массиве:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
   "access_token":"2YotnFZFEjr1zCsicMWpAA",
   "token_type":"Bearer",
   "expires_in":1800,
   "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
   "permissions":[
      {
        "accountId":123,
        "availableScopes":["contacts_view", "contacts_me", 
"contacts_edit", "finances_view", "events_view"]
      }
   ]
}

Я пробовал установить путь к ID пользователя в обратном вызове OAuth2 как permissions[0].accountId, но значение uid всегда остается пустым. К сожалению, для вызовов получения JSON пользователя требуется этот accountId в URL запроса.

Мне удалось заставить это работать, передав permissions.first.accountId. Я обнаружил, что при передаче permissions в тестовое свойство массив уже был распарсен как массив Ruby. К сожалению, поля, похоже, отвергают синтаксис Ruby для обращения к элементам массива, и любая попытка использовать синтаксис JavaScript приводила к ошибке TypeError: String to Integer. К счастью, в Ruby есть синтаксис, приведенный выше. Является ли это предполагаемым методом?

У меня наконец получилось настроить интеграцию с Authentik OAuth2, однако возникли некоторые сложности с настройкой oauth2 user json url. Я использовал эндпоинт user_info Authentik (/application/o/userinfo/), но не знал, как сопоставить поля. Для тех, кто ищет информацию о настройке Discourse с OAuth2 от Authentik, вот краткое резюме:

  • Путь к ID пользователя: preferred_username
  • Путь к имени пользователя: preferred_username
  • Путь к имени: name
  • Путь к электронной почте: email
  • Путь к подтверждению электронной почты: email_verified
  • Аватар: пусто.

У меня возникли следующие проблемы:

  1. Сначала я забыл добавить завершающий слэш в URL JSON https://DOMAIN/application/o/userinfo/. Из-за этого запрос информации о пользователе (ссылка на исходный код) возвращал HTTP-код 301, что приводило к сбою входа. Не знаю, обязателен ли завершающий слэш по спецификации, но, возможно, стоит правильно обрабатывать код 301.
  2. Отладка этого процесса оказалась непростой. Настройка oauth2 debug auth оказалась спасением, но… Logster обрезает отладочный лог до того, как выводит содержательные данные ответа. Мне пришлось вручную изменить строку лога в контейнере на:
    log("user_json_response: #{user_json_response.status} #{user_json_response.headers} #{user_json_response.body}")
    
    Возможно, эту строку лога стоит обновить? Думаю, это поможет другим пользователям разобраться с путями атрибутов JSON.
4 лайка

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

Вот соответствующие настройки:

  DISCOURSE_OAUTH2_ENABLED: true
  DISCOURSE_OAUTH2_CLIENT_ID: '${DISCOURSE_OAUTH2_CLIENT_ID}'
  DISCOURSE_OAUTH2_CLIENT_SECRET: '${DISCOURSE_OAUTH2_CLIENT_SECRET}'
  DISCOURSE_OAUTH2_AUTHORIZE_URL: '${DISCOURSE_OAUTH2_ISSUER}/authorize?connection=xxx&login_options=yyy'
  DISCOURSE_OAUTH2_TOKEN_URL: '${DISCOURSE_OAUTH2_ISSUER}/oauth/token'
  DISCOURSE_OAUTH2_USER_JSON_URL: '${DISCOURSE_OAUTH2_ISSUER}/userinfo'
  DISCOURSE_OAUTH2_SCOPE: 'email openid profile'
  DISCOURSE_OAUTH2_JSON_USER_ID_PATH: 'sub'
  DISCOURSE_OAUTH2_JSON_USERNAME_PATH: 'nickname'
  DISCOURSE_OAUTH2_JSON_NAME_PATH: 'name'
  DISCOURSE_OAUTH2_JSON_EMAIL_PATH: 'email'
  DISCOURSE_OAUTH2_JSON_EMAIL_VERIFIED_PATH: 'email_verified'
  DISCOURSE_OAUTH2_JSON_AVATAR_PATH: 'picture'
  DISCOURSE_OAUTH2_EMAIL_VERIFIED: true
  DISCOURSE_OAUTH2_OVERRIDES_EMAIL: true
  DISCOURSE_OAUTH2_ALLOW_ASSOCIATION_CHANGE: false

В логах отладки я вижу, что элемент picture присутствует в JSON-ответе, но аватар пользователя не меняется ни для новых, ни для существующих пользователей.

Что я упустил?

Какой лучший способ заменить иконку на кнопке входа на другую иконку или изображение?

.btn-social.oauth2_basic:before {
    content: url('https://www.contoso.com/path/to/image');
}

.btn-social.oauth2_basic > svg {
    display: none;
}

Это выглядит достаточным, но немного хакичным.

2 лайка

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

Есть ли способ исправить это, чтобы плагин обновлял аватар также при входе/переподключении?

Вы можете использовать настройки auth overrides email, auth overrides username и auth overrides name, чтобы эти изменения применялись при следующих входах в систему. К сожалению, у нас пока нет аналогичной настройки для аватаров, но мы будем рады соответствующему pull-запросу.

2 лайка

Спасибо! Я позже сам нашёл эти материалы. Я сделал форк репозитория и добавил свои версии, чтобы всё заработало с Roblox, включая переопределение аватаров. Насколько я понимаю, это использует переопределение аватара через DiscourseConnect, чтобы пользователи не могли его изменить.

Однако есть один момент: Roblox не предоставляет email через OAuth, поэтому, к сожалению, им приходится регистрироваться с указанием email. Но для вас это, думаю, не проблема, ха-ха.

Пост был разделён на новую тему: Вход в Twitter не работает на Meta

Кто-нибудь знает, работает ли это ещё?

Да. Я уверен, что этот плагин работает. :+1:

1 лайк

Привет, мне удалось интегрировать этот плагин в мой форум discuss.frontendlead.com. Я использую OAuth от Teachable: OAuth Quickstart Guide

Однако я хочу разрешить успешную регистрацию только тем пользователям, у которых есть активный платный аккаунт на Teachable. Думаю, мне нужно добавить в плагин пользовательский функционал для обработки этого. Подскажите, возможно ли добавить новое поле в настройки с названием «Пользовательский код после OAuth», которое позволит разработчикам выполнять определённые действия после регистрации? Или, если есть лучшие предложения, пожалуйста, дайте знать.

Редактирование: Я форкнул репозиторий и получил работающее решение здесь:

Если кто-то другой, кто использует Teachable, пытается сделать то же самое, мой репозиторий будет работать из коробки. Единственное, если вы не купили курс, система сообщит, что нужно перейти на мой домен для покупки. Возможно, вам стоит обновить это сообщение под свои нужды.

2 лайка

Это здорово!

Аналогичная ситуация возникает с регистрацией через OAuth2 в плагине Discourse Patreon. Когда включена опция «Войти через Patreon», зарегистрироваться на сайте Discourse может любой пользователь с аккаунтом Patreon. Обычно владельцы сайтов хотят разрешить регистрацию только своим сторонникам. Интересно, возвращает ли Patreon данные, которые позволили бы внедрить аналогичную логику в аутентификацию через Patreon?

1 лайк

У меня точно такая же ошибка, как у @qlands выше.

Мой первоначальный план заключался в том, чтобы отправлять информацию профиля в токене. Поскольку это не сработало, я упростил отступы, чтобы попробовать подход с JSON. Но дело даже не доходит до вызова JSON-файла.

Сообщение об ошибке:

(oauth2_basic) Authentication failure! invalid_credentials: OAuth2::Error, {
  "access_token":"fa79b6fe0763862f5a8fd8",
  "token_type":"Bearer",
  "expires_in":3600,
  "scope":"profile"
} 

Вы видите что-то не так в вышеприведённом ответе?
Почему плагин выдаёт ошибку invalid_credentials, в то время как сервер OAuth2 ответил кодом 200 с токеном?