Use Discourse as an identity provider (SSO, DiscourseConnect)

Мне удалось использовать это для создания системы привязки учётных записей для моего сервера Minecraft! Решил поделиться тем, как это выглядит! Это был мой первый опыт работы с SSO Discourse, поэтому, возможно, я всё излишне усложнил. Однако главное, что это работает.

4 лайка

Привет, я всё сделал правильно. Но когда пользователь не авторизован в Discourse, появляется всплывающее окно входа. Когда я ввожу имя пользователя и пароль, меня не перенаправляет обратно на return_url. Не могли бы вы помочь?

Я предполагаю, что nonce нужен для предотвращения повторных атак (replay attacks). Я читал в интернете, что с HTTPS повторные атаки невозможны, а я использую именно его. Значит ли это, что мне всё ещё нужен nonce? Я спрашиваю, потому что не уверен, где его хранить. Имеет ли смысл хранить его как безопасный cookie с открытым текстом в браузере пользователя, а затем считывать его из браузера вместе с возвращаемой полезной нагрузкой?

Эта библиотека, на которую есть ссылка в оригинальном посте GitHub - ArmedGuy/discourse_sso_node: npm package for Discourse SSO login features. · GitHub, не использует nonce при валидации пользователя.

Да, вам всё ещё необходимо проверять nonce, так как он предотвращает повторное использование полезной нагрузки (payload), которую Discourse отправляет при перенаправлении пользователей обратно на ваш сайт.

Например, пусть на вашем сайте есть контент за платным доступом, который могут просматривать только пользователи группы subscribers в Discourse, и вы используете поле groups в полезной нагрузке, отправляемой Discourse на ваш сайт, чтобы показывать платный контент только членам группы subscribers. Если вы не будете проверять nonce, пользователь, который больше не состоит в группе subscribers, сможет использовать старую полезную нагрузку от момента, когда он был членом группы, чтобы войти на ваш сайт и получить доступ к платному контенту.

Лучше всего хранить nonce в базе данных с коротким сроком действия и удалять nonce из базы данных сразу после его использования. Однако, если вы не можете использовать базу данных, вы можете использовать cookie для хранения nonce, но вам необходимо выполнить дополнительные шаги для предотвращения повторного использования полезной нагрузки:

  1. Добавьте дату истечения срока действия к nonce при его создании, например, через 10 минут от текущего времени.
  2. Подпишите весь cookie (nonce + дата истечения срока действия), чтобы предотвратить модификацию nonce и/или даты истечения срока действия пользователем.
  3. Проверьте подпись cookie и убедитесь, что nonce не истёк.

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

Более простое решение, не требующее cookie, — включить дату истечения срока действия в пользовательское поле в полезной нагрузке, которую вы генерируете. Тогда, когда Discourse перенаправит пользователей обратно на ваш сайт с полезной нагрузкой, ваши пользовательские поля будут включены, и вы сможете извлечь дату истечения срока действия и проверить, не истекла ли она. Чтобы включить пользовательское поле в полезную нагрузку, необходимо добавить поле с префиксом custom., поэтому ваша полезная нагрузка будет выглядеть так:

nonce=NONCE&return_sso_url=RETURN_URL&custom.expiration_date=TIMESTAMP
4 лайка

Вы также можете хранить nonce в сессии, что также предотвратит манипулирование им пользователем.

3 лайка

Всего лишь возвращаюсь к этой теме спустя годы

Может кто-нибудь сказать мне (@pfaffman или @tobiaseigen или @iamntz), что возвращает провайдер SSO Discourse? Я знаю, что могу «попробовать и посмотреть», но было бы неплохо иметь это в документации. В примере кода на PHP для GitHub даже не упоминаются какие-либо другие поля.

В идеале, он должен отправлять те же поля, что и когда Discourse использует внешний скрипт для SSO, такие как внешний ID, email, имя пользователя, имя, фото аватара и т.д. Чтобы мы могли импортировать это и создать пользователя на нашей стороне!

Сообщает ли он также WordPress email?

А как насчет групп, бейджей и т.д.? Можем ли мы найти эту информацию, делая REST-запросы?

И наконец, что насчет личных сообщений пользователя и прочего? Думаю, если Discord был бы провайдером oAuth и позволял нашим приложениям потреблять эту информацию, это было бы здорово.

При попытке включить Discourse Connect возникает следующая ошибка:
enable_discourse_connect: Вы не можете одновременно включить DiscourseConnect и разрешить приглашения только для приглашенных.

Есть какие-либо идеи?

Я вижу, что вы уже задавали тот же вопрос здесь: Setup DiscourseConnect - Official Single-Sign-On for Discourse (sso) - #537. Если вы пытаетесь настроить параметр enable discourse connect, а не enable discourse connect provider, то правильный адрес для вашего вопроса — другая тема.

Параметр enable discourse connect provider предназначен для случаев, когда вы хотите использовать свой сайт Discourse в качестве провайдера идентификации для другого сайта. Параметр enable discourse connect используется, когда вы хотите входить в Discourse через внешний сайт.

1 лайк

Я реализовал эту процедуру на Python для Flask-приложения, которое создаю. Вот шаблонный код для тех, кому он может понадобиться. Шаги, описанные в этой теме, были довольно простыми для выполнения, но я не специалист по безопасности, поэтому, если я что-то упустил, пожалуйста, дайте знать!

2 лайка

Мы пытаемся внедрить Discourse в качестве провайдера единого входа (SSO), и мы не понимаем, как Discourse определяет, какого пользователя нужно проверить. В инструкциях сказано: «Создайте новую полезную нагрузку с nonce и URL-адресом возврата». Но когда вы отправляете это в Discourse через fetch, как Discourse узнает, какого пользователя проверять на предмет входа в систему? Извините, если вопрос звучит глупо, но я просто не могу понять, как это работает, хотя за годы работы я сталкивался со множеством систем аутентификации и в целом знаком с ними. Нужно ли включать адрес электронной почты пользователя, для которого мы проверяем статус входа, в полезную нагрузку, отправляемую в Discourse? Если да, то какова точная структура полезной нагрузки, которую необходимо отправить в Discourse? Если нет, то что именно проверяет Discourse? Я предполагаю, что мы запрашиваем у пользователя его адрес электронной почты на нашей стороне, а затем отправляем полезную нагрузку с этим адресом в Discourse, чтобы проверить, вошел ли в систему именно этот пользователь, но в инструкциях написано не так, поэтому я совершенно запутался. Спасибо за любую помощь.

Не стоит беспокоиться. Мы разобрались. Мы думали, что URL SSO нужно отправлять как POST-запрос к экземпляру Discourse и затем получать ответ. Теперь мы видим, что это перенаправление на Discourse, а затем Discourse перенаправляет обратно на наш сайт. Теперь всё ясно, что делать. Извините за предыдущий пост.

3 лайка

FYI/FWIW: Я отправил PR, который добавляет возможность использования параметра prompt=none в запросе авторизации. Аналогично функции в протоколе OpenID Connect, это позволяет потребителю SSO проверять, выполнен ли вход пользователя/клиента, не перенаправляя его на окно входа в случае, если вход не выполнен.

PR находится в ожидании финальной проверки кем-то из команды Discourse уже около 8 недель; это кажется значительно дольше, чем я ожидал. :crying_cat_face:

7 лайков

Привет, @mdoggydog — извините за такую долгую задержку!

Я только что просмотрел и принял ваш PR — спасибо за вклад! :raised_hands:

3 лайка

Ура! Спасибо, @david.

Как и обещал, я только что обновил статью в вики здесь, добавив описание нового параметра (а также более раннего нового параметра logout), исправив несколько мелких опечаток и грамматических ошибок, а также добавив раздел с ссылками, документирующий полезную нагрузку sso=, насколько я понимаю это из изучения исходного кода.

2 лайка

Я хочу перестать использовать наш веб-сайт в качестве SSO для Discourse и вместо этого воспользоваться встроенными инструментами входа в Discourse для ограничения доступа к определенным материалам на нашем сайте.

Мне кажется, подходящий инструмент для этого: GitHub - discourse/discourse-auth-proxy: An http proxy that uses the DiscourseConnect protocol to authenticate users · GitHub

Я не нашел подробных инструкций по его использованию.

Могу ли я установить его на том же цифровом облачном сервере (droplet) DigitalOcean, где размещен наш сайт Discourse, или мне нужно разместить его в другом месте?

Редактирование: выделил свой вопрос жирным шрифтом :slight_smile:

1 лайк

Есть ли какая-либо помощь по вышеуказанному вопросу? Use Discourse as an identity provider (SSO, DiscourseConnect) - #148 by alehandrof

Я устанавливаю в качестве return_sso_url URL, который сам содержит параметр запроса:

http://localhost:7000/completeLogin?returnto=%2F

Полезная нагрузка, которую я отправляю в /session/sso_provider как параметр sso, до base64-кодирования выглядит так:

nonce=ENIwf0bElViDu325dTd6&return_sso_url=http://localhost:7000/completeLogin?returnto=%2F

URL, на который Discourse фактически перенаправляет после аутентификации, выглядит так (параметры sso и sig сокращены):

http://localhost:7000/completeLogin?returnto=/&sso=...&sig=...

Меня удивляет, что строка запроса, которую я указал для return_sso_url, похоже, была декодирована URL-декодером неким компонентом, так как теперь она содержит returnto=/ вместо returnto=%2F. Значение return_sso_url, которое я нахожу внутри sso после base64-декодирования, также содержит косую черту вместо %2F.

Это то, чего мне следует ожидать? (Если да, то почему?) Является ли это ошибкой в Discourse?

В чём причина того, что в полезной нагрузке sso содержится поле avatar_url вместо avatar_template, которое возвращается в /u/{username}.json и /session/current.json?

avatar_url отсутствует у пользователей, не установивших аватар, тогда как avatar_template содержит путь letter_avatar_proxy, который фактически используется в Discourse для отображения аватаров у таких пользователей. Кроме того, avatar_url указывает на исходное изображение аватара, а не на масштабированную версию нужного размера для пользователей, установивших аватар.

Мне кажется, что avatar_template — это именно то, что нужно любому, кто планирует использовать информацию об аватаре из sso, — но тогда потребуется сделать дополнительный запрос к API, чтобы получить её.

2 лайка

Сейчас я реализую SSO в своём приложении: вход работает отлично, а вот выход — нет.

Мой экземпляр Discourse корректно перенаправляет обратно на return URL без каких-либо параметров sso или sig, но при открытии Discourse моя учётная запись всё ещё в системе.

Есть какие-то идеи?

Я предполагаю, что вы используете настройку сайта «logout redirect» (перенаправление при выходе) в Discourse, чтобы перенаправлять пользователей обратно в ваше приложение после выхода из Discourse.

Возможной причиной проблемы может быть включённая настройка «login required» (требуется вход) на вашем сайте Discourse. Когда эта настройка включена, Discourse автоматически перенаправляет неавторизованных пользователей на сайт провайдера SSO, если они напрямую обращаются к сайту Discourse. Это означает, что если вы не выходите из своего приложения в момент первого перенаправления на URL «logout redirect», пользователи будут автоматически авторизованы в Discourse при следующем посещении сайта. Вы можете подтвердить это поведение, пройдя весь процесс с открытой вкладкой «Network» в инструментах разработчика вашего браузера.

На случай, если это будет полезно, вот как плагин WP Discourse обрабатывает перенаправление при выходе из Discourse: wp-discourse/lib/sso-provider/discourse-sso.php at main · discourse/wp-discourse · GitHub.

2 лайка