Hinzufügen einer neuen 'verwalteten' Authentifizierungsmethode zu Discourse

Weiter von Future Social Authentication Improvements

Wir sind nun dabei, alle Informationen zu „verknüpften Konten“ in einer einzigen Datenbanktabelle zusammenzuführen. Dies wird dazu beitragen, duplizierte Logik erheblich zu reduzieren und zukünftig eine schnellere Entwicklung zu ermöglichen. Zum Beispiel reduzierte die Migration unserer Kern-Twitter-Logik auf das neue System die Anzahl der Codezeilen von 136 auf nur 24 :tada:.

Dieser Beitrag ist nicht als Schritt-für-Schritt-Anleitung zum Hinzufügen eines neuen Authentifizierungsanbieters gedacht, sondern soll einen Überblick geben und bei Bedarf auf den relevanten Quellcode verweisen.

Einen Authentifikator implementieren

Jeder Authentifikator muss eine Unterklasse von Auth::Authenticator implementieren. Um die neue gemeinsame Logik zu nutzen, kann der Authentifikator stattdessen von Auth::ManagedAuthenticator erben. Ein Beispiel für eine minimalistische Implementierung findet sich im Kern-Facebook-Authentifikator:

name, enabled? und register_middleware müssen von implementierenden Klassen überschrieben werden.

:information_source: Nebenbemerkung: Für die Kompatibilität mit mehreren Sites ist es wichtig, dass alle sitespezifischen Informationen Omniauth in einem setup-Lambda übergeben werden, anstatt zum Zeitpunkt der Definition festgelegt zu werden. Siehe alle Kern-Authentifikatoren für Beispiele hierfür.

Die gesamte Logik zum Verknüpfen externer Konten mit Discourse-Konten wird von Auth::ManagedAuthenticator gehandhabt. Dies setzt voraus, dass der Omniauth-Anbieter Daten im Format liefert, das in dessen Dokumentation definiert ist. Wenn eine Manipulation dieser Daten erforderlich ist, können Authentifikatoren die Methode after_authenticate überschreiben und das auth_token nach Bedarf bearbeiten. Zum Beispiel entfernt der Kern-Twitter-Authentifikator alle extra-Informationen aus dem Token:

Daten werden in der Datenbanktabelle user_associated_accounts gespeichert. provider_uid, info, credentials und extra werden direkt aus den von Omniauth zurückgegebenen Daten übernommen.

Sobald eine Authenticator-Klasse definiert wurde, muss sie registriert werden. Dies muss früh im Lebenszyklus der Anwendung geschehen und darf nicht innerhalb der after_initialize-Methode eines Plugins erfolgen. Die minimale Registrierung kann einfach eine Referenz auf den Authentifikator enthalten. In einem Plugin kann die Registrierung mit der Funktion auth_provider erfolgen. Zum Beispiel:

auth_provider authenticator: OpenIDConnectAuthenticator.new()

Im Kern erfolgt die Registrierung in discourse.rb. Eine vollständige Liste der möglichen AuthProvider-Optionen finden Sie hier. Inhaltlicher Text kann mit diesen Optionen definiert werden, aber es ist besser, lokalisierbare Zeichenketten in client.en.yml gemäß den Standard-Schlüsseln bereitzustellen. Zum Beispiel:

Zusätzliche ManagedAuthenticator-Hinweise von @fantasticfears

ManagedAuthenticator im Detail

Möglicherweise müssen Sie an etwas Besonderem für die Authentifizierung arbeiten. Und Sie möchten mehr über ManagedAuthenticator erfahren. Im Grunde hat es mehrere Operationen, Optionen und steuert, wie die Daten verwendet werden.

Discourse verwaltet Benutzerinformationen mit zwei Controllern. Users::OmniauthCallbacksController verwaltet die Nutzlast, sobald die OAuth2-Authentifizierung abgeschlossen ist. after_authenticate wird hier aufgerufen. can_connect_existing_user? wird hier ebenfalls verwendet.
Es gibt einige private Methoden, die Sie lesen können, um zu verstehen, wie verschiedene Datenfelder funktionieren.

if authenticator.can_connect_existing_user? && current_user
  @auth_result = authenticator.after_authenticate(auth, existing_account: current_user)
else
  @auth_result = authenticator.after_authenticate(auth)
end

UsersController hat revoke_account, das can_revoke? und revoke verwendet. Aber damit die revoke-Methode remote funktioniert, müssen Sie Ihre eigene Implementierung erstellen.

UserAuthenticator ist eine Service-Klasse, die bei der Authentifizierung (Überprüfung der E-Mail-Bestätigung oder des OAuth2-Pfads) von Benutzern hilft. after_create_account wird hier aufgerufen.

Die Kernlogik bleibt bei after_authenticate mit der Datenklasse Auth::Result. Wir folgen hier der Datenstruktur. extra_data wird an after_create_account übergeben, um zugehörige Datensätze zu erstellen.

result.extra_data = {
  provider: auth_token[:provider],
  uid: auth_token[:uid],
  info: auth_token[:info],
  extra: auth_token[:extra],
  credentials: auth_token[:credentials]
}

Es wird versuchen, ein bestehendes Konto abzugleichen und zu verknüpfen.
Sie fragen sich vielleicht, warum eine automatische Kontoerstellung möglich ist, es aber kein User.create gibt. Dies geschieht in UsersController#create.

authentication = UserAuthenticator.new(user, session)

Der Benutzer ist eine frische Instanz, die mit Sitzungsdaten gefüllt wird, die vom Authentifizierungsanbieter vorbereitet wurden. Glauben Sie mir, es ist reine Magie.


Migration zum neuen System

Um einen nahtlosen Übergang zum neuen System zu gewährleisten, sollten Daten aus dem alten Speicherort migriert werden. Für Kern-Authentifizierungsanbieter können dies dedizierte Tabellen sein. Für Plugins können dies plugin_store_rows oder oauth2_user_infos sein. Die minimal erforderlichen Daten in einem user_associated_accounts-Eintrag sind provider_name, provider_uid und user_id. Ein Beispiel für eine Migration finden Sie hier:

Sobald das ManagedAuthenticator-System mit v2.2.0 auf dem stabilen Zweig veröffentlicht wurde, beginnen wir mit der Migration der offiziellen Authentifizierungs-Plugins. Zu diesem Zeitpunkt wird hier ein Beispiel für eine plugin_store_row-Migration hinzugefügt.


Dieses Dokument wird versioniert – schlagen Sie Änderungen auf github vor.

23 „Gefällt mir“

@david Die hier geleistete Arbeit ist super cool. Ich schätze sie sehr. Ich hatte auch die Gelegenheit, mit GitHub - discourse/discourse-development-auth: A discourse plugin which adds a fake authentication provider. For development purposes only. zu spielen, was sehr praktisch ist.

Nur eine Einschränkung: Die Funktion funktioniert nicht gut (öffnet nicht das Anmelde-Popup) mit dem Ember CLI lokal. Ich habe mir den Kopf zerbrochen, als ich ein Plugin zum Hinzufügen eines Authentifizierungsanbieters schrieb, und plötzlich fiel mir ein, NO_EMBER_CLI=1 zu verwenden, und alles funktionierte.

7 „Gefällt mir“

Ich würde gerne erfahren, ob die Implementierung eines Authentifikators der richtige Weg ist, um

Verstehe ich richtig, dass alle registrierten Authentifikatoren früh in der App aufgerufen werden, sodass ich dort testen könnte, ob ein Benutzername und ein Hinweis zur E-Mail-Authentifizierung in der URL enthalten sind und als Antwort ein Formular “Senden Sie mir einen Login-Link” rendern könnte?