web-dev-qa-db-de.com

Benutzerregistrierungs-/Authentifizierungsablauf auf a REST API

Ich weiß, dies ist nicht das erste Mal, dass das Thema in StackOverflow behandelt wird. Ich habe jedoch einige Fragen, auf die ich keine Antwort finden konnte, oder andere Fragen haben gegenteilige Antworten.

Ich mache eine ziemlich einfache REST - API (Silex-PHP), die anfangs von nur einem SPA (Backbone-App) genutzt werden kann. Ich möchte nicht alle verschiedenen Authentifizierungsmethoden in dieser Frage kommentieren, da dieses Thema bereits vollständig in SO behandelt wird. Grundsätzlich werde ich für jeden Benutzer ein Token erstellen. Dieses Token wird in jede Anforderung eingefügt, für die eine Authentifizierung durch den SPA erforderlich ist. Alle SPA-Server-Transaktionen werden unter HTTPS ausgeführt. Meine Entscheidung ist, dass das Token nicht verfällt. Token, die pro Sitzung auslaufen/Token sind, entsprechen nicht der Statuslosigkeit von REST, richtig? Ich verstehe, dass es viel Raum für Sicherheitsverbesserungen gibt, aber das ist mein Spielraum für jetzt. 

Ich habe ein Modell für Token und somit eine Tabelle in der Datenbank für Token mit einer FK to user_id. Damit meine ich, dass das Token nicht Teil meines Benutzermodells ist.

REGISTRIEREN

Ich habe einen POST/Benutzer (erfordert keine Authentifizierung), der einen Benutzer in der Datenbank erstellt und den neuen Benutzer zurückgibt. Dies entspricht der Anforderung einer Ressourcenanforderung. Dies bringt mir jedoch einige Zweifel:

  • Meine Idee ist, zum Zeitpunkt der Erstellung eines neuen Benutzers ein neues Token für den Benutzer zu erstellen, es sofort mit der Antwort zurückzugeben und somit die UX zu verbessern. Der Benutzer kann die Web-App sofort verwenden. Die Rückgabe des Tokens für eine solche Antwort würde jedoch die Regel widerrufen, nur die Ressource zurückzugeben. Soll ich stattdessen zwei Anfragen zusammen stellen? Eine zum Erstellen des Benutzers und eine zum Abrufen des Tokens, ohne dass der Benutzer die Anmeldeinformationen erneut eingeben muss.

  • Wenn ich mich entschied, das Token zusammen mit dem Benutzer zurückzugeben, glaube ich, dass POST/Benutzer für den API-Konsumenten verwirrend wären, und dann erscheint etwas wie POST/auth/register. Wieder einmal mag ich diese Idee nicht, weil sie ein Verb beinhaltet. Ich mag die Einfachheit in diese Antwort . Andererseits müsste ich zwei Anfragen zusammen stellen, einen POST/Benutzer und einen POST/Token. Wie falsch ist es, zwei Anfragen gemeinsam zu erledigen, und wie kann ich genau die relevanten Informationen für das Token senden, das an einen bestimmten Benutzer angehängt werden soll, wenn beide Anfragen zusammen gesendet werden?

Im Moment funktioniert mein Fluss wie folgt:

1. Register form makes a POST /users request
2. Server creates a new user and a new token, returns both in the response (break REST rule)
3. Client now attaches token to every Request that needs Authorization

Das Token verfällt nie und behält die Statuslosigkeit REST.

EMAIL-BEWERTUNG

Die meisten aktuellen Webapps erfordern eine E-Mail-Validierung, ohne dass die Benutzer die UX beschädigen müssen. Das heißt, die Benutzer können die Webapp nach der Registrierung sofort verwenden. Auf der anderen Seite haben die Benutzer sofort Zugriff auf jede Ressource, ohne E-Mails zu überprüfen, wenn ich das Token wie oben beschrieben mit der Registrierungsanforderung zurücksende.

Normalerweise würde ich für den folgenden Arbeitsablauf gehen:

1. Register form sends POST /users request.
2. Server creates a new user with validated_email set to false and stores an email_validation_token. Additionally, the server sends an email generating an URL that contains the email_validation_token. 
3. The user clicks on the URL that makes a request: For example POST /users/email_validation/{email_validation_token}
4. Server validates email, sets validated_email to true, generates a token and returns it in the response, redirecting the user to his home page at the same time.

Das sieht unkompliziert aus und ruiniert die UX total. Wie bist du darüber vorgegangen?

ANMELDUNG

Das ist ganz einfach, jetzt mache ich es auf diese Weise, also korrigiere mich bitte, falls falsch:

1. User fills a log in form which makes a request to POST /login sending Basic Auth credentials.
2. Server checks Basic Auth credentials and returns token for the given user.
3. Web app attached the given token to every future request.

login ist ein Verb und bricht damit eine REST -Regel. Jeder scheint sich jedoch auf diese Weise zu einigen.

AUSLOGGEN

Warum scheint jeder einen/auth/logout-Endpunkt zu benötigen? Wenn Sie in der Web-App auf "Abmelden" klicken, sollte das Token aus meiner Sicht grundsätzlich aus der Anwendung entfernt und nicht in weiteren Abfragen gesendet werden. Der Server spielt dabei keine Rolle.

Da es möglich ist, dass das Token in localStorage aufbewahrt wird, um zu verhindern, dass das Token bei einer möglichen Seitenaktualisierung verloren geht, bedeutet das Abmelden auch, dass das Token aus dem localStorage entfernt wird. Dies betrifft jedoch nicht den Server. Ich verstehe, dass Leute, die ein POST/logout benötigen, grundsätzlich mit Session-Token arbeiten, die die Statuslosigkeit von REST wieder brechen.

ERINNERE DICH AN MICH

Ich verstehe, dass sich die Erinnerung an mich grundsätzlich auf das Speichern des zurückgegebenen Tokens im localStorage bezieht oder nicht in meinem Fall. Ist das richtig?

Wenn Sie weiterführende Informationen zu diesem Thema empfehlen würden, wäre ich sehr dankbar. Vielen Dank!

26
mezod

REGISTER

Tokens, die ablaufen/Tokens pro Sitzung entsprechen nicht der Staatenlosigkeit von REST, oder?

Nein, daran ist nichts auszusetzen. Viele HTTP-Authentifizierungsschemata haben ablaufende Token. OAuth2 ist für REST Services sehr beliebt, und viele OAuth2-Implementierungen erzwingen, dass der Client das Zugriffstoken von Zeit zu Zeit aktualisiert.

Meine Idee ist, dass zur Zeit ein neuer Benutzer erstellt wird, ein neues Token für den Benutzer erstellt wird, um es sofort mit der Antwort zurückzugeben und somit die UX zu verbessern. Der Benutzer kann die Web-App sofort verwenden. Das Zurückgeben des Tokens für eine solche Antwort würde jedoch gegen die Regel verstoßen, nur die Ressource zurückzugeben. Soll ich stattdessen zwei Anfragen zusammen stellen? Einer zum Erstellen des Benutzers und einer zum Abrufen des Tokens, ohne dass der Benutzer die Anmeldeinformationen erneut eingeben muss?

Wenn Sie eine neue Ressource erstellen, die den bewährten Methoden REST entspricht, geben Sie in der Regel keine Antwort auf eine POST) zurück RPC-ähnlicher nennen, daher stimme ich Ihnen hier zu ... es ist nicht perfekt restvoll. Ich werde zwei Lösungen dafür anbieten:

  1. Ignorieren Sie dies und brechen Sie die bewährten Methoden. Vielleicht ist es in diesem Fall das Beste, und Ausnahmen zu machen, wenn sie viel sinnvoller sind, ist manchmal das Beste (nach sorgfältiger Überlegung).
  2. Wenn Sie mehr RESTful sein möchten, biete ich eine Alternative an.

Nehmen wir an, Sie möchten OAuth2 verwenden (keine schlechte Idee!). Die OAuth2-API ist aus mehreren Gründen nicht wirklich REST-konform. Ich bin der Meinung, dass es immer noch besser ist, eine gut definierte Authentifizierungs-API zu verwenden, um REST-konform zu sein.

Sie haben weiterhin das Problem, einen Benutzer in Ihrer API zu erstellen und als Antwort auf diesen Aufruf (POST) ein Geheimnis zurückzugeben, das als Zugriffs-/Aktualisierungstoken verwendet werden kann.

Meine Alternative ist wie folgt:

Sie benötigen keinen Benutzer, um eine Sitzung zu starten.

Sie können stattdessen die Sitzung starten , bevor Sie den Benutzer erstellen . Dies garantiert, dass Sie bei jedem zukünftigen Anruf wissen, dass Sie mit demselben Kunden sprechen.

Wenn Sie Ihren OAuth2-Prozess starten und Ihr Zugriffs-/Aktualisierungstoken erhalten, können Sie einfach eine authentifizierte POST request on /users. Dies bedeutet, dass Ihr System zwei Arten von authentifizierten Benutzern kennen muss:

  1. Benutzer, die sich mit einem Benutzernamen/Passwort angemeldet haben (`grant_type = passsword1).
  2. Benutzer, die sich "anonym" angemeldet haben und danach einen Benutzer erstellen möchten. (grant_type = client_credentials).

Sobald der Benutzer erstellt wurde, können Sie Ihre zuvor anonyme Sitzung mit der neu erstellten Benutzerentität verknüpfen, sodass Sie nach der Erstellung keinen Zugriffs-/Aktualisierungstokenaustausch mehr durchführen müssen.

EMAIL VALIDATION

Beide Ihre Vorschläge zu entweder:

  • Verhindern Sie, dass der Benutzer die Anwendung verwendet, bis die E-Mail-Überprüfung abgeschlossen ist.
  • Ermöglichen Sie dem Benutzer, die Anwendung sofort zu verwenden

Werden von Anwendungen durchgeführt. Welches am besten geeignet ist, hängt von Ihrer Anwendung ab und davon, was für Sie am besten geeignet ist. Besteht das Risiko, dass ein Benutzer ein Konto mit einer E-Mail-Adresse verwendet, die er nicht besitzt? Wenn nein, ist es vielleicht in Ordnung, den Benutzer sofort einzulassen.

Hier ist ein Beispiel, in dem Sie dies nicht tun möchten: Wenn die E-Mail-Adresse von anderen Mitgliedern Ihres Systems verwendet wird, um einen Benutzer als Freund hinzuzufügen, ist die E-Mail-Adresse eine Art Identität. Wenn Sie Benutzer nicht zwingen, ihre E-Mails zu validieren, kann ich im Namen einer anderen E-Mail-Adresse handeln. Dies ähnelt dem Empfangen von Einladungen usw. Handelt es sich um einen Angriffsvektor? Dann sollten Sie in Betracht ziehen, den Benutzer daran zu hindern, die Anwendung zu verwenden, bis die E-Mail validiert ist.

Sie können auch in Betracht ziehen, nur bestimmte Funktionen in Ihrer Anwendung zu blockieren, für die die E-Mail-Adresse möglicherweise vertraulich ist. Im vorherigen Beispiel konnten Sie verhindern, dass andere Benutzer Einladungen sehen, bis die E-Mail validiert wurde.

Hier gibt es keine richtige Antwort, es hängt nur davon ab, wie Sie die E-Mail-Adresse verwenden möchten.

LOGIN

Bitte benutzen Sie einfach OAuth2. Der Ablauf, den Sie beschreiben, entspricht bereits ziemlich genau der Funktionsweise von OAuth2. Gehen Sie noch einen Schritt weiter und verwenden Sie tatsächlich OAuth2. Es ist ziemlich gut und sobald Sie die anfängliche Hürde überwunden haben, das Protokoll zu verstehen, werden Sie feststellen, dass es einfacher als gedacht und ziemlich einfach ist, nur die Bits zu implementieren, die Sie speziell für Ihre API benötigen.

Die meisten der PHP OAuth2-Serverimplementierungen sind nicht großartig. Sie tun zu viel und sind etwas schwer zu integrieren. Das eigene Rolling ist nicht so schwer und Sie sind schon ziemlich nahe daran, etwas Ähnliches zu bauen.

LOGOUT

Die beiden Gründe, warum Sie möglicherweise einen Abmeldeendpunkt wünschen, sind:

  1. Wenn Sie die Cookie-/Sitzungsauthentifizierung verwenden und den Server anweisen möchten, die Sitzung zu vergessen. Es hört sich so an, als ob dies für Sie kein Problem ist.
  2. Wenn Sie dem Server mitteilen möchten, dass das Zugriffs-/Aktualisierungstoken früher abläuft. Ja, Sie können sie einfach aus dem lokalen Speicher entfernen, und das könnte gut genug sein. Das Erzwingen, dass sie serverseitig ablaufen, gibt Ihnen möglicherweise das gewisse zusätzliche Vertrauen. Was ist, wenn jemand in der Lage war, Ihren Browser MITM zu bearbeiten und nun Zugriff auf Ihre Token hat? Möglicherweise möchte ich mich schnell abmelden und alle vorhandenen Token verfallen lassen. Es ist ein Edge-Fall, und ich persönlich habe das noch nie gemacht, aber das könnte ein Grund sein, warum Sie es wollen würden.

ERINNERE MICH

Ja, die Implementierung von "Remember Me" mit lokalem Speicher klingt nach einer guten Idee.

11
Evert

Ich habe ursprünglich den /LOGON- und /LOGOUT-Ansatz gewählt. Ich fange an, /PRESENCE zu erkunden. Es scheint mir zu helfen, sowohl den Status einer Person als auch die Authentifizierung zu kombinieren.

0 = Offline
1 = Available
2 = Busy

Wenn Sie von Offline zu einem anderen Element wechseln, sollte dies die anfängliche Validierung umfassen (aka Benutzername und Kennwort erforderlich). Sie können PATCH oder PUT verwenden (je nachdem, wie Sie es sehen).

0
Tyler Montney