Auslösung der Kontoerstellung/-anmeldung bei externem Dienst beim Login eines Nutzers in Discourse

Ich habe ein JS-Plugin auf meiner Discourse-Seite. Dieses JS verwendet Pocketbase zum Speichern von Daten. Pocketbase ist ein „serverloser“ SQLite-Dienst, der es mir ermöglicht, Dateien und JSON-Blobs zu speichern. Pocketbase verfügt über ein hervorragendes JWT-basiertes Authentifizierungssystem, sodass ein Benutzer, sobald er ein Authentifizierungstoken hat, Daten sicher direkt in Pocketbase speichern kann (ohne über einen Backend-Server usw. zu leiten) direkt von JS auf der Client-Seite.

Ich versuche, einen Weg zu finden, um auf der Pocketbase-Seite automatisch einen Login zu generieren, wenn sich ein Benutzer bei Discourse anmeldet.

Mein erster Versuch war, das JS-Plugin einen Aufruf an einen Pfad auf dem Server machen zu lassen und Discourse-Authentifizierungs-Cookies einzuschließen. Dann kann Nginx diesen Pfad an einen Dienst („Login-Proxy“) weiterleiten, der die Authentifizierungs-Cookies dekodieren und den Benutzer identifizieren kann. Mit verifizierten Benutzerinformationen kann der Login-Proxy dann einen speziellen Aufruf an Pocketbase machen, ein Pocketbase-Authentifizierungs-Cookie erhalten und dieses Pocketbase-Authentifizierungs-Cookie zurücksenden, das das clientseitige JS für nachfolgende Anfragen direkt an Pocketbase verwenden kann.

Ich habe jedoch Schwierigkeiten, die Discourse-Authentifizierungs-Cookies zu dekodieren (ich stelle mir vor, dass _t das richtige Cookie ist, aber ich sehe keine einfache Möglichkeit, an die Benutzerdetails zu gelangen, und mache mir Sorgen, dass sich die Struktur sowieso ändern könnte).

Gibt es eine intelligentere Möglichkeit, sicher auf die E-Mail-Adresse eines angemeldeten Benutzers zuzugreifen? Ich glaube nicht, dass dies auf der Client-Seite geschehen sollte, und ziehe es aus offensichtlichen Sicherheitsgründen vor, dies auf der Serverseite zu tun.

Ich kenne Ihren Stack oder Anwendungsfall nicht gut genug, aber ich glaube, ich habe ein ähnliches Problem schon einmal gelöst, und einige Ideen könnten für Sie nützlich sein.

Ich habe eine Next.js-App, bei der die Client-Seite einen gültigen JWT benötigt, um Aufrufe an meine Backend-API zu tätigen, wenn eine Discourse-Sitzung vorhanden ist.

Dafür nutze ich Discourse als meinen Identitätsanbieter über DiscourseConnect.

In meinem Fall mache ich das mit einem einzigen clientseitigen fetch-Aufruf mit { credentials: "include" }, was nur funktioniert, weil ich alles mit einer einzigen Domain eingerichtet habe und der fetch-Aufruf transparent Weiterleitungen folgt.

Mein Client ruft einen benutzerdefinierten /auth/token ab, der auf die Existenz von _t prüft (nur um sonst eine sinnlose Weiterleitung zu vermeiden) und eine Weiterleitung zu einer gesicherten /session/sso_provider-URL zurückgibt, die gemäß der Dokumentation im verlinkten Thema mit nonce/sso/sig und einer return_sso_url erstellt wird, die auf einen benutzerdefinierten /auth/callback verweist. Dieser extrahiert die von Discourse gesendeten Daten, erstellt und gibt einen JWT-Token zurück, den mein Client von diesem Moment an verwenden kann.

Ich glaube, Ihr Anwendungsfall kann auf ähnliche Weise gelöst werden.

Super, das werde ich ausprobieren. Vielen Dank.

1 „Gefällt mir“

@renato Ein paar Fragen, wenn es Ihnen nichts ausmacht.

Bedeutet dies, dass ich die gesamte Benutzerverwaltung und Authentifizierung an die Connect-App delegieren muss? Ich bin mir nicht sicher, ob ich das tun möchte.

Wenn Connect nur eine zusätzliche Ebene über der bestehenden Discourse-Benutzerauthentifizierung darstellt, dann scheint das machbar zu sein.

Aber als ich anfing, über Discourse Connect zu lesen, befürchte ich nun, dass ich eine völlig neue App zur Verwaltung der Benutzerauthentifizierung erstellen und warten muss, und ich weiß im Moment nicht wirklich, wie ich das eingrenzen soll.

Meine Antwort geht davon aus, dass Sie Discourse als Ihren Identitätsanbieter verwenden (mit seinen Anmelde-/Registrierungs-UIs) und dies auch beibehalten möchten.

Auf der Discourse-Seite ist die Aktivierung so einfach wie

Sie haben jedoch erwähnt, dass Sie ein Plugin erstellen.

Wenn Sie „einen Pfad auf dem Server“ in einer neuen Controller-Aktion in einem Discourse-Plugin erstellen, können Sie den Benutzer aus der Sitzung abrufen, Drittanbieter aufrufen und den JWT an Ihren Client zurückgeben.

Vielen Dank für die Diskussion.

Ich habe den Link gelesen: Use Discourse as an identity provider (SSO, DiscourseConnect) - #8 by reverend_paco

Aber ich denke, das ist, wenn ich meine Benutzer auf eine sekundäre Website senden und die Authentifizierung dort verwalten möchte.

In meinem Fall habe ich ein Stück JS, das auf der Discourse-Website ausgeführt wird. Und ich möchte, dass dieses JS einen Pfad auf demselben Server aufruft und einen Cookie für Pocketbase zurückbekommt.

Ich verwende tatsächlich einen Nginx-Proxy vor Discourse und habe gerade eine spezielle Route /pb/auth (zum Beispiel) hinzugefügt. Wenn mein JS diese Route aufruft, akzeptiert ein Backend-Proxy-Server (der nicht in Discourse ist) diese Verbindung und versucht, den _t-Sitzungs-Cookie zu dekodieren.

Ich habe es auf diese Weise gemacht, weil es einfacher zu sein scheint, als ein Discourse-Plugin hinzuzufügen (ich bin damit und mit dem Dev-Setup usw. weniger vertraut). Wenn es sich um eine einfache Angelegenheit handelt, einen Cookie mit Base64 und SHA-Hashing zu dekodieren, dachte ich, das würde mir eine gesicherte Nutzlast liefern, die mir sagt, wer der Benutzer ist.

Aber wenn Sie denken, dass es eine einfache Möglichkeit gibt, ein Plugin zu erstellen, das diese Route zu Discourse hinzufügt, bin ich sehr daran interessiert, dies auszuprobieren. Es scheint der richtige Weg für die Zukunft zu sein. Aber ich bin ein alter Perl-Programmierer, also bevorzuge ich den faulen Weg, und meine Nginx-Route schien fauler zu sein. :slight_smile:

Ganz im Gegenteil: Wenn Sie eine separate „Website“ (in diesem Beispiel PocketBase) haben und Discourse als Quelle der Wahrheit für die Benutzer-/Authentifizierungsverwaltung verwenden möchten – wie in meinem Next.js-Beispiel.

Ich würde mit dem Lesen beginnen

Großartig, ich freue mich, das zu lesen. Ich habe angefangen, mir das Beispiel-Skeleton-Plugin anzusehen (GitHub - discourse/discourse-plugin-skeleton: Template for Discourse plugins) und war ein wenig enttäuscht, da es überhaupt keine Dokumentation gibt.

Auf den ersten Blick muss ich fragen: Fügt dieses Tutorial Code zur Basis-Rails-Installation für Discourse hinzu? Das ist für mich in Ordnung, wenn das der offizielle Weg ist, aber es fühlt sich gefährlich an und wäre besser als Plugin gehandhabt (das leicht deinstalliert und deaktiviert werden kann). Muss ich mir auch keine Sorgen machen, dass dies Upgrades von Discourse unterbricht, wenn mein Code nicht im GitHub-Repository ist?

Zum Beispiel hier:

Bedeutet das, dass ich wirklich in den Container springe (./launcher enter app) und dann /var/www/app/controllers/snack_controller.rb bearbeite?

Und ich habe tatsächlich genau diese Anweisungen befolgt. Ich kann die Route /admin/snack.json nicht zum Laufen bringen, selbst nachdem ich ./launcher rebuild app ausgeführt habe.

Dieses Tutorial scheint ungefähr acht Jahre alt zu sein. Ist das wirklich der richtige Weg, Dinge zu tun?

Es gibt andere Anleitungen, das Datum oben ist die Erstellung des Themas, aber alles in Documentation sollte aktuell sein – lassen Sie uns wissen, wenn Sie Probleme finden.

Sie können den Code des vorhandenen Plugin als Referenz überprüfen.

Nein:

Ich habe den Eindruck, dass dies noch nicht aktualisiert wurde

Ich denke, die Datei ist jetzt https://github.com/discourse/discourse/blob/main/app/assets/javascripts/admin/addon/routes/admin-route-map.js
und sie wurde 2020 umbenannt.

2 „Gefällt mir“

Okay, ich habe versucht, die Anweisungen zu befolgen. Ich habe versucht, diesen Befehl rake plugin:create[pocketbase-auth] innerhalb des Containers zu verwenden (da Rake außerhalb nicht verfügbar wäre, oder?). Aber es schlägt fehl, weil ich kein Git im Container eingerichtet habe.

Wie ich weiter gelesen habe, scheint es, dass ich das Git-Repository des Plugins angeben muss, um ein Plugin im Admin-Bereich anzuzeigen. Aber ich bin noch nicht an dem Punkt, an dem ich eine funktionierende Version des Plugins habe, und ich habe die Dinge nicht in einem Git-Repository.

EDIT: Ich habe nicht sorgfältig gelesen, und tatsächlich erfordert dies eine Entwicklungsumgebung, was von Anfang an klar angegeben ist. Ich werde daran arbeiten und komme darauf zurück.

Ich vermute, dass diese Schwierigkeiten daher rühren, dass Plugins normalerweise gegen eine “Entwicklungs”-Umgebung von Discourse entwickelt werden und nicht innerhalb des Docker-Containers, den ich gerade ausführe. Das ist in Ordnung, aber ich wünschte, die Plugin-Entwickler-Anleitungen würden von dieser Annahme ausgehen und Anleitungen geben, wie man auf diese Weise arbeitet. Die empfohlene Methode, Discourse auszuführen, ist die Verwendung von Docker (was ich liebe), aber ich denke, es gibt eine Lücke zwischen der Art und Weise, wie man Dinge innerhalb von Docker ausführt, und der Entwicklung innerhalb der Dokumentation.

# rake plugin:create[pocketbase-auth]
Cloning 'https://github.com/discourse/discourse-plugin-skeleton' to '/var/www/discourse/plugins/pocketbase-auth'...
Initializing git repository...
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint: 	git config --global init.defaultBranch <name>
hint: 
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint: 
hint: 	git branch -m <name>
Initialized empty Git repository in /var/www/discourse/plugins/pocketbase-auth/.git/
Author identity unknown

*** Please tell me who you are.

Run

  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.
fatal: unable to auto-detect email address (got 'discourse@community-public-do-vm-app.(none)')
rake aborted!
Command failed with exit 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>'
Tasks: TOP => plugin:create
(See full trace by running task with --trace)

Ein Update: Ich habe Ihren Rat befolgt und ein Plugin entwickelt. Es funktioniert hervorragend und tut genau das, was ich brauche. Vielen Dank für Ihre Hilfe.

1 „Gefällt mir“

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