‫Kursleiter: Und nun erstellen wir endlich den letzten
‫Teil der Passwort-Reset-Funktionalität, wo
‫wir tatsächlich das neue Passwort für den Benutzer festlegen.
‫Beginnen wir also wie zuvor damit, die Schritte
‫zu definieren, die wir für diesen resetPassword-Flow unternehmen werden.
‫Rufen Sie also zuerst den Benutzer basierend auf dem Token ab.
‫Im zweiten Schritt legen wir dann das neue Passwort fest, jedoch
‫nur, wenn das Token noch nicht abgelaufen ist und
‫ein Benutzer vorhanden ist.
‫Legen Sie in diesem Fall das neue Passwort fest.
‫Danach müssen wir die Eigenschaft changePasswordAt
‫für den aktuellen Benutzer aktualisieren und schließlich,
‫wie bei
‫dieser Funktionalität üblich, den Benutzer
‫anmelden.
‫Senden Sie grundsätzlich das JSON Web Token an den Client.
‫Okay, es gibt noch viel zu tun und fangen wir an.
‫Denken Sie also aus dem letzten Video daran, dass
‫der Reset-Token, der tatsächlich in der URL gesendet
‫wird, dieser unverschlüsselte Token hier ist.
‫Also eigentlich dieser.
‫Aber diejenige, die wir in der
‫Datenbank haben, ist die verschlüsselte.
‫Darüber haben wir bereits gesprochen, und nun müssen
‫wir im Grunde den ursprünglichen Token
‫wieder verschlüsseln, damit
‫wir ihn dann mit dem in der
‫Datenbank gespeicherten, also verschlüsselten, vergleichen können.
‫Mit dem Passwort haben wir also schon
‫einmal etwas Ähnliches gemacht, aber mit dem Passwort
‫konnten wir es nicht so einfach wie möglich mit diesem
‫vergleichen, da wir für
‫das Passwort den recht komplexen bcrypt-Algorithmus verwendet haben, der
‫in diesem Fall wir nicht.
‫Hier ist es also ganz einfach.
‫Alles was wir noch tun müssen, ist den
‫Token zu verschlüsseln und mit dem verschlüsselten in der Datenbank zu vergleichen.
‫Sagen wir also hashedToken, und so brauchen wir
‫nun auch hier das Krypto-Paket.
‫Const crypto und dann require('crypto').
‫Jetzt rechts.
‫Dann gehen wir zurück, und so verwenden
‫wir Krypto. createHash.
‫Denken Sie daran, dann wieder den Namen des
‫Algorithmus, sha256, dann . update, im Grunde für den Ort, für den
‫String, den wir hashen möchten.
‫Erinnern Sie sich in req. Parameter.
‫Also benutzen wir diesen dann für eine lange Zeit. Zeichen.
‫Und wieder ist es ein Parameter,
‫weil wir das hier in der URL angegeben haben, also so.
‫Es handelt sich jetzt also um einen Parameter namens token.
‫Und so ist es hier natürlich req. Parameter. Zeichen.
‫Und schließlich müssen wir auch Digest sagen
‫und in hexadezimal umwandeln.
‫Das ist jetzt im Grunde dasselbe
‫wie vorher, wo wir das Original verschlüsselt haben, und so
‫konnten wir es in seine eigene Funktion umwandeln, aber
‫halten wir es hier einfach.
‫Lassen Sie uns nun den Benutzer basierend auf diesem Token abrufen.
‫Denn das ist eigentlich das Einzige,
‫was wir derzeit über den Benutzer wissen.
‫Wir haben keine E-Mail, wir haben nichts, daher ist dieses
‫Token das einzige, was den Benutzer identifizieren kann.
‫Und so können wir jetzt im Grunde die
‫Datenbank nach diesem Token abfragen.
‫Und es wird dann den Benutzer finden, der dieses Token hat.
‫Warten Sie also, wie wir bereits wissen, und dann Benutzer. einen finden.
‫Diese Eigenschaft heißt also passwordResetToken
‫und wir suchen nach dem hashedToken.
‫Und jetzt müssen wir es natürlich als async deklarieren und
‫in catchAsync vorbereiten.
‫Speichern Sie es, das sollte diesen Fehler beheben,
‫und das tut es tatsächlich.
‫Dadurch wird der Benutzer gefunden, der über das Token verfügt,
‫das über die URL gesendet wird.
‫Aber im Moment
‫berücksichtigen wir das Ablaufdatum des Tokens nicht.
‫Und wie könnten wir das tun?
‫Nun, im Grunde wollen wir überprüfen,
‫ob die Eigenschaft passwordResetExpires größer
‫ist als jetzt.
‫Denn wenn das Ablaufdatum größer als jetzt ist,
‫bedeutet dies, dass es in der Zukunft liegt, was wiederum bedeutet,
‫dass es noch nicht abgelaufen ist.
‫Dies ist also eine
‫sehr einfache Möglichkeit, dies mit dieser Abfrage tatsächlich richtig zu machen.
‫Also, passwordResetExpires, wo dieses Datum
‫gespeichert ist, und jetzt müssen wir
‫nur noch überprüfen,
‫ob es tatsächlich größer ist als jetzt.
‫Und wir wissen also bereits, wie das mit MongoDB geht, oder?
‫Also, neues Objekt und dann der größere Operator und dann wollen wir
‫es mit Datum vergleichen. jetzt, und dies wird
‫eigentlich ein Zeitstempel von jetzt sein, aber
‫hinter den Kulissen konvertiert MongoDB dann alles in
‫dasselbe und kann sie daher
‫genau vergleichen.
‫Damit können wir gleichzeitig den Benutzer
‫für den Token finden und auch prüfen, ob der
‫Token noch nicht abgelaufen ist.
‫So großartig.
‫Als nächstes wollen wir natürlich einen Fehler senden,
‫wenn kein Benutzer vorhanden ist oder im Grunde der Token abgelaufen ist.
‫Aber das ist in
‫diesem Fall dasselbe, denn wenn das Token abgelaufen ist,
‫wird es einfach keinen Benutzer zurückgeben.
‫Und so müssen wir nur sagen, wenn kein
‫Benutzer, dann, wie immer, als nächstes zurück, das ist nicht mext.
‫Also neuer AppError, und
‫sagen wir, Token ist ungültig oder abgelaufen.
‫Und dann 400, so schlechte Anfrage.
‫Wenn also kein Fehler auftritt und
‫next nicht aufgerufen wird,
‫dann setzen wir das Passwort tatsächlich.
‫Wir haben also bereits den Benutzer und jetzt ist es
‫ganz einfach: Benutzer. Passwort ist gleich req. Karosserie. Passwort.
‫Und das liegt daran, dass wir das Passwort
‫und auch die Passwortbestätigung selbstverständlich über den Body versenden.
‫Lassen Sie uns
‫also auch das und passwordConfirm duplizieren.
‫Und dann löschen wir im Grunde das Reset-Token und
‫das abgelaufene.
‫Also passwordResetToken, also genau wie zuvor, haben wir es auf undefined
‫gesetzt und jetzt user. Passwort läuft ab gleich
‫undefined.
‫Gut.
‫Und natürlich müssen wir es jetzt wieder
‫speichern, da dies das Dokument nur ändert, es
‫nicht wirklich aktualisiert.
‫Es wird also nicht wirklich gespeichert.
‫Warten Sie also auf den Benutzer. speichern.
‫Und in diesem Fall müssen wir
‫die Validatoren eigentlich nicht ausschalten, weil wir tatsächlich validieren wollen.
‫Zum Beispiel möchten wir, dass der
‫Validator bestätigt, ob das Passwort gleich passwordConfirm ist.
‫Und damit erledigt dieser Validator automatisch
‫all diese Arbeit für uns.
‫Dann der dritte Schritt, was wir am Ende
‫tatsächlich tun werden, und als nächstes werden wir
‫den Benutzer im Grunde einsperren.
‫Mit anderen Worten, senden Sie das JSON Web Token.
‫Und holen wir uns den Code von hier, also diesen.
‫Und wieder tun wir dies
‫hier bereits an drei verschiedenen Orten.
‫Also hier im Login, auch im Signup, und jetzt
‫zum dritten Mal hier unten.
‫Und so werden wir das irgendwann in der Zukunft in seine
‫eigene Funktion umgestalten.
‫Aber im Moment sind wir gut so.
‫Und so lassen Sie uns jetzt tatsächlich weitermachen und dies testen.
‫Dieses Reset-Token, das wir zuvor
‫hatten, ist also bereits abgelaufen, und wir müssen
‫im Grunde genommen nach einem neuen fragen.
‫Kommen wir also zu Postman und schlagen unsere Route zum Vergessen des Passworts ein.
‫Lassen Sie uns hier einfach die
‫Unordnung reduzieren und all diese offenen Registerkarten loswerden, die wir nicht
‫mehr benötigen.
‫Eigentlich brauchen wir hier diesen Test
‫für dieses Reset-Passwort, denn denken
‫Sie daran, dass dieser tatsächlich ein JSON-Web-Token zurückbekommt,
‫und deshalb wollen wir das in der Umgebungsvariablen speichern, genau
‫wie wir es
‫bei allen anderen gemacht haben.
‫Das mache ich jetzt, nur damit ich es nicht vergesse.
‫Alles klar, fangen wir im Grunde damit an, das
‫Passwort zu vergessen.
‫Das Versenden dieser Anfrage, was wiederum wegen des
‫Sendens der E-Mail einige Zeit
‫in Anspruch nimmt, aber hier gehen wir zu unserer E-Mail, und das
‫ist gerade vor ein paar Sekunden angekommen.
‫Also, das ist natürlich dieses Token.
‫Also schnappen wir es uns, kopieren es und jetzt zurück zu Postman, wir
‫verwenden es im Reset Password, als URL.
‫Okay, sinnvoll?
‫Auch hier senden wir dieses Token direkt in der URL.
‫Dann geben wir hier den
‫Body an, denn jetzt müssen wir tatsächlich unser neues Passwort angeben.
‫Also Passwort und sagen wir newpass.
‫Und dann...
‫Und hier nennen wir es
‫anders, denn im Moment möchte ich eigentlich einen Fehler sehen.
‫Und natürlich heißt dies passwordConfirm.
‫Mal sehen, was passiert, wenn wir dies versuchen.
‫Warten wir es ab, und wir erhalten, dass das Passwort
‫kürzer ist als die zulässige Mindestlänge.
‫Okay, also lass uns das ändern, 123, und hier sagen wir 1234.
‫Deshalb möchte ich, dass sie anders sind.
‫Aber Sie sehen, dass die Validierung hier gut funktioniert hat,
‫auch wenn das Passwort mit Speichern aktualisiert wurde.
‫So, und jetzt bekommen wir Passwörter sind nicht gleich!
‫Das ist also wieder ein Validierungsfehler.
‫Und denken Sie daran, dass dies der
‫einzige Grund ist, warum wir speichern und nicht aktualisieren müssen.
‫Früher haben wir also
‫zum Aktualisieren von Touren findOneAndUpdate verwendet, aber jetzt für alles,
‫was mit Passwörtern und dem Benutzer zu tun
‫hat, verwenden wir immer save, weil wir immer
‫alle Validatoren und vor allem die
‫Save-Middleware-Funktionen ausführen möchten.
‫Also zum Beispiel diejenigen, bei denen die Passwörter verschlüsselt sind.
‫Also lass es uns jetzt beenden.
‫Oh, ich habe es nicht richtig korrigiert, tut mir leid.
‫Und nun sollte es eigentlich funktionieren.
‫Und tatsächlich haben wir Erfolg und wir bekommen ein neues Zeichen.
‫Also toll, mal sehen, ob dieses Token tatsächlich gültig ist.
‫Wenn wir können, erhalten Sie alle Touren mit diesem brandneuen Token.
‫Und es geht los.
‫Unser neues Token funktioniert also tatsächlich, und nun sollten diese beiden
‫Eigenschaften für diesen Benutzer, also für
‫hello@jonas, eigentlich weg sein.
‫Das Passwort läuft also ab und das Token sollte
‫weg sein, denn das haben wir in unserem Code getan.
‫Also, ja, sie sind nicht mehr hier.
‫Jetzt müssen wir nur noch diesen fehlenden Schritt hier tun,
‫nämlich die passwordAt-Eigenschaft für diesen aktuellen Benutzer
‫zu aktualisieren.
‫Aber das sollte
‫nicht allzu schwer sein, also kehren wir schnell zum
‫userModel zurück, wo wir dies mithilfe von Middleware tun werden.
‫Und lassen Sie uns die gesamte
‫Middleware hier oben zusammenfassen.
‫Also userSchema. pre und nochmal
‫dot save, und dann eine Funktion mit next.
‫Auch hier wird diese Funktion
‫ausgeführt, kurz bevor ein neues Dokument tatsächlich gespeichert wird.
‫Und so ist es
‫der perfekte Ort, um diese Eigenschaft tatsächlich zu spezifizieren.
‫Und ich hätte es natürlich auch in einem Controller
‫direkt neben diesem Code machen können, zum Beispiel.
‫Aber ich möchte wirklich, dass dies sozusagen automatisch geschieht.
‫Also quasi hinter den Kulissen.
‫Denn später werden wir an einer
‫anderen Stelle das Passwort aktualisieren und dann sicherstellen, dass wir
‫dort denselben Code einfügen.
‫Und so passiert es wieder,
‫irgendwie hinter den
‫Kulissen, ohne dass wir uns darum kümmern müssen.
‫Wann genau wollen wir
‫nun die Eigenschaft passwordChangedAt auf genau setzen?
‫Nun, wir wollen es nur, wenn wir die Kennworteigenschaft
‫tatsächlich geändert haben.
‫Und ich bin mir nicht sicher, ob wir diesen Trick schon einmal
‫verwendet haben, aber egal, lass uns ihn jetzt anwenden.
‫Also, wenn wir nicht geändert haben, wenn nicht dies. isModified, also einfach so,
‫und dann
‫der Name der Eigenschaft, also Passwort.
‫Kehren Sie in diesem Fall also sofort zurück und führen
‫Sie die nächste Middleware aus.
‫Okay, nicht so, aber so.
‫Wenn wir also die Eigenschaft password nicht
‫geändert
‫haben, dann manipulieren Sie natürlich nicht die Eigenschaft passwordChangedAt.
‫Aber was ist mit dem Erstellen eines neuen Dokuments?
‫Nun, wenn wir ein neues Dokument
‫erstellen, haben wir tatsächlich
‫das Passwort geändert und dann die Eigenschaft passwordChangedAt gesetzt, richtig?
‫Nun, in der aktuellen Implementierung würden wir das tatsächlich tun.
‫Aber es gibt noch etwas anderes, das wir hier verwenden können.
‫Im Grunde wollen wir diese Middleware-Funktion also
‫sofort beenden, wenn das Passwort nicht geändert wurde oder
‫das Dokument neu ist, und können daher die
‫isNew-Eigenschaft verwenden.
‫Und wieder ist dies eines dieser sehr schönen Dinge, die Sie
‫durch das Lesen Ihrer Dokumentation lernen.
‫Daher kann ich nicht genug betonen, wie wichtig es ist,
‫die Dokumentationen wirklich zu lesen, wenn Sie etwas brauchen,
‫das Sie nirgendwo finden.
‫Denn es gibt wirklich so
‫viele Dinge, die man in einem Kurs nicht unterrichten kann.
‫Wie auch immer, wenn der Code diese Überprüfung hier
‫besteht, nun, dann sagen wir ganz
‫einfach dies. passwordChangedAt = Datum. jetzt.
‫Und dann rufen wir als nächstes an.
‫Theoretisch sollte dies nun gut funktionieren, aber
‫in der Praxis
‫tritt manchmal ein kleines Problem auf.
‫Und dieses Problem besteht darin, dass das Speichern
‫in der Datenbank manchmal etwas langsamer ist als das
‫Ausgeben des JSON-Web-Tokens, sodass der geänderte Passwort-Zeitstempel
‫manchmal etwas nach
‫der Erstellung des JSON-Web-Tokens gesetzt wird.
‫Und damit wird es dann so weit, dass
‫sich der Benutzer nicht mit dem neuen Token anmelden kann.
‫Denken Sie daran, dass dieser Zeitstempel hier
‫tatsächlich existiert, damit wir ihn
‫mit dem Zeitstempel im JSON-Web-Token vergleichen können, richtig?
‫Zur Erinnerung, es ist genau
‫hier, wo wir überprüfen, ob der Benutzer das Passwort
‫nach der Ausgabe des Tokens geändert hat.
‫Und hier unten, wo wir dann dieses neue Token
‫im Reset-Passwort erstellt haben.
‫Denken Sie daran, dass wir hier dieses
‫neue Token erstellen, und auch hier kommt es manchmal
‫vor, dass dieses Token kurz vor der tatsächlichen Erstellung
‫des geänderten Kennwortzeitstempels erstellt wird.
‫Also müssen wir das nur beheben, indem wir eine Sekunde subtrahieren.
‫Also im Grunde tausend Millisekunden.
‫Und damit wird das passwordChangedAt dann eine Sekunde in die
‫Vergangenheit gelegt, okay, was dann natürlich nicht
‫100% genau ist, aber das ist überhaupt kein Problem,
‫denn eine Sekunde macht hier überhaupt keinen Unterschied.
‫Es ist ein kleiner Hack, aber auch hier ist es kein Problem.
‫Wenn Sie also dieses passwordChanged eine Sekunde in die Vergangenheit legen,
‫wird sichergestellt, dass das Token immer erstellt
‫wird, nachdem das Passwort geändert wurde.
‫So, das funktioniert jetzt, aber wie
‫immer, lassen Sie es uns auch schnell testen.
‫Okay, also zurück zum Postboten.
‫Lassen Sie uns ein neues Passwort zurücksetzen, oder eigentlich
‫wollte ich das überhaupt nicht, aber
‫es ist eine großartige Sache zu sehen,
‫dass der Code tatsächlich funktioniert.
‫Das Token ist also ungültig oder abgelaufen, und das liegt
‫daran, dass 10 Minuten vergangen sind,
‫seit ich dieses Token tatsächlich erstellt habe.
‫Und ich denke, das hatten wir noch nicht
‫getestet, und deshalb ist es toll, dass es jetzt aus Versehen tatsächlich funktioniert hat.
‫Also, das kommt noch einmal, falls Sie sich fragen, was
‫zum Teufel passiert
‫ist, das ist natürlich diese Fehlermeldung hier.
‫Dies bedeutet, dass kein Benutzer gefunden wurde,
‫der dieses Token besitzt oder dessen Token länger als
‫10 Minuten zurückliegt.
‫Und so wollte ich tatsächlich das Passwort vergessen.
‫Warten wir also ab.
‫Also 8. 6 Sekunden,
‫aber das könnte an meiner Internetverbindung liegen.
‫Wenn Sie dies also auf einem
‫Server ausführen, wird es wahrscheinlich viel schneller sein.
‫Nehmen wir das hier, zurück zu Postman, und jetzt setzen
‫wir das Passwort zurück, wieder mit diesem Passwort, egal ob es
‫das gleiche wie das alte ist.
‫Und so haben wir jetzt unseren Erfolg hier.
‫Und nun zurück zum Kompass.
‫Lassen Sie uns neu laden, und tatsächlich erhalten wir das PasswortChangedAt.
‫Und das ist jetzt tatsächlich so.
‫Wenn wir also nun tatsächlich versuchen, dieses Token zu verwenden, um
‫beispielsweise auf diese geschützte Route zuzugreifen, dann
‫sollte das aufgrund dieses kleinen,
‫ein-zweiten-Tags, den wir gemacht haben, funktionieren.
‫So, und so haben wir jetzt
‫unsere Passwort-Reset-Funktionalität abgeschlossen.
‫Das war also ziemlich viel Code, aber es lohnt sich
‫natürlich auf jeden Fall.
‫Sie sollten diese Funktionalität also
‫immer in Ihrer Webanwendung anbieten, da
‫sonst ein Benutzer, der sein Passwort vergisst, völlig vermasselt wird,
‫er Ihre Anwendung nicht mehr verwenden
‫kann, und das ist natürlich eine schreckliche Praxis.
‫Wie auch immer, diese
‫Art beendet bereits den Authentifizierungs- und Autorisierungsteil dieses Abschnitts.
‫Es ist also wieder ziemlich
‫vollständig und ich hatte viel Spaß bei der Umsetzung.
‫Dieser Teil ist für mich also der Punkt, an
‫dem Webanwendungen wirklich zum Leben erwachen.
‫Ich weiß, dass es an dieser Stelle nicht wirklich
‫sichtbar ist, mit all diesen, nur Token und Token kopieren
‫und an anderer Stelle einfügen.
‫Das ist nicht die übliche Vorstellung, die wir beim Einloggen
‫haben, ich weiß, aber natürlich wieder, etwas später, wenn
‫wir endlich mit dem Aufbau der dynamischen Website beginnen,
‫dann werden wir
‫natürlich weiterhin diese Authentifizierung verwenden, die wir
‫gerade erstellt haben und dann es wird auch auf dieser dynamischen Website sichtbar.
‫Als nächstes werden wir Funktionen zum Aktualisieren
‫und Löschen des Benutzers implementieren
‫und danach auch über die Sicherheit sprechen.
‫Das ist, was für den Rest des Abschnitts vor
‫Ihnen liegt, also verpassen Sie das nicht.