Kann ich die Discourse API verwenden, um Benutzer in einer anderen App zu authentifizieren?

Ich habe ein funktionierendes Discourse-Forum, in dem Benutzer einen lokalen Login (Benutzername + Passwort) erstellen können.

Ich möchte deren Login und Passwort in einer anderen Anwendung wiederverwenden. Mit anderen Worten: Benutzer würden ihren Benutzernamen und ihr Passwort in der anderen Anwendung eingeben, und die Anwendung sollte überprüfen können, ob dies ein gültiger Login für das Forum ist.

Ich habe die Dokumentation für die Discourse API gelesen. Vieles ist möglich, einschließlich der Festlegung von Benutzername und Passwort für einen bestimmten Benutzer, aber ich habe keinen API-Endpunkt gefunden, um einen vorhandenen Benutzernamen und ein Passwort gegen die Liste der Forum-Benutzer zu validieren.

Ich gehe davon aus, dass ein solcher API-Endpunkt existieren muss, da das Forum dies tun können muss, um einen Benutzer über die Weboberfläche anzumelden.

Was ist der API-Endpunkt, um einen Benutzernamen und ein Passwort zu überprüfen, um sich im Forum anzumelden?

3 „Gefällt mir“

Direkter gesagt, Sie könnten DiscourseConnect als Mechanismus zur Benutzervalidierung verwenden oder vor Ihrer Anwendung den discourse-auth-proxy einsetzen.

Dies sind vorgeschlagene Methoden zur Authentifizierung von Benutzern, anstatt Anmeldedaten direkt zu verarbeiten. Es bedeutet auch, dass Sie sich nicht mit 2FA-Details befassen müssen.

5 „Gefällt mir“

Tatsächlich ist meine „andere Anwendung“ eine Desktop-App und keine Web-App. Ich glaube nicht, dass discourse-auth-proxy in diesem Fall funktionieren wird.

Auf der DiscourseConnect-Seite steht eine der ersten Aussagen:

Viele Websites, die eine Integration mit einer Discourse-Site wünschen, möchten die gesamte Benutzerregistrierung auf einer separaten Website beibehalten. In einer solchen Konfiguration sollten alle Anmeldevorgänge an diese andere Website ausgelagert werden.

Dies ist genau das Gegenteil von dem, was ich tun möchte: Ich möchte alle Anmeldevorgänge an Discourse auslagern. Gibt es eine Möglichkeit, DiscourseConnect dafür zu verwenden?

1 „Gefällt mir“

Ja, absolut.

Das knifflige daran ist, dass es ein gemeinsames Geheimnis zwischen dem Anbieter (Discourse) und dem Konsumenten (Ihre App) gibt. Wenn Sie Ihre App verteilen, haben Benutzer Zugriff auf alle Geheimnisse darin.

Das Platzieren eines Auth-Proxys vor einem benutzerdefinierten minimalen Webdienst, der Ihrer App einen signierten Token gibt, könnte gut funktionieren.

Ich bin sicher, es gibt andere Wege, an die ich gerade nicht denke.

Beziehen Sie sich auf den API-Schlüssel? Es scheint möglich zu sein, einen „granularen“ API-Schlüssel zu erstellen, der nur Zugriff auf bestimmte API-Endpunkte hat. Es ist mir immer noch nicht klar, welche Endpunkte erforderlich wären, wenn ich diesen Ansatz verwende. Wissen Sie das?

Ja, ein minimaler Webdienst mit Auth-Proxy könnte eine gute Lösung sein; Ich muss ein wenig experimentieren, um das herauszufinden.

Nicht genau – es wäre der Wert discourse connect provider secrets für die Anwendung, der in Verbindung mit enable discourse connect provider gesetzt werden müsste.

Weitere Informationen hierzu finden Sie hier: Use Discourse as an identity provider (SSO, DiscourseConnect)

1 „Gefällt mir“

Für eine Desktop-App ist diese Methode, die in diesem React Native-Beispiel verwendet wird, vielleicht hilfreich für Sie:

https://github.com/pmusaraj/discourse-mobile-single-site-app/blob/main/js/Authenticate.js

1 „Gefällt mir“

Wenn ich das richtig verstehe, würde diese Methode bedeuten, dass sich der Benutzer über einen Browser anmeldet. Das kann funktionieren, obwohl ich auf eine Methode gehofft hatte, bei der Benutzername und Passwort in unserer Desktop-Anwendung eingegeben werden können, ohne einen Browser zu öffnen.

Ich verstehe, dass der von mir angedachte Ansatz TFA nicht unterstützt, es sei denn, ich implementiere ihn selbst, und dass er keine Anmeldungen über Drittanbieter (Google, Facebook, Discord, …) unterstützt.

Ich glaube nicht, dass dies explizit unterstützt wird, aber ich würde eine Sitzung auf die gleiche Weise erstellen, wie der Benutzer es im Webbrowser tun würde:


curl 'https://try.discourse.org/session' \
  -H 'sec-ch-ua: "Chromium";v="113", "Not-A.Brand";v="24"' \
  -H 'Discourse-Present: true' \
  -H 'DNT: 1' \
  -H 'X-CSRF-Token: …' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'User-Agent: …' \
  -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \
  -H 'Accept: */*' \
  -H 'Referer: https://try.discourse.org/' \
  -H 'X-Requested-With: XMLHttpRequest' \
  -H 'sec-ch-ua-platform: "Linux"' \
  --data-raw 'login=demouser&password=demopassword&second_factor_method=1&timezone=America%2FToronto' \
  --compressed

und diese Anmeldelogik in der App duplizieren.

Soweit ich das im Moment verstehe, sieht es so aus, als ob die im Beispiel für Reactive Native verwendete Methode auf unsere Desktop-Anwendung (die in Python geschrieben ist) übertragen werden kann.

Der verwendete API-Zugangspunkt scheint <site>/session zu sein und erfordert einen Benutzernamen, ein Passwort und ein CSRF-Token. Das CSRF-Token kann von <site>/session/csrf bezogen werden.

Das kommt dem, was ich gesucht habe, sehr nahe. Ich denke, ich werde das versuchen und melde mich, wenn es bei mir funktioniert.

Ist der API-Zugangspunkt <site>/session irgendwo dokumentiert?

1 „Gefällt mir“

Der beste Weg, um das zu erreichen, was Sie in einer Desktop-App möchten, ist die Verwendung von User API Keys.

Sie benötigen zwar eine Weboberfläche, entweder in der App oder durch Öffnen des Browsers, aber wenn Sie Ihre App zu einem Handler für das von den mobilen Apps verwendete Protokoll machen, können Sie den Token auf diese Weise leicht erhalten und müssen den Browser nur wieder verwenden, wenn der Token abläuft oder sie ein anderes Gerät verwenden.

Meine persönliche Erfahrung damit ist, dass die Verwendung von User API Keys eine viel sicherere und einfachere Option ist, als zu versuchen, die Session-Endpunkte zu verwenden. :slight_smile:

2 „Gefällt mir“

Hier sind 20 Zeilen Python-Code, die ungefähr das Gleiche tun wie der von @renato erwähnte React Native-Code (außer dass keine Kompatibilität mit Discourse 2.5 besteht – das brauche ich nicht)

Es funktioniert gut, vorausgesetzt, Sie verwenden die grundlegende Anmeldung per Benutzername und Passwort. Ich werde mich trotzdem mit alternativen Methoden befassen, die die in der Discourse-Instanz konfigurierte Discourse SSO-Anmeldung verwenden.

import requests
import json

def discourse_authenticate(url, name, password):
    session = requests.Session()
    session.headers.update({'X-Requested-With': 'XMLHttpRequest'})
    r1 = session.get(url + '/session/csrf')
    csrf_token = json.loads(r1.text).get('csrf')
    r2 = session.post(url + '/session',
        data={
            'login': name,
            'password': password,
            'authenticity_token': csrf_token,
        },
    )
    if r2.status_code != 200:
        return None
    return json.loads(r2.text)

Ich habe versucht, dies anzuwenden, aber es funktioniert nicht. Unten ist ein (vereinfachter) Python-Code, der eine URL für .../session/sso_provider generiert. Wenn ich es versuche, erhalte ich Login Error. Keine Ahnung, was das bedeutet.

import secrets
import base64
import urllib.parse
import hmac
import hashlib

forum_url = 'https://forum.embeetle.com'
target_url = 'https://embeetle.com/#account'
sso_secret = b'JCLSVcqbAnEPXz2p2xBY'

nonce = secrets.token_urlsafe()
payload = f'nonce={nonce}&return_sso_url={target_url}'
payload_base64 = base64.b64encode(payload.encode('utf-8')).decode()
payload_for_url = urllib.parse.quote(payload_base64)

payload_for_url = 'bm9uY2U9YklKeEU1WWw2OFhjSkJydGlwSU15UTRZeVlMeWd6ZzQyUU9mOFo0SWF5QSZyZXR1cm5fc3NvX3VybD1odHRwczovL2VtYmVldGxlLmNvbS8jYWNjb3VudA%3D%3D'

signature = hmac.new(
    sso_secret, payload_for_url.encode('utf-8'), hashlib.sha256
).hexdigest()

print(f'{forum_url}/session/sso_provider?sso={payload_for_url}&sig={signature}')

Zum Beispiel könnte ein Durchlauf die URL im unten stehenden curl-Befehl generieren:

johan@morla:~/sa> curl 'https://forum.embeetle.com/session/sso_provider?sso=bm9uY2U9YklKeEU1WWw2OFhjSkJydGlwSU15UTRZeVlMeWd6ZzQyUU9mOFo0SWF5QSZyZXR1cm5fc3NvX3VybD1odHRwczovL2VtYmVldGxlLmNvbS8jYWNjb3VudA%3D%3D&sig=a392ebb81b93ba7411290fbd00240921ae053bbb82998830dda994c8a71853da'
Login Errorjohan@morla:~/sa> 

Als Administrator, aktiviere verbose discourse connect logging, probiere es aus, und überprüfe dann /logs auf deinem Forum, um detailliertere Fehler zu sehen, z.B. https://forum.embeetle.com/logs

Du wirst z.B. sehen:

Du musst die Nutzlast signieren, nicht die zitierte Nutzlast, z.B.:

signature = hmac.new(
  sso_secret, payload_base64.encode('utf-8'), hashlib.sha256
).hexdigest()

Und dann funktioniert es!

Übrigens, du musst dieses Geheimnis sofort widerrufen und ändern, da jeder, der es hat, sich in deine App einloggen kann, wie ich es gerade beim Testen getan habe.

3 „Gefällt mir“

Machen Sie sich keine Sorgen, die Seite, zu der ich weiterleite, ist eine öffentliche Seite, nur zum Testen.

Ich habe das Geheimnis trotzdem geändert.

2 „Gefällt mir“

Was sind Ihre Top 3 Open-Source-Vorschläge für diese spezielle Aufgabe?

Ich verwende Nginx, aber vielleicht könnte dies mit einer robusteren Entwicklung wie Keycloak verwaltet werden?

Mit auth-proxy meine ich Folgendes: GitHub - discourse/discourse-auth-proxy: An http proxy that uses the DiscourseConnect protocol to authenticate users

2 „Gefällt mir“

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