RESTful Passwort zurückgesetzt

107

Wie kann eine RESTful-Ressource zum Zurücksetzen eines Kennworts richtig strukturiert werden?

Diese Ressource ist als Passwort-Resetter für jemanden gedacht, der sein Passwort verloren oder vergessen hat. Es macht ihr altes Passwort ungültig und sendet ihnen ein Passwort per E-Mail.

Die zwei Optionen, die ich habe, sind:

POST /reset_password/{user_name}

oder...

POST /reset_password
   -Username passed through request body

Ich bin mir ziemlich sicher, dass die Anfrage ein POST sein sollte. Ich bin weniger sicher, dass ich einen geeigneten Namen ausgewählt habe. Und ich bin nicht sicher, ob der Benutzername über die URL oder den Anfragetext übergeben werden soll.

Chris Dutrow
quelle

Antworten:

54

UPDATE: (weiter unten zu kommentieren)

Ich würde so etwas machen:

POST /users/:user_id/reset_password

Sie haben eine Sammlung von Benutzern, wobei der einzelne Benutzer durch die angegeben wird {user_name}. Sie würden dann die Aktion angeben, mit der gearbeitet werden soll, in diesem Fall reset_password. Es ist, als würde man "Create ( POST) a new reset_passwordaction for {user_name}" sagen .


Vorherige Antwort:

Ich würde so etwas machen:

PUT /users/:user_id/attributes/password
    -- The "current password" and the "new password" passed through the body

Sie hätten zwei Sammlungen, eine Benutzersammlung und eine Attributsammlung für jeden Benutzer. Der Benutzer wird durch das :user_idund das Attribut durch angegeben password. Die PUTOperation aktualisiert das adressierte Mitglied der Sammlung.

Daniel Vassallo
quelle
10
Ich stimme Ihrer aktualisierten (POST) Lösung zu. PUT-Anforderungen sollten idempotent sein (dh wiederholte Anforderungen sollten das Ergebnis nicht beeinflussen). Dies ist bei POST-Anfragen nicht der Fall.
Ross McFarlane
16
Ich würde reset_password in password_reset ändern
Richard Knop
9
Moment mal, würde das nicht im Wesentlichen jemandem erlauben, das Passwort von jemandem zurückzusetzen? Wenn dies für jemanden gilt, der das aktuelle Kennwort vergisst, kann der betroffene Benutzer nicht mit dem aktuellen Kennwort authentifiziert werden. Im Wesentlichen bedeutet dies, dass diese API überhaupt kein Passwort akzeptieren konnte - so dass jeder das Passwort einer anderen Person zurücksetzen kann und wenn die API es zurückgibt, sogar das Passwort eines bekannten Benutzers erhalten kann ??? Oder fehlt mir etwas
transient_loop
39
Das Problem mit / user / {id} / password und dergleichen ist, dass Sie möglicherweise die "ID" des Benutzers nicht kennen. Sie würden ihren "Benutzernamen" oder "E-Mail" oder "Telefon" kennen, aber nicht die "ID".
coolaj86
17
Der grundlegende Fehler bei diesem Ansatz besteht darin, dass davon ausgegangen wird, dass Sie die Benutzer-ID bereits kennen. Dies ist unter bestimmten Umständen der Fall, aber wie gehen Sie vor, wenn der Benutzername oder die Benutzer-ID nicht bekannt ist, wenn der Benutzer nur eine E-Mail für das Zurücksetzen angeben muss.
Alappin
93

Nicht authentifizierte Benutzer

Wir führen eine PUTAnfrage auf einem api/v1/account/passwordEndpunkt durch und benötigen einen Parameter mit der entsprechenden Konto-E-Mail, um das Konto zu identifizieren, für das der Benutzer das Kennwort zurücksetzen (aktualisieren) möchte:

PUT : /api/v1/account/password?email={[email protected]}

Hinweis: Wie @DougDomeny in seinem Kommentar erwähnt hat, ist das Übergeben der E-Mail als Abfragezeichenfolge in der URL ein Sicherheitsrisiko. GET-Parameter werden bei der Verwendung nicht angezeigt https(und Sie sollten httpsfür solche Anforderungen immer eine ordnungsgemäße Verbindung verwenden), es bestehen jedoch andere Sicherheitsrisiken. Weitere Informationen zu diesem Thema finden Sie in diesem Blogbeitrag hier .

Das Übergeben der E-Mail im Anforderungshauptteil wäre eine sicherere Alternative zum Übergeben als GET-Parameter:

PUT : /api/v1/account/password

Text anfordern:

{
    "email": "[email protected]"
}

Die Antwort hat eine 202akzeptierte Antwortbedeutung:

Die Anforderung wurde zur Verarbeitung angenommen, die Verarbeitung wurde jedoch nicht abgeschlossen. Auf die Anforderung kann möglicherweise reagiert werden oder nicht, da sie möglicherweise nicht zulässig ist, wenn die Verarbeitung tatsächlich stattfindet. Es gibt keine Möglichkeit, einen Statuscode von einer solchen asynchronen Operation erneut zu senden.

Der Benutzer erhält eine E-Mail an [email protected]und die Verarbeitung der Aktualisierungsanforderung hängt von den Aktionen ab, die mit dem Link aus der E-Mail ausgeführt werden.

https://example.com/password-reset?token=1234567890

Das Öffnen des Links aus dieser E-Mail führt zu einem Formular zum Zurücksetzen des Kennworts in der Front-End-Anwendung, das das Token zum Zurücksetzen des Kennworts vom Link als Eingabe für ein verstecktes Eingabefeld verwendet (das Token ist Teil des Links als Abfragezeichenfolge). In einem weiteren Eingabefeld kann der Benutzer ein neues Passwort festlegen. Eine zweite Eingabe zur Bestätigung des neuen Passworts wird zur Validierung im Front-End verwendet (um Tippfehler zu vermeiden).

Hinweis: In der E-Mail können wir auch erwähnen, dass der Benutzer, falls er kein Zurücksetzen des Passworts initialisiert hat, die E-Mail ignorieren und die Anwendung weiterhin normal mit seinem aktuellen Passwort verwenden kann

Wenn das Formular mit dem neuen Passwort und dem Token als Eingabe gesendet wird, findet der Vorgang zum Zurücksetzen des Passworts statt. Die Formulardaten werden erneut mit einer PUTAnfrage gesendet, diesmal jedoch einschließlich des Tokens, und wir ersetzen das Ressourcenkennwort durch einen neuen Wert:

PUT : /api/v1/account/password

Text anfordern:

{
    "token":"1234567890",
    "new":"password"
}

Die Antwort ist eine Antwort 204ohne Inhalt

Der Server hat die Anforderung erfüllt, muss jedoch keinen Entitätskörper zurückgeben und möchte möglicherweise aktualisierte Metainformationen zurückgeben. Die Antwort kann neue oder aktualisierte Metainformationen in Form von Entity-Headern enthalten, die, falls vorhanden, der angeforderten Variante zugeordnet werden sollten.

Authentifizierte Benutzer

Für authentifizierte Benutzer, die ihr Passwort ändern möchten, PUTkann die Anforderung sofort ohne E-Mail ausgeführt werden (das Konto, für das wir das Passwort aktualisieren, ist dem Server bekannt). In diesem Fall sendet das Formular zwei Felder:

PUT : /api/v1/account/password

Text anfordern:

{
    "old":"password",
    "new":"password"
}
Verwelken
quelle
In Ihrem ersten Absatz sagen Sie PUT, aber das folgende Beispiel sagt DELETE. Welches ist genau?
Jpierson
Dadurch wird die E-Mail-Adresse in der URL angezeigt, wodurch JSON-Daten sicherer wären.
Doug Domeny
@DougDomeny Ja, das Senden der E-Mail als JSON-Daten wäre wahrscheinlich besser. Ich habe dies der Antwort als alternative sicherere Option hinzugefügt, andernfalls kann die Lösung dieselbe sein.
Wilt
@Wilt: Wäre das nicht eine PATCH-Operation? PUT erfordert das Senden der vollständigen Ressource
j10
1
@ Jitenshah Guter Punkt. Als ich das schrieb, dachte ich, PUT wäre besser, aber ich erinnere mich nicht genau warum. Ich stimme Ihrer Argumentation zu, dass Patch für diesen Fall besser geeignet sein könnte.
Wilt
18

Lassen Sie uns für eine Sekunde überruhig werden. Warum nicht die Aktion DELETE für das Passwort verwenden, um einen Reset auszulösen? Macht Sinn, nicht wahr? Schließlich verwerfen Sie das vorhandene Passwort effektiv zugunsten eines anderen.

Das heißt, Sie würden tun:

DELETE /users/{user_name}/password

Nun zwei große Vorbehalte:

  1. HTTP DELETE soll idempotent sein (ein schickes Wort für "keine große Sache, wenn Sie es mehrmals tun"). Wenn Sie die üblichen Aufgaben wie das Versenden einer E-Mail mit dem Titel "Passwort zurücksetzen" ausführen, treten Probleme auf. Sie könnten dies umgehen, indem Sie den Benutzer / das Kennwort mit einem booleschen "Is Reset" -Flag versehen. Bei jedem Löschen aktivieren Sie dieses Flag. Wenn es nicht festgelegt ist , können Sie das Passwort zurücksetzen und Ihre E-Mail senden. (Beachten Sie, dass dieses Flag auch andere Verwendungszwecke haben kann.)

  2. Sie können HTTP DELETE nicht über ein Formular verwenden , daher müssen Sie einen AJAX-Aufruf tätigen und / oder das DELETE über den POST tunneln.

Craig Walker
quelle
9
Interessante Idee. Allerdings sehe ich hier keine DELETEgute Passform. Sie würden das Passwort durch ein zufällig generiertes ersetzen, DELETEkönnte also irreführend sein. Ich bevorzuge die Create (POST) new reset_password action, bei der das Substantiv (Ressource), auf das Sie einwirken würden, die "reset_password action" ist. Dies eignet sich auch gut zum Senden von E-Mails, da die Aktion alle diese Details auf niedrigerer Ebene enthält. POSTist nicht idempotent.
Daniel Vassallo
Ich mag den Vorschlag. Problem 1 könnte mithilfe von bedingten Anforderungen behoben werden, dh HEAD, das ETag + DELETE- und If-Match-Header sendet. Wenn jemand versucht, ein Passwort zu löschen, das nicht mehr aktiv ist, erhält er eine 412.
Whiskysierra
1
Ich würde DELETE vermeiden. Sie aktualisieren, da dieselbe Entität / dasselbe Konzept einen neuen Wert erhält. Aber normalerweise passiert es jetzt nicht einmal, sondern erst nach dem Senden des neuen Passworts in einer späteren anderen Anfrage (nach einem Zurücksetzen des Passworts) - Heutzutage sendet niemand ein neues Passwort per Mail, sondern ein Token, um es in einer neuen Anfrage mit zurückzusetzen ein gegebenes Zeichen, richtig?
antonio.fornie
11
Was ist, wenn sich der Benutzer sein Passwort unmittelbar nach einer Rücksetzanforderung merkt? Was ist mit einem Bot, der versucht, zufällige Konten zurückzusetzen? In diesem Fall sollte es dem Benutzer gestattet sein, die zurückgesetzte E-Mail zu ignorieren (dies sollte in der E-Mail angegeben sein). Dies bedeutet, dass Ihr System Kennwörter nicht selbst löschen oder aktualisieren sollte.
Maxime Laval
3
@ MaximeLaval Das ist ein sehr guter Punkt. Wirklich, Sie "erstellen eine Rücksetzanforderung", was ein POST wäre.
Craig Walker
12

Oft möchten Sie das vorhandene Kennwort des Benutzers bei der ersten Anforderung nicht löschen oder zerstören, da dies möglicherweise (unbeabsichtigt oder absichtlich) von einem Benutzer ausgelöst wurde, der keinen Zugriff auf die E-Mail hat. Aktualisieren Sie stattdessen ein Reset-Kennwort-Token im Benutzerdatensatz und senden Sie es über einen in einer E-Mail enthaltenen Link. Durch Klicken auf den Link wird bestätigt, dass der Benutzer das Token erhalten hat und sein Kennwort aktualisieren möchte. Im Idealfall wäre dies auch zeitkritisch.

Die RESTful-Aktion wäre in diesem Fall ein POST: Auslösen der Erstellungsaktion auf dem PasswordResets-Controller. Die Aktion selbst würde das Token aktualisieren und eine E-Mail senden.

Mark Swardstrom
quelle
9

Ich suche tatsächlich nach einer Antwort, ohne eine zu liefern - aber "reset_password" klingt für mich in einem REST-Kontext falsch, weil es ein Verb ist, kein Substantiv. Selbst wenn Sie sagen, dass Sie ein Substantiv "Aktion zurücksetzen" ausführen - mit dieser Begründung sind alle Verben Substantive.

Es ist möglicherweise auch niemandem in den Sinn gekommen, nach der gleichen Antwort zu suchen, dass Sie den Benutzernamen möglicherweise über den Sicherheitskontext abrufen können und ihn überhaupt nicht über die URL oder den Text senden müssen, was mich nervös macht.

Orbfish
quelle
4
Klingt vielleicht reset-passwordwie ein Verb, aber Sie können es leicht umkehren ( password-reset), um es zu einem Substantiv zu machen. Und wenn Sie Ihre Anwendung mithilfe von Event Sourcing modelliert haben oder nur eine Art von Prüfung haben, ist es sinnvoll, dass Sie tatsächlich eine echte Entität mit diesem Namen haben und möglicherweise sogar GETs darauf zulassen, die Benutzer oder Administratoren sehen können Verlauf (offensichtlich Maskierung des Passworttextes). Macht mich überhaupt nicht nervös. Und was das automatische Abrufen des Benutzernamens auf der Serverseite betrifft - Sie können, aber wie gehen Sie dann mit Dingen wie Verwaltung / Identitätswechsel um?
Aaronaught
1
Es ist nichts Falsches daran, Verb in REST zu verwenden. Nur solange es an geeigneten Stellen verwendet wird. Ich denke, dies ist eher ein Controller als eine Resorce und es reset-passwordgelingt mir, die Auswirkungen gut zu beschreiben.
Anders Östman
6

Ich denke, eine bessere Idee wäre:

DELETE /api/v1/account/password    - To reset the current password (in case user forget the password)
POST   /api/v1/account/password    - To create new password (if user has reset the password)
PUT    /api/v1/account/{userId}/password    - To update the password (if user knows is old password and new password)

In Bezug auf die Bereitstellung der Daten:

  • Aktuelles Passwort zurücksetzen

    • E-Mail sollte in Körper gegeben werden.
  • So erstellen Sie ein neues Passwort (nach dem Zurücksetzen)

    • Neues Passwort, Aktivierungscode und E-Mail-ID sollten im Text angegeben werden.
  • So aktualisieren Sie das Kennwort (für angemeldete Benutzer)

    • altes Passwort, neues Passwort sollte im Text angegeben werden.
    • Benutzer-ID in Params.
    • Auth Token in den Headern.
Codesnooker
quelle
2
Wie in anderen Antworten kommentiert, ist "DELETE / api / v1 / account / password" eine schlechte Idee, da jeder sein Passwort zurücksetzen kann.
Maximedupre
Wir benötigen eine registrierte E-Mail-ID, um das Passwort zurückzusetzen. Die Wahrscheinlichkeit, die E-Mail-ID eines unbekannten Benutzers zu kennen, ist sehr gering, es sei denn, wir betreiben eine Website wie Facebook und haben Tonnen von E-Mail-IDs auf irgendeine Weise gesammelt. Dann werden die Sicherheitsrichtlinien entsprechend definiert. Was ist Ihr Vorschlag, um jemandes Passwort zurückzusetzen?
Codesnooker
5

Es sind einige Überlegungen zu treffen:

Das Zurücksetzen von Passwörtern ist nicht idempotent

Eine Kennwortänderung wirkt sich auf die Daten aus, die als Anmeldeinformationen für die Ausführung verwendet werden. Dies kann zukünftige Versuche ungültig machen, wenn die Anforderung einfach wörtlich wiederholt wird, während sich die gespeicherten Anmeldeinformationen geändert haben. Wenn beispielsweise ein Token zum vorübergehenden Zurücksetzen verwendet wird, um die Änderung zuzulassen, wie es in einer Situation mit vergessenem Kennwort üblich ist, sollte dieses Token bei erfolgreicher Kennwortänderung abgelaufen sein, wodurch weitere Versuche, die Anforderung zu replizieren, erneut zunichte gemacht werden. Daher scheint ein RESTful-Ansatz für eine Kennwortänderung ein Job zu sein, der besser geeignet ist POSTals PUT.

ID oder E-Mail in der Datenladung ist wahrscheinlich redundant

Obwohl dies nicht gegen REST verstößt und möglicherweise einen besonderen Zweck hat, ist es häufig nicht erforderlich, eine ID oder E-Mail-Adresse für ein Zurücksetzen des Kennworts anzugeben. Denken Sie darüber nach, warum sollten Sie die E-Mail-Adresse als Teil der Daten für eine Anfrage angeben, die auf die eine oder andere Weise authentifiziert werden soll? Wenn der Benutzer lediglich sein Passwort ändert, muss er sich dazu authentifizieren (über Benutzername: Passwort, E-Mail: Passwort oder Zugriffstoken, das über Header bereitgestellt wird). Daher haben wir ab diesem Schritt Zugriff auf ihr Konto. Wenn sie ihr Passwort vergessen hätten, hätten sie ein temporäres Reset-Token (per E-Mail) erhalten, das sie speziell als Anmeldeinformationen für die Durchführung der Änderung verwenden können. In diesem Fall sollte die Authentifizierung über ein Token ausreichen, um das Konto zu identifizieren.

Unter Berücksichtigung all dieser Punkte ist hier meines Erachtens das richtige Schema für eine RESTful-Passwortänderung:

Method: POST
url: /v1/account/password
Access Token (via headers): pwd_rst_token_b3xSw4hR8nKWE1d4iE2s7JawT8bCMsT1EvUQ94aI
data load: {"password": "This 1s My very New Passw0rd"}
Michael Ekoka
quelle
Eine Aussage, dass ein Platzhalter Out-of-Band-Informationen benötigt, ist nicht vollständig wahr. Ein spezieller Medientyp kann die Syntax und Semantik bestimmter Elemente einer Anforderung oder Antwort beschreiben. Es ist somit möglich, dass ein Medientyp definiert, dass ein in einem bestimmten Feld enthaltener URI Platzhalter für bestimmte Daten definieren kann, und die Semantik definiert ferner, dass eine codierte Benutzer-E-Mail oder was nicht anstelle des Platzhalters enthalten sein sollte. Clients und Server, die diesen Medientyp respektieren, entsprechen weiterhin den RESTful-Architekturprinzipien.
Roman Vottner
1
In Bezug auf POSTvs. PUT RFC 7231 gibt an, dass eine teilweise Aktualisierung durch überlappende Daten von zwei Ressourcen erreicht werden kann , aber es ist fraglich , ob so etwas wie /v1/account/passwordeigentlich wirklich für eine gute Ressource nicht bilden. Ebenso POSTwie der Schweizer Armee-Kniff des Webs, der verwendet werden kann, wenn keine der anderen Methoden durchführbar ist, PATCHkönnte dies auch eine Option zum Festlegen des neuen Passworts sein.
Roman Vottner
Was ist mit der URL, um das Zurücksetzen des Passworts anzufordern, wenn sie ihr Passwort nicht kennen?
Dan Carter
2

Ich hätte nichts, das das Passwort ändert und ihnen ein neues sendet, wenn Sie sich für die Methode / users / {id} / password entscheiden und an Ihrer Idee festhalten, dass die Anfrage eine eigene Ressource ist. dh / user-password-request / ist die Ressource und verwendet PUT. Benutzerinformationen sollten sich im Hauptteil befinden. Ich würde das Passwort jedoch nicht ändern. Ich sende eine E-Mail an den Benutzer, die einen Link zu einer Seite enthält, die eine request_guid enthält, die zusammen mit einer Anfrage an POST / user / {id} / password /? Request_guid = übergeben werden kann xxxxx

Das würde das Passwort ändern und es erlaubt niemandem, einen Benutzer durch Anfordern einer Passwortänderung abzuspritzen.

Außerdem kann der anfängliche PUT fehlschlagen, wenn eine ausstehende Anforderung vorliegt.

bpeikes
quelle
0

Wir aktualisieren das protokollierte Benutzerkennwort PUT / v1 / users / password - identifizieren Sie die Benutzer-ID mit AccessToken.

Es ist nicht sicher, die Benutzer-ID auszutauschen. Die Restful-API muss den Benutzer anhand des im HTTP-Header empfangenen AccessToken identifizieren.

Beispiel im Spring-Boot

@putMapping(value="/v1/users/password")
public ResponseEntity<String> updatePassword(@RequestHeader(value="Authorization") String token){
/* find User Using token */
/* Update Password*?
/* Return 200 */
}
Dapper Dan
quelle