# Запуск создания аккаунта/входа во внешнем сервисе при входе пользователя в Discourse

**URL:** https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359
**Category:** Development
**Created:** [04.Декабрь.2024 13:18:09 UTC](https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359 "2024-12-04T13:18:09Z")
**Posts on this page:** 13
**Page:** 1

<div class="post-metadata">

### Author: ![Chris\_Dawson](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/chris_dawson/32/184486_2.png) [@Chris\_Dawson](https://meta.discourse.org/u/Chris_Dawson)
#### Post date: [04.Декабрь.2024 13:18:09 UTC](https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359/1 "2024-12-04T13:18:09Z")

</div>

На моём сайте Discourse установлен JS-плагин. Этот JS использует PocketBase для хранения данных. PocketBase — это «серверless»-сервис на базе SQLite, позволяющий хранить файлы и JSON-объекты. В PocketBase есть отличная система аутентификации на основе JWT: как только у пользователя появляется токен аутентификации, он может безопасно записывать данные напрямую в PocketBase (без проксирования через бэкенд-сервер и т.п.) прямо из JS на стороне клиента.

Я пытаюсь найти способ автоматически создавать учётную запись в PocketBase, когда пользователь входит в Discourse.

Моя первая попытка заключалась в том, чтобы JS-плагин делал запрос к определённому пути на сервере, передавая куки аутентификации Discourse. Затем этот путь должен был проксироваться через nginx к сервису («прокси входа»), который мог бы декодировать куки аутентификации и определять пользователя. После получения подтверждённых данных о пользователе прокси входа мог бы сделать специальный запрос в PocketBase, получить оттуда токен аутентификации PocketBase и вернуть его клиенту. Затем клиентский JS мог бы использовать этот токен для последующих запросов напрямую к PocketBase.

Однако у меня возникли трудности с декодированием куки аутентификации Discourse (я предполагаю, что правильная кука — `_t`, но не вижу простого способа извлечь данные о пользователе, к тому же меня беспокоит, что структура этих данных может измениться).

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

---

<div class="post-metadata">

### Author: ![renato](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/renato/32/383632_2.png) [@renato](https://meta.discourse.org/u/renato)
#### Post date: [04.Декабрь.2024 15:36:45 UTC](https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359/2 "2024-12-04T15:36:45Z")

</div>

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

У меня есть приложение на Next.js, где клиентская часть должна иметь валидный JWT для вызова моего бэкенд-API, если существует сессия в Discourse.

Для этого я использую [Discourse как провайдера идентификации через DiscourseConnect](https://meta.discourse.org/t/use-discourse-as-an-identity-provider-sso-discourseconnect/32974).

В моём случае я делаю это одним клиентским вызовом `fetch` с `{ credentials: "include" }`, что работает только потому, что у меня всё настроено в рамках одного домена, а вызов `fetch` прозрачно следует за перенаправлениями.

Мой клиент запрашивает кастомный эндпоинт `/auth/token`, который проверяет наличие `_t` (просто чтобы избежать бессмысленного перенаправления) и возвращает редирект на защищённый URL `/session/sso_provider`, построенный согласно документации в связанной теме, с параметрами `nonce`/`sso`/`sig` и `return_sso_url`, указывающим на кастомный `/auth/callback`. Этот коллбэк извлекает данные, отправленные Discourse, формирует и возвращает JWT-токен, который клиент может использовать с этого момента.

> [@Chris\_Dawson](#):
>
> Есть ли более умный способ безопасно получить адрес электронной почты авторизованного пользователя? Я не думаю, что это должно происходить на стороне клиента, и из соображений безопасности предпочёл бы делать это на стороне сервера.

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

> [@Use Discourse as an identity provider (SSO, DiscourseConnect)](https://meta.discourse.org/t/use-discourse-as-an-identity-provider-sso-discourseconnect/32974):
>
> So you want to use Discourse as an identity provider for your own web app? Great! Let’s get started. Enable [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) provider setting Under Discourse admin site settings (/admin/site\_settings) enable setting enable discourse connect provider and add a secret string to discourse connect provider secrets (used to hash SSO payloads). Implement [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) in your web app: Generate a random [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce). Let’s call this value NONCE. Save it temporarily so that you can verify it with the …

---

<div class="post-metadata">

### Author: ![Chris\_Dawson](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/chris_dawson/32/184486_2.png) [@Chris\_Dawson](https://meta.discourse.org/u/Chris_Dawson)
#### Post date: [04.Декабрь.2024 15:38:11 UTC](https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359/3 "2024-12-04T15:38:11Z")

</div>

Отлично, я попробую. Большое спасибо.

---

<div class="post-metadata">

### Author: ![Chris\_Dawson](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/chris_dawson/32/184486_2.png) [@Chris\_Dawson](https://meta.discourse.org/u/Chris_Dawson)
#### Post date: [04.Декабрь.2024 15:55:42 UTC](https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359/4 "2024-12-04T15:55:42Z")

</div>

@renato, если не возражаете, у меня несколько вопросов.

Значит ли это, что мне нужно делегировать всю аутентификацию управления пользователями приложению Connect? Я не уверен, хочу ли я этого.

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

Однако, когда я начал изучать [Discourse Connect](https://meta.discourse.org/t/13045?silent=true), я забеспокоился, что теперь мне нужно создать и поддерживать совершенно новое приложение для управления аутентификацией пользователей, а я пока не совсем понимаю, как правильно определить его масштаб.

---

<div class="post-metadata">

### Author: ![renato](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/renato/32/383632_2.png) [@renato](https://meta.discourse.org/u/renato)
#### Post date: [04.Декабрь.2024 16:27:55 UTC](https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359/5 "2024-12-04T16:27:55Z")

</div>

Мой ответ исходит из предположения, что вы используете Discourse в качестве провайдера идентификации (с его интерфейсами входа/регистрации), и хотите оставить всё именно так.

> [@Chris\_Dawson](#):
>
> Если Connect — это просто дополнительный слой поверх существующей системы управления аутентификацией пользователей Discourse, то это кажется выполнимым.

На стороне Discourse его включение так же просто, как

> [@Discourse](#):
>
> включение настройки `enable discourse connect provider` и добавление секретной строки в `discourse connect provider secrets` (используется для хеширования полезной нагрузки SSO).

Однако вы упомянули, что создаёте плагин.

> [@Chris\_Dawson](#):
>
> Моя первая попытка заключалась в том, чтобы JS-плагин делал запрос к **пути на сервере** и включал куки аутентификации Discourse. Затем для этого пути nginx должен проксировать запрос к сервису («прокси входа»), который может декодировать куки аутентификации и определить пользователя. Имея подтверждённую информацию о пользователе, прокси входа может сделать специальный запрос к PocketBase и получить обратно куку аутентификации PocketBase, а затем отправить её клиенту. Клиентский JS сможет использовать эту куку для последующих запросов напрямую к PocketBase.

Если вы реализуете «путь на сервере» как новое действие контроллера в плагине Discourse, вы сможете получить пользователя из сессии, вызвать сторонние сервисы и вернуть JWT вашему клиенту.

---

<div class="post-metadata">

### Author: ![Chris\_Dawson](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/chris_dawson/32/184486_2.png) [@Chris\_Dawson](https://meta.discourse.org/u/Chris_Dawson)
#### Post date: [04.Декабрь.2024 16:39:15 UTC](https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359/6 "2024-12-04T16:39:15Z")

</div>

Большое спасибо за обсуждение.

Я прочитал ссылку: [Use Discourse as an identity provider (SSO, DiscourseConnect) - #8 by reverend\_paco](https://meta.discourse.org/t/use-discourse-as-an-identity-provider-sso-discourseconnect/32974/8)

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

В моём случае у меня есть JS-скрипт, который выполняется на сайте Discourse, и я хочу, чтобы этот JS обращался к пути на том же сервере и получал обратно cookie для PocketBase.

На самом деле я использую nginx-прокси перед Discourse, поэтому просто добавил специальный маршрут `/pb/auth` (например). Когда мой JS обращается к этому маршруту, бэкенд-прокси-сервер (не являющийся частью Discourse) принимает соединение и пытается декодировать сессионный cookie `_t`.

Я поступал так, потому что это казалось немного проще, чем добавлять плагин для Discourse (я меньше знаком с ним и с настройкой разработки и т.д.). Если дело сводится к простому декодированию cookie с использованием base64 и хешированию sha, я думал, что это обеспечит защищённую полезную нагрузку для определения пользователя.

Но если вы считаете, что существует простой способ создать плагин, добавляющий этот маршрут в Discourse, я очень заинтересован в этом. Это кажется правильным подходом в долгосрочной перспективе. Но я старый программист на Perl, поэтому предпочитаю самый ленивый путь, и мой маршрут через nginx казался мне ещё более ленивым. 🙂

---

<div class="post-metadata">

### Author: ![renato](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/renato/32/383632_2.png) [@renato](https://meta.discourse.org/u/renato)
#### Post date: [04.Декабрь.2024 16:50:11 UTC](https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359/7 "2024-12-04T16:50:11Z")

</div>

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

Совершенно наоборот: если у вас есть отдельный «сайт» (в данном примере PocketBase) и вы хотите, чтобы Discourse был источником истины для управления пользователями и аутентификацией — как в моём примере с Next.js.

> [@Chris Dawson](#):
>
> Но, если вы думаете, что есть простой способ создать плагин, который добавит этот маршрут в Discourse,

Я бы начал с прочтения

> [@Creating Routes in Discourse and Showing Data](https://meta.discourse.org/t/creating-routes-in-discourse-and-showing-data/48827?silent=true):
>
> Over time Discourse has grown in complexity and it can be daunting for beginners to understand how data gets all the way from the back end Ruby on Rails application to the Ember.js application in front. This tutorial is meant to show the full lifecycle of a request in Discourse and explain the steps necessary if you want to build a new page with its own URL in our application. URLs First I always prefer to start thinking of features in terms of the URLs to access them. For example let’s say we…

---

<div class="post-metadata">

### Author: ![Chris\_Dawson](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/chris_dawson/32/184486_2.png) [@Chris\_Dawson](https://meta.discourse.org/u/Chris_Dawson)
#### Post date: [04.Декабрь.2024 17:14:02 UTC](https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359/8 "2024-12-04T17:14:02Z")

</div>

Отлично, я с нетерпением это прочитаю. Я начал изучать образец плагина-каркаса ([GitHub - discourse/discourse-plugin-skeleton: Template for Discourse plugins · GitHub](https://github.com/discourse/discourse-plugin-skeleton)), но остался немного разочарован, так как у него вообще нет документации.

С первого взгляда у меня возникает вопрос: добавляет ли этот учебник код в базовую установку Rails для Discourse? Я не против делать так, если это официальный способ, но это кажется опасным, и было бы лучше реализовать это как плагин (который можно легко удалить или отключить). Кроме того, не стоит ли мне беспокоиться, что это сломает обновления Discourse, если мой код не будет в репозитории GitHub?

Например, здесь:

> [@Creating Routes in Discourse and Showing Data](https://meta.discourse.org/t/creating-routes-in-discourse-and-showing-data/48827?silent=true#p-216704-the-server-side-ruby-on-rails-2):
>
> Over time Discourse has grown in complexity and it can be daunting for beginners to understand how data gets all the way from the back end Ruby on Rails application to the Ember.js application in front. This tutorial is meant to show the full lifecycle of a request in Discourse and explain the steps necessary if you want to build a new page with its own URL in our application. URLs First I always prefer to start thinking of features in terms of the URLs to access them. For example let’s say we…

Это значит, что мне действительно нужно зайти в контейнер (`./launcher enter app`) и затем отредактировать `/var/www/app/controllers/snack_controller.rb`?

И я на самом деле только что следовал этим инструкциям. Мне не удаётся заставить маршрут `/admin/snack.json` работать, даже после выполнения `./launcher rebuild app`.

Этот учебник, похоже, написан около восьми лет назад. Действительно ли это правильный способ делать вещи?

---

<div class="post-metadata">

### Author: ![renato](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/renato/32/383632_2.png) [@renato](https://meta.discourse.org/u/renato)
#### Post date: [04.Декабрь.2024 17:48:42 UTC](https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359/9 "2024-12-04T17:48:42Z")

</div>

Есть и другие руководства. Дата вверху указывает на дату создания темы, но всё в разделе #Documentation должно быть актуальным — сообщите нам, если обнаружите какие-либо проблемы.

> [@Разработка плагинов для Discourse — Часть 1: Создание базового плагина](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515):
>
> Создание плагина для Discourse может быть очень простым, как только вы разберётесь с несколькими особенностями. Цель этой статьи — создать каркас плагина и познакомить вас с основами. Ваша среда разработки Убедитесь, что у вас на компьютере запущена среда разработки Discourse. Рекомендуется использовать [соответствующее руководство по настройке](https://meta.discourse.org/tag/dev-install) и вернуться, когда закончите. plugin.rbtada Используйте [GitHub - discourse/discourse-plugin-skeleton: Template for Discourse plugins · GitHub](https://github.com/discourse/discourse-plugin-skeleton) для со…

Вы можете посмотреть код существующего #Customization > Plugin в качестве примера.

> [@Chris\_Dawson](#):
>
> Это означает, что мне действительно нужно зайти в контейнер?

Нет:

> [@Install plugins on a self-hosted site](https://meta.discourse.org/t/install-plugins-on-a-self-hosted-site/19157):
>
> warning This guide assumes that you have a self-hosted standard installation. We only support the standard method of install here, so these instructions assume you have a [standard install](https://meta.discourse.org/t/142537?silent=true). warning This guide only applies to self-hosted Discourse instances. If you are using a managed hosting service, the available plugins are controlled by your hosting provider. For example, on our hosting [these specific plugins](https://www.discourse.org/plugins) are available by hosting tier. information_source As of mid-2025, [many p…](https://meta.discourse.org/t/373574)

---

<div class="post-metadata">

### Author: ![Moin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/moin/32/554653_2.png) [@Moin](https://meta.discourse.org/u/Moin)
#### Post date: [04.Декабрь.2024 18:02:03 UTC](https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359/10 "2024-12-04T18:02:03Z")

</div>

> [@renato](#):
>
> Дата вверху — это дата создания темы, но всё, что находится в #Documentation, должно быть актуальным.

У меня сложилось впечатление, что это ещё не было обновлено.

> [@Discourse](#):
>
> Откройте файл `app/assets/javascripts/admin/routes/admin-route-map.js.es6`.

Я думаю, что сейчас этот файл находится по адресу [https://github.com/discourse/discourse/blob/main/app/assets/javascripts/admin/addon/routes/admin-route-map.js](https://github.com/discourse/discourse/blob/main/app/assets/javascripts/admin/addon/routes/admin-route-map.js), и он был переименован в 2020 году.

---

<div class="post-metadata">

### Author: ![Chris\_Dawson](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/chris_dawson/32/184486_2.png) [@Chris\_Dawson](https://meta.discourse.org/u/Chris_Dawson)
#### Post date: [04.Декабрь.2024 18:14:19 UTC](https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359/11 "2024-12-04T18:14:19Z")

</div>

Хорошо, я попытался следовать инструкциям. Я попробовал использовать команду `rake plugin:create[pocketbase-auth]` **внутри контейнера** (поскольку rake, вероятно, недоступен снаружи, верно). Но это привело к полному провалу, так как у меня не настроен git внутри контейнера.

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

**РЕДАКТИРОВАНИЕ** : Я невнимательно прочитал, и действительно, это требует настройки для разработки, что четко указано в самом начале. Я займусь этим и вернусь к этому позже.

~~Я подозреваю, что эти трудности возникают потому, что обычно плагины разрабатываются в среде «dev» Discourse, а не внутри docker-контейнера, который я запускаю. Это нормально, но я бы хотел, чтобы руководства для разработчиков плагинов начинались именно с этого предположения и инструкций о том, как работать таким образом. Рекомендуемый способ запуска Discourse — использование docker (что мне нравится), но, на мой взгляд, существует разрыв между тем, как запускать всё внутри docker, и тем, как выполнять разработку согласно документации.~~

```plaintext

# rake plugin:create[pocketbase-auth]
Клонирование 'https://github.com/discourse/discourse-plugin-skeleton' в '/var/www/discourse/plugins/pocketbase-auth'...
Инициализация репозитория git...
hint: Использование 'master' в качестве имени для начальной ветки. Это имя ветки по умолчанию
hint: может быть изменено. Чтобы настроить имя начальной ветки, используемое во всех
hint: ваших новых репозиториях (чтобы подавить это предупреждение), выполните:
hint: 
hint: git config --global init.defaultBranch <name>
hint: 
hint: Имена, которые обычно выбирают вместо 'master', — это 'main', 'trunk' и
hint: 'development'. Недавно созданную ветку можно переименовать с помощью команды:
hint: 
hint: git branch -m <name>
Инициализирован пустой репозиторий Git в /var/www/discourse/plugins/pocketbase-auth/.git/
Идентичность автора неизвестна

*** Пожалуйста, укажите, кто вы.

Выполните:

  git config --global user.email "you@example.com"
  git config --global user.name "Ваше Имя"

чтобы установить идентификатор по умолчанию для вашей учетной записи.
Пропустите параметр --global, чтобы установить идентификатор только для этого репозитория.

fatal: не удалось автоматически определить адрес электронной почты (получено 'discourse@community-public-do-vm-app.(none)')
rake aborted!
Команда завершилась с ошибкой 128: git
/var/www/discourse/lib/tasks/plugin.rake:356:in `system'
/var/www/discourse/lib/tasks/plugin.rake:356:in `block (2 levels) in <main>'
/var/www/discourse/lib/tasks/plugin.rake:346:in `chdir'
/var/www/discourse/lib/tasks/plugin.rake:346:in `block in <main>'
/usr/local/bin/bundle:25:in `load'
/usr/local/bin/bundle:25:in `<main>'
Задачи: TOP => plugin:create
(Полный трассировочный отчет можно получить, запустив задачу с флагом --trace)

```

---

<div class="post-metadata">

### Author: ![Chris\_Dawson](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/chris_dawson/32/184486_2.png) [@Chris\_Dawson](https://meta.discourse.org/u/Chris_Dawson)
#### Post date: [06.Декабрь.2024 01:23:23 UTC](https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359/12 "2024-12-06T01:23:23Z")

</div>

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

---

<div class="post-metadata">

### Author: ![system](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/system/32/443519_2.png) [@system](https://meta.discourse.org/u/system)
#### Post date: [05.Январь.2025 01:24:15 UTC](https://meta.discourse.org/t/triggering-account-creation-login-on-external-service-when-a-user-logs-in-on-discourse/340359/13 "2025-01-05T01:24:15Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
