passport.js RESTful auth

155

Wie geht man mit der Authentifizierung (z. B. lokal und Facebook) über passport.js über eine RESTful-API anstatt über eine Weboberfläche um?

Besondere Bedenken bestehen darin, die Übergabe von Daten aus Rückrufen an eine RESTful-Antwort (JSON) im Vergleich zur Verwendung eines typischen res.send ({data: req.data}) zu behandeln und einen anfänglichen / login-Endpunkt einzurichten, der an Facebook umleitet (/ login kann nicht sein) Zugriff über AJAX, da es sich nicht um eine JSON-Antwort handelt - es handelt sich um eine Weiterleitung zu Facebook mit einem Rückruf).

Ich habe https://github.com/halrobertson/test-restify-passport-facebook gefunden , aber ich habe Probleme, es zu verstehen.

Wie speichert passport.js außerdem die Authentifizierungsdaten? Der Server (oder ist es ein Dienst?) Wird von MongoDB unterstützt, und ich würde erwarten, dass Anmeldeinformationen (Login & Salted Hash von pw) dort gespeichert werden, aber ich weiß nicht, ob passport.js über diese Art von Funktion verfügt.

Ryanrhee
quelle
Da Sie Node noch nicht kennen, starten Sie einfach und sehen Sie sich die Beispielanwendung für an passport-facebook. Nachdem Sie dies zum Laufen gebracht haben, müssen Sie zunächst verstehen, wie Passport funktioniert und wie Anmeldeinformationen gespeichert werden. Einhaken es Restify oben ( siehe hier , um eine aktualisierte Version des von Ihnen erwähnen) würde einer der letzten Schritte sein (oder Sie können die REST - Schnittstelle in Express implementieren).
Robertklep

Antworten:

312

Hier werden viele Fragen gestellt, und obwohl die Fragen im Kontext von Node und passport.js gestellt werden, geht es bei den eigentlichen Fragen eher um den Workflow als darum, wie dies mit einer bestimmten Technologie geschehen soll.

Verwenden wir das @ Keith-Beispiel-Setup, das für zusätzliche Sicherheit etwas modifiziert wurde:

  • Der Webserver von https://example.comdient einer einseitigen Javascript-Client-App
  • RESTful Web Service at https://example.com/apibietet Serverunterstützung für Rich Client-Apps
  • Server in Node und passport.js implementiert.
  • Der Server verfügt über eine Datenbank (beliebiger Art) mit einer "Benutzer" -Tabelle.
  • Benutzername / Passwort und Facebook Connect werden als Authentifizierungsoptionen angeboten
  • Rich Client macht REST-Anfragen in https://example.com/api
  • Möglicherweise gibt es andere Clients (z. B. Telefon-Apps), die den Webdienst unter verwenden, https://example.com/apiaber nichts über den Webserver unter wissen https://example.com.

Beachten Sie, dass ich sicheres HTTP verwende. Dies ist meiner Meinung nach ein Muss für jeden Dienst, der offen verfügbar ist, da vertrauliche Informationen wie Kennwörter und Autorisierungstoken zwischen Client und Server übertragen werden.

Authentifizierung mit Benutzername / Passwort

Schauen wir uns zunächst an, wie die einfache alte Authentifizierung funktioniert.

  • Der Benutzer stellt eine Verbindung zu her https://example.com
  • Der Server stellt eine umfangreiche Javascript-Anwendung bereit, die die Startseite rendert. Irgendwo auf der Seite befindet sich ein Anmeldeformular.
  • Viele der Abschnitte dieser einseitigen App wurden nicht mit Daten gefüllt, da der Benutzer nicht angemeldet ist. Alle diese Abschnitte verfügen über einen Ereignis-Listener für ein "Anmelde" -Ereignis. All dies ist clientseitiges Zeug, der Server kennt diese Ereignisse nicht.
  • Der Benutzer gibt sein Login und Passwort ein und klickt auf die Schaltfläche "Senden". Dadurch wird ein Javascript-Handler ausgelöst, der den Benutzernamen und das Passwort in clientseitigen Variablen aufzeichnet. Dann löst dieser Handler das "Login" -Ereignis aus. Auch dies ist alles eine clientseitige Aktion. Anmeldeinformationen wurden noch nicht an den Server gesendet .
  • Die Listener des "Login" -Ereignisses werden aufgerufen. Jeder von diesen muss nun eine oder mehrere Anforderungen an die RESTful-API unter senden https://example.com/api, um die benutzerspezifischen Daten zu erhalten, die auf der Seite gerendert werden sollen. Jede einzelne Anfrage sie an den Webdienst sendet den Benutzernamen und das Passwort enthalten, möglicherweise in Form von HTTP - Basic - Authentifizierung, da der Dienst wird RESTful nicht erlaubt ist , Client - Zustand von einer Anfrage zur nächsten beizubehalten. Da sich der Webdienst auf sicherem HTTP befindet, wird das Kennwort während der Übertragung sicher verschlüsselt.
  • Der Webdienst at https://example.com/apiempfängt eine Reihe von Einzelanforderungen mit jeweils Authentifizierungsinformationen. Der Benutzername und das Kennwort in jeder Anforderung werden mit der Benutzerdatenbank verglichen. Wenn sie als korrekt befunden werden, wird die angeforderte Funktion ausgeführt und die Daten werden im JSON-Format an den Client zurückgegeben. Wenn Benutzername und Passwort nicht übereinstimmen, wird ein Fehler in Form eines 401-HTTP-Fehlercodes an den Client gesendet.
  • Anstatt Clients zu zwingen, bei jeder Anforderung einen Benutzernamen und ein Kennwort zu senden, können Sie in Ihrem RESTful-Dienst eine Funktion "get_access_token" verwenden, die den Benutzernamen und das Kennwort verwendet und mit einem Token antwortet, bei dem es sich um eine Art kryptografischen Hash handelt, der eindeutig ist und einen gewissen Ablauf hat Datum damit verbunden. Diese Token werden mit jedem Benutzer in der Datenbank gespeichert. Anschließend sendet der Client das Zugriffstoken in nachfolgenden Anforderungen. Das Zugriffstoken wird dann anstelle des Benutzernamens und des Kennworts anhand der Datenbank überprüft.
  • Nicht-Browser-Client-Anwendungen wie Telefon-Apps tun dasselbe wie oben. Sie fordern den Benutzer auf, seine Anmeldeinformationen einzugeben und diese dann (oder ein daraus generiertes Zugriffstoken) bei jeder Anforderung an den Webdienst zu senden.

Der wichtige Aspekt dieses Beispiels ist, dass RESTful-Webdienste bei jeder Anforderung eine Authentifizierung erfordern .

Eine zusätzliche Sicherheitsebene in diesem Szenario würde zusätzlich zur Benutzerauthentifizierung die Berechtigung für Clientanwendungen hinzufügen. Wenn beispielsweise der Webclient, iOS- und Android-Apps den Webdienst verwenden, möchte der Server möglicherweise wissen, welcher der drei Clients der Client einer bestimmten Anforderung ist, unabhängig davon, wer der authentifizierte Benutzer ist. Dadurch kann Ihr Webdienst bestimmte Funktionen auf bestimmte Clients beschränken. Hierfür können Sie API-Schlüssel und Geheimnisse verwenden. In dieser Antwort finden Sie einige Ideen dazu.

Facebook-Authentifizierung

Der obige Workflow funktioniert nicht für Facebook Connect, da die Anmeldung über Facebook einen Dritten hat, Facebook selbst. Für den Anmeldevorgang muss der Benutzer auf die Facebook-Website weitergeleitet werden, auf der Anmeldeinformationen außerhalb unserer Kontrolle eingegeben werden.

Mal sehen, wie sich die Dinge ändern:

  • Der Benutzer stellt eine Verbindung zu her https://example.com
  • Der Server stellt eine umfangreiche Javascript-Anwendung bereit, die die Startseite rendert. Irgendwo auf der Seite befindet sich ein Anmeldeformular mit der Schaltfläche "Mit Facebook anmelden".
  • Der Benutzer klickt auf die Schaltfläche "Mit Facebook anmelden". Dies ist nur ein Link, zu dem (zum Beispiel) umgeleitet wird https://example.com/auth/facebook.
  • Die https://example.com/auth/facebookRoute wird von passport.js verwaltet (siehe Dokumentation ).
  • Der Benutzer sieht lediglich, dass sich die Seite ändert und sich nun auf einer von Facebook gehosteten Seite befindet, auf der er sich anmelden und unsere Webanwendung autorisieren muss. Dies liegt völlig außerhalb unserer Kontrolle.
  • Der Benutzer meldet sich bei Facebook und gibt die Erlaubnis, unsere Anwendung, so dass Facebook nun zurück an die Callback - URL umleitet , dass wir in der passport.js Setup konfiguriert, die im Beispiel folgende Dokumentation isthttps://example.com/auth/facebook/callback
  • Der passport.js-Handler für die https://example.com/auth/facebook/callbackRoute ruft die Rückruffunktion auf, die das Facebook-Zugriffstoken und einige Benutzerinformationen von Facebook empfängt, einschließlich der E-Mail-Adresse des Benutzers.
  • Mit der E-Mail können wir den Benutzer in unserer Datenbank finden und das Facebook-Zugriffstoken damit speichern.
  • Das Letzte, was Sie im Facebook-Rückruf tun, ist, zur Rich-Client-Anwendung zurückzukehren. Diesmal müssen wir jedoch den Benutzernamen und das Zugriffstoken an den Client übergeben, damit dieser sie verwenden kann. Dies kann auf verschiedene Arten erfolgen. Beispielsweise können Javascript-Variablen über eine serverseitige Vorlagen-Engine zur Seite hinzugefügt werden, oder es kann ein Cookie mit diesen Informationen zurückgegeben werden. (Vielen Dank an @RyanKimber für den Hinweis auf die Sicherheitsprobleme bei der Übergabe dieser Daten an die URL, wie ich ursprünglich vorgeschlagen hatte).
  • Jetzt starten wir die Single-Page-App noch einmal, aber der Client hat den Benutzernamen und das Zugriffstoken.
  • Die Clientanwendung kann das "Anmelde" -Ereignis sofort auslösen und die verschiedenen Teile der Anwendung die Informationen anfordern lassen, die sie vom Webdienst benötigen.
  • Alle an gesendeten Anforderungen https://example.com/apienthalten das Facebook-Zugriffstoken zur Authentifizierung oder das eigene Zugriffstoken der Anwendung, das aus dem Facebook-Token über eine Funktion "get_access_token" in der REST-API generiert wird.
  • Die Nicht-Browser-Apps haben es hier etwas schwieriger, da OAuth einen Webbrowser zum Anmelden benötigt. Um sich von einer Telefon- oder Desktop-App aus anzumelden, müssen Sie einen Browser starten, um die Weiterleitung zu Facebook durchzuführen, und noch schlimmer, Sie benötigen eine Möglichkeit für den Browser, das Facebook-Zugriffstoken über einen Mechanismus an die Anwendung zurückzugeben.

Ich hoffe, dies beantwortet die meisten Fragen. Natürlich können Sie Facebook durch Twitter, Google oder einen anderen OAuth-basierten Authentifizierungsdienst ersetzen.

Es würde mich interessieren, ob jemand einen einfacheren Weg hat, damit umzugehen.

Miguel
quelle
5
Vielen Dank für Ihre ausführliche Antwort. Nur eine Frage: Das sagst du Every single request they send to the web service will include the username and passwordund doch sagst du you can have a "get_access_token" function in your RESTful service. Es scheint widersprüchlich zu sagen, dass REST zustandslos sein muss, aber das Speichern der Zugriffstoken auf der Serverseite ist in Ordnung, da das Speichern von Zugriffstoken bedeutet, dass der Server jetzt statusbehaftet ist. Ich würde mich über eine Klarstellung oder Begründung in diesem Zusammenhang freuen. Vielen Dank! :)
Ryanrhee
6
Wenn ich an die zustandslose Anforderung denke, denke ich an den Status für eine bestimmte Sitzung mit einem Client. Ich sehe das Speichern von Daten, die einem Benutzer in einer Datenbank zugeordnet sind, wie ein Kennwort oder ein Zugriffstoken, nicht als "Status", zumindest nicht als "Sitzungsstatus". Ihr Server muss natürlich wissen, wer die Benutzer sind und welche Kennwörter sie haben. Dies sind jedoch Anwendungsdaten, die keiner Sitzung zugeordnet sind. Trotzdem verwenden viele Leute Cookies in angeblich RESTful-Diensten. Wie streng Sie sich also an die REST-Spezifikation halten möchten, hängt wirklich von jedem Implementierer ab.
Miguel
1
@Dexter: Im herkömmlichen Anmeldefall gibt ein Benutzer Benutzername und Passwort in ein Formular ein. Wenn er auf die Schaltfläche Senden klickt, werden diese Informationen auf einem Webserver veröffentlicht. In diesem Fall füllt der Benutzer das Formular nicht aus. Wenn er auf Senden eines Javascript-Handlers (ein onClick-Ereignis in der Schaltfläche "Senden") klickt, werden die Daten erfasst und im Clientkontext gespeichert. Ich habe kein Beispiel bereit zu zeigen, aber achten Sie auf einen zweiten Teil dieses Tutorials in meinem Blog, in dem ich zeigen werde, wie es gemacht wird: blog.miguelgrinberg.com/post/…
Miguel
2
Dies ist eine sehr gut durchdachte Beschreibung, enthält jedoch ein wichtiges Versehen oder einen wichtigen Fehler. Bei der Bearbeitung des Facebook-Logins (oder von Github, Twitter usw.) wird es sehr bevorzugt, das Token in einem Cookie an den Client zurückzugeben und das Cookie dann auf der Clientseite zu löschen, sobald es erkannt wird. Wenn Sie das Token als Teil der URL-Zeichenfolge übergeben, wird diese URL zum Browserverlauf hinzugefügt und kann (wenn die Dinge nicht ordnungsgemäß behandelt werden) dazu führen, dass diese URL vom Browser angefordert wird. Entweder macht das Token sichtbar. Jeder, der dann die URL kopiert hat, kann diesen Benutzer in Ihrer Anwendung fälschen.
Ryan Kimber
1
@ Nathan: Die grundlegende Authentifizierung über https ist sicher, also ja, das ist ein akzeptabler Mechanismus.
Miguel
11

Ich schätze die Erklärung von @ Miguel mit dem vollständigen Ablauf in jedem Fall sehr, aber ich möchte einige zum Facebook-Authentifizierungsteil hinzufügen.

Facebook stellt ein Javascript-SDK zur Verfügung, mit dem Sie das Zugriffstoken direkt auf Client-Seite abrufen können. Dieses wird dann an den Server übergeben und zum weiteren Abrufen aller Benutzerinformationen von Facebook verwendet. Sie brauchen also grundsätzlich keine Umleitungen.

Darüber hinaus können Sie denselben API-Endpunkt auch für mobile Anwendungen verwenden. Verwenden Sie einfach das Android / iOS SDK für Facebook, holen Sie sich das Facebook access_token auf der Client-Seite und übergeben Sie es an den Server.

Die in Bezug auf stateless Natur wie erläuterte, wenn get_access_token verwendet wird , ein Token und an den Client zu erzeugen, wird dieses Token auch auf dem Server gespeichert. Es ist also so gut wie ein Sitzungstoken und ich glaube, das macht es zustandsbehaftet?

Nur meine 2 Cent ..

Madhur
quelle
1
Dies ist sehr wichtig, da Sie auf diese Weise eine einseitige Ajax-Authentifizierung über Facebook durchführen können. Token ist als Sitzungsauthentifizierung GLEICH sicher. Der einzige Unterschied besteht darin, dass ein Token an eine andere Domäne übergeben werden kann und die Sitzung nur in einer bestimmten Domäne verwendet wird. Wenn Sie expressjs und passport verwenden, können Sie eine API erstellen, die einen Status speichert, und die Sitzungsauthentifizierung verwenden.
Jperelli
Die Javascript-API ist großartig, wenn Sie den Benutzer authentifizieren möchten, um Aktionen gegen Facebook auszuführen, aber nutzlos, wenn Sie den Benutzer gegen Ihren Server / Ihre Datenbank validieren möchten, soweit ich das beurteilen kann.
James Westgate
4
Sie können auch die oben von Miguel beschriebene Methode verwenden, aber dann Ihr eigenes JWT-Token als Cookie ausgeben, wenn Sie den Client im Auth-Rückruf umleiten. Dies ermöglicht das Beste aus beiden Welten - Ihre einseitige Anwendung kann sich nur um eine Art der Authentifizierung (JWT) kümmern, während das gleiche Sicherheitsniveau beibehalten wird und die Flexibilität geboten wird, alle sozialen Anmeldungen zu unterstützen, ohne diese verwenden zu müssen spezifische JavaScript-APIs für jedes soziale Netzwerk (Facebook, Twitter, LinkedIn, Google usw.). Außerdem können Sie die AJAX-Unterstützung für Benutzername / Passwort und REST-Zugriff beibehalten.
Ryan Kimber
Das Facebook Javascript SDK funktioniert derzeit nicht mit Chrome iOS. Vielleicht ein Problem für einige.
Demisx
@ RyanKimber können Sie ein kleines Git-Repo oder ähnliches schreiben, wo dies als Beispiel gemacht wird.
Ich
3

Hier ist ein großartiger Artikel, mit dem ich mich authentifizieren kann:

  • Facebook
  • Twitter
  • Google
  • Lokale Auth

Einfache Knotenauthentifizierung: Setup und lokal

myusuf
quelle
Ihr Link führt nicht zu einem Artikel, sondern zu einer Liste von Artikeln mit dem Tag 'Javascript'
Paulo Oliveira
1
Vielen Dank. Der Link wurde aktualisiert. Sie haben sich verändert, hatten es.
Myusuf