‫Instructeur : Et maintenant, créons enfin la dernière partie de la
‫fonctionnalité de réinitialisation de mot de passe,
‫où nous définissons en fait le nouveau mot de passe pour l'utilisateur.
‫Et donc, comme avant, commençons par définir les
‫étapes que nous allons suivre pour ce flux resetPassword.
‫Donc, tout d'abord, obtenez l'utilisateur en fonction du jeton.
‫Ensuite, dans un deuxième temps, nous définirons le nouveau mot de passe
‫mais uniquement si le jeton n'a pas expiré et qu'il
‫y a un utilisateur.
‫Donc, dans ce cas, définissez le nouveau mot de passe.
‫Ensuite, nous devons mettre à jour
‫la propriété modifiedPasswordAt pour l'utilisateur actuel, puis
‫enfin, comme
‫d'habitude dans cette fonctionnalité, connecter
‫l'utilisateur.
‫En gros, envoyez le jeton Web JSON au client.
‫Bon, donc beaucoup de travail à faire, et commençons.
‫Et donc, rappelez-vous de la dernière vidéo, que le jeton
‫de réinitialisation qui est réellement envoyé dans l'URL
‫est ce jeton non crypté ici.
‫Donc, en fait celui-ci.
‫Mais celui que nous avons dans la base
‫de données est celui crypté.
‫Nous en avons donc déjà parlé, et ce que nous devons
‫maintenant faire, c'est essentiellement crypter à nouveau le
‫jeton d'origine, afin que nous
‫puissions ensuite le comparer avec celui qui est stocké,
‫donc celui crypté dans la base de données.
‫Donc, nous avons en fait fait quelque chose de
‫similaire avant avec le mot de passe, mais avec le mot
‫de passe, nous ne pouvions pas le comparer aussi facilement qu'avec celui-ci, encore
‫une fois parce que pour
‫le mot de passe nous avons utilisé l'algorithme bcrypt assez complexe, qui dans
‫ce cas, nous ne l'avons pas fait.
‫Donc, ici, c'est très simple.
‫Tout ce que nous avons à faire à nouveau, c'est de
‫crypter le jeton et de le comparer avec celui crypté dans la base de données.
‫Donc, disons hashedToken, et nous aurons donc maintenant besoin
‫du package crypto également ici.
‫Const crypto puis require('crypto').
‫Maintenant à droite.
‫Ensuite, revenons en arrière, et donc, nous utilisons
‫la crypto. createHash.
‫Rappelez-vous, puis le nom de l'algorithme à nouveau,
‫sha256, puis . update, essentiellement pour l'endroit, pour la chaîne
‫que nous voulons hacher.
‫Et donc, celui-là est, rappelez-vous dans req. paramètres.
‫Nous utilisons donc celui-ci pendant longtemps. jeton.
‫Et encore une fois, c'est un
‫paramètre, car nous l'avons spécifié ici dans l'URL, donc comme ça.
‫C'est donc maintenant un paramètre appelé jeton.
‫Et donc, bien sûr, ici c'est req. paramètres. jeton.
‫Et puis, enfin, nous devons aussi dire digérer et
‫le convertir en hexadécimal.
‫Maintenant, c'est fondamentalement la même chose
‫qu'avant, où nous avons chiffré l'original, et nous pourrions
‫donc le refactoriser dans sa propre fonction, mais gardons
‫les choses simples ici.
‫Donc, maintenant, obtenons l'utilisateur basé sur ce jeton.
‫Parce que c'est en fait la seule
‫chose que nous savons sur l'utilisateur en ce moment.
‫Nous n'avons pas d'email, nous n'avons rien, donc ce token
‫est la seule chose qui permet d'identifier l'utilisateur.
‫Et donc nous pouvons maintenant, en gros, interroger la
‫base de données pour ce jeton.
‫Et il trouvera alors l'utilisateur qui possède ce jeton.
‫Alors, attendez, comme nous le savons déjà, puis Utilisateur. trouverUn.
‫Ainsi, cette propriété s'appelle
‫passwordResetToken et nous recherchons le hashedToken.
‫Et maintenant, bien sûr, nous devons le déclarer comme async et le
‫préparer dans catchAsync.
‫Faites-lui une sauvegarde, cela devrait corriger ce bogue,
‫et c'est effectivement le cas.
‫Ainsi, cela trouvera l'utilisateur qui a le
‫jeton qui enverra via l'URL.
‫Mais, pour le moment, nous
‫ne prenons pas en considération la date d'expiration du jeton.
‫Et alors comment pourrions-nous faire cela?
‫Eh bien, en gros, ce que nous voulons, c'est
‫vérifier si la propriété passwordResetExpires est supérieure à
‫ce qu'elle est actuellement.
‫Parce que si la date d'expiration est supérieure à maintenant,
‫cela signifie que c'est dans le futur, ce qui signifie à son
‫tour qu'elle n'a pas encore expiré.
‫Et donc, c'est un
‫moyen très simple de le faire correctement avec cette requête.
‫Donc, passwordResetExpires, qui est l'endroit où cette
‫date est stockée, et maintenant tout ce dont
‫nous avons besoin pour
‫vérifier si elle est réellement supérieure à celle actuelle.
‫Et donc nous savons déjà comment faire cela avec MongoDB, n'est-ce pas ?
‫Donc, nouvel objet, puis le plus grand opérateur et ensuite ce avec quoi nous
‫voulons le comparer est Date. maintenant, et ce sera
‫en fait un horodatage de maintenant, mais
‫dans les coulisses, MongoDB convertira alors tout au
‫même, et pourra donc les comparer
‫avec précision.
‫Et ainsi, avec cela, nous pouvons, en
‫même temps, trouver l'utilisateur du token et vérifier également si le
‫token n'a pas encore expiré.
‫Tellement bon.
‫Donc, ensuite, nous voulons, bien sûr, envoyer une
‫erreur s'il n'y a pas d'utilisateur, ou fondamentalement, si le jeton a expiré.
‫Mais c'est, dans ce cas,
‫la même chose, car si le jeton a expiré, eh bien,
‫il ne renverra tout simplement aucun utilisateur.
‫Et donc tout ce que nous avons à faire est de
‫dire, s'il n'y a pas d'utilisateur, eh bien, comme toujours, revenez ensuite, ce n'est pas mext.
‫Donc nouvelle AppError, et disons que
‫le jeton n'est pas valide ou a expiré.
‫Et puis 400, donc mauvaise demande.
‫Et donc, s'il n'y a pas d'erreur, et
‫si next n'est pas appelé,
‫eh bien, définissons le mot de passe.
‫Donc, nous avons déjà l'utilisateur et maintenant c'est très
‫simple : user. le mot de passe est égal à req. corps. le mot de passe.
‫Et c'est parce que nous allons bien sûr envoyer
‫le mot de passe et également passwordConfirm via le corps.
‫Alors dupliquons cela et
‫confirmez également le mot de passe.
‫Et puis aussi, supprimons essentiellement le jeton de réinitialisation
‫et l'expiration.
‫Donc passwordResetToken, donc comme nous l'avons fait auparavant, nous l'avons défini sur
‫undefined, et maintenant sur user. le mot de passe expire est
‫égal à indéfini.
‫D'accord.
‫Et encore une fois, bien sûr, nous devons maintenant
‫l'enregistrer, car cela ne fait que modifier le document, il ne se
‫met pas vraiment à jour.
‫Donc ça ne le sauve pas vraiment.
‫Alors, attendez l'utilisateur. enregistrer.
‫Et dans ce cas, nous n'avons en fait
‫pas besoin de désactiver les validateurs, car en effet, nous voulons valider.
‫Par exemple, nous voulons que le validateur
‫confirme si le mot de passe est égal à passwordConfirm.
‫Et pour que ce validateur fasse automatiquement
‫tout ce travail pour nous.
‫Ensuite, la troisième étape, ce que nous allons faire
‫à la fin, et donc ce que nous allons
‫faire ensuite, c'est essentiellement verrouiller l'utilisateur.
‫En d'autres termes, envoyez le jeton Web JSON.
‫Et récupérons ce code d'ici, donc celui-ci.
‫Et encore une fois, nous le
‫faisons déjà ici à trois endroits différents.
‫Donc ici dans la connexion, également dans l'inscription, et maintenant
‫pour la troisième fois, ici.
‫Et donc, dans le futur, nous refactoriserons cela dans sa
‫propre fonction.
‫Mais pour l'instant, on est bien comme ça.
‫Et donc, allons-y maintenant et testons cela.
‫Donc, ce jeton de réinitialisation que
‫nous avions auparavant a déjà expiré, et nous devons
‫donc en demander un nouveau, en gros.
‫Venons-en donc à Postman et empruntons notre route pour oublier le mot de passe.
‫Réduisons simplement l'encombrement ici et
‫débarrassons-nous de tous ces onglets ouverts dont nous n'avons
‫plus besoin.
‫En fait, ici, nous aurons besoin de ce
‫test pour ce mot de passe
‫de réinitialisation, car rappelez-vous que celui-ci récupère en fait un
‫jeton Web JSON, et nous voulons donc l'enregistrer dans la variable
‫d'environnement, comme nous
‫l'avons fait avec tous les autres.
‫Alors je fais ça maintenant, juste pour ne pas l'oublier.
‫Bon, de toute façon, commençons par oublier le
‫mot de passe.
‫Donc envoyer cette demande, ce qui, encore une fois,
‫prend du temps à cause de
‫l'envoi de l'e-mail, mais c'est parti, et passons maintenant à notre e-mail, et donc
‫cela vient d'arriver il y a quelques secondes.
‫Alors, c'est ça, bien sûr, ce jeton.
‫Alors saisissons-le, copions-le et revenons maintenant à Postman, nous l'utilisons dans le
‫mot de passe de réinitialisation, comme URL.
‫D'accord, ça a du sens ?
‫Encore une fois, nous envoyons ce jeton directement dans l'URL.
‫Alors ici, spécifions le corps,
‫car maintenant, nous devons réellement spécifier notre nouveau mot de passe.
‫Alors mot de passe et disons newpass.
‫Puis...
‫Et ici appelons ça autrement,
‫car pour l'instant, je veux en fait voir une erreur.
‫Et bien sûr, cela s'appelle passwordConfirm.
‫Voyons donc ce qui se passe lorsque nous essayons de faire cela.
‫Attendons-le, et nous obtenons que le mot de passe est plus
‫court que la longueur minimale autorisée.
‫D'accord, changeons cela, 123, et ici disons 1234.
‫Alors je veux qu'ils soient différents.
‫Mais vous voyez que la validation ici a très bien fonctionné, même lors
‫de la mise à jour du mot de passe avec save.
‫Donc, et maintenant nous obtenons que les mots de passe ne sont pas les mêmes !
‫Encore une fois, c'est une erreur de validation.
‫Et rappelez-vous, en fait, que c'est la seule raison pour
‫laquelle nous devons utiliser la sauvegarde et non la mise à jour.
‫Alors avant, pour la mise
‫à jour des visites, nous utilisions findOneAndUpdate, mais maintenant, pour tout
‫ce qui concerne les mots de passe et l'utilisateur, nous
‫utilisons toujours save, car nous voulons toujours exécuter tous
‫les validateurs, et surtout, les fonctions
‫de sauvegarde du middleware.
‫Ainsi, par exemple, ceux où les mots de passe sont cryptés.
‫Alors finissons-en maintenant.
‫Oh, je ne l'ai pas vraiment corrigé, désolé pour ça.
‫Et maintenant, cela devrait fonctionner.
‫Et en effet, nous obtenons du succès, et nous obtenons un nouveau jeton.
‫Tellement génial, voyons si ce jeton est réellement valide.
‫Donc, si nous le pouvons, obtenez toutes les visites en utilisant ce tout nouveau jeton.
‫Et c'est reparti.
‫Ainsi, notre nouveau jeton fonctionne réellement, et maintenant pour cet utilisateur,
‫donc pour hello@jonas, ces deux propriétés
‫devraient en fait disparaître.
‫Donc le mot de passe expire et le jeton devrait disparaître,
‫puisque, eh bien, puisque c'est ce que nous avons fait dans notre code.
‫Et donc, oui, ils ne sont plus là.
‫Maintenant, tout ce que nous devons faire en fait, c'est cette étape manquante ici,
‫qui consiste à mettre à jour la propriété passwordAt
‫pour cet utilisateur actuel.
‫Mais cela ne
‫devrait pas être trop difficile, et revenons donc rapidement
‫au userModel, où nous allons le faire en utilisant un middleware.
‫Et rassemblons en fait tous les
‫middleware ici en haut.
‫Donc, userSchema. pre et
‫encore dot save, puis une fonction avec next.
‫Encore une fois, cette fonction ici
‫va s'exécuter juste avant qu'un nouveau document ne soit réellement enregistré.
‫Et donc, c'est
‫l'endroit idéal pour spécifier réellement cette propriété.
‫Et j'aurais pu, bien sûr, le faire dans un contrôleur juste
‫à côté d'ici à ce code, par exemple.
‫Mais je veux vraiment que cela se produise, en quelque sorte, automatiquement.
‫Donc, un peu dans les coulisses.
‫Parce que plus tard, nous aurons un autre endroit
‫où nous mettrons à jour le mot de passe et ensuite nous nous assurerons
‫que nous incluons le même code là-bas.
‫Et comme ça, encore une fois, cela se
‫passe, en quelque sorte,
‫dans les coulisses, sans que nous ayons à nous en soucier du tout.
‫Maintenant, à quel moment voulons-nous
‫réellement définir la propriété passwordChangedAt pour le moment ?
‫Eh bien, nous ne le voulons que lorsque nous avons réellement modifié la
‫propriété du mot de passe.
‫Et je ne sais pas si nous avons utilisé cette
‫astuce avant, mais de toute façon, utilisons-la maintenant.
‫Donc si on n'a pas modifié, alors si ce n'est pas ça. isModified, donc juste comme ça,
‫puis le
‫nom de la propriété, donc mot de passe.
‫Donc, dans ce cas, revenez tout de suite
‫et exécutez le middleware suivant.
‫D'accord, pas comme ça, mais comme ça.
‫Encore une fois, si nous n'avons pas modifié la propriété
‫du mot
‫de passe, alors bien sûr, ne manipulez pas le mot de passeChangedAt.
‫Mais qu'en est-il de la création d'un nouveau document ?
‫Eh bien, lorsque nous créons un nouveau
‫document, nous modifions en fait
‫le mot de passe, puis nous définissons la propriété passwordChangedAt, n'est-ce pas ?
‫Eh bien, dans la mise en œuvre actuelle, nous le ferions en fait.
‫Mais il y a autre chose que nous pouvons utiliser ici.
‫Donc, en gros, nous voulons quitter cette fonction middleware tout
‫de suite, si le mot de passe n'a pas été modifié
‫ou si le document est nouveau, et nous pouvons donc utiliser
‫la propriété isNew.
‫Et encore une fois, c'est l'une de ces très belles choses
‫qui s'apprennent en lisant votre documentation.
‫Et donc, je ne saurais trop insister sur l'importance de vraiment lire les
‫documentations lorsque vous avez besoin de quelque chose que vous
‫ne pouvez trouver nulle part.
‫Parce qu'il y a vraiment tellement
‫de choses là-dedans qu'il est complètement impossible d'enseigner dans un seul cours.
‫Quoi qu'il en soit, si le code passe cette vérification
‫ici, eh bien, alors disons très
‫simplement, ceci. passwordChangedAt = Date. maintenant.
‫Et puis, nous appelons ensuite.
‫Maintenant, en théorie, cela devrait très bien fonctionner,
‫mais en réalité,
‫dans la pratique, un petit problème survient parfois.
‫Et ce problème est que parfois l'enregistrement dans la base de
‫données est un peu plus lent que l'émission du jeton Web JSON,
‫ce qui fait que l'horodatage du mot de passe
‫modifié est parfois défini un
‫peu après la création du jeton Web JSON.
‫Et cela fera alors en sorte que l'utilisateur
‫ne pourra pas se connecter en utilisant le nouveau jeton.
‫Parce que, rappelez-vous, la raison pour laquelle cet horodatage existe
‫réellement ici, c'est pour que nous puissions le
‫comparer avec l'horodatage sur le jeton Web JSON, n'est-ce pas ?
‫Donc, juste pour se rappeler, c'est,
‫eh bien, donc ici, où nous vérifions si l'utilisateur a changé
‫le mot de passe après l'émission du jeton.
‫Et donc, ici-bas, où nous avons ensuite créé ce nouveau jeton
‫dans réinitialiser le mot de passe.
‫Donc, ici, rappelez-vous, nous créons ce nouveau jeton,
‫et encore une fois, il arrive parfois que ce
‫jeton soit créé un peu avant que l'horodatage du mot
‫de passe modifié n'ait été créé.
‫Et donc, nous avons juste besoin de corriger cela en soustrayant une seconde.
‫Donc, en gros, mille millisecondes.
‫Et alors, cela mettra le mot de passeChangedAt une seconde dans le
‫passé, d'accord, ce qui ne sera bien sûr
‫pas précis à 100%, mais ce n'est pas du tout
‫un problème, car une seconde ici ne fait aucune différence.
‫C'est un petit hack, mais encore une fois, ce n'est pas un problème.
‫Ainsi, mettre ce mot de passeChanged une seconde dans le passé garantira alors
‫que le jeton est toujours créé après que
‫le mot de passe a été modifié.
‫Donc, cela fonctionne maintenant,
‫mais comme toujours, testons-le également rapidement.
‫Bon, revenons à Postman.
‫Faisons un nouveau mot de passe de réinitialisation, ou en fait,
‫ce n'est pas du tout ce que
‫je voulais, mais c'est une bonne chose de
‫voir que le code fonctionne réellement.
‫Donc, le jeton n'est pas valide ou a expiré, et c'est
‫parce que, eh bien, 10 minutes se
‫sont écoulées depuis que j'ai réellement créé ce jeton.
‫Et je pense que nous n'avions pas encore
‫testé cela, et c'est donc bien que cela l'ait fait accidentellement maintenant.
‫Encore une fois, cela vient, au cas où vous vous demanderiez ce
‫qui s'est passé,
‫alors c'est bien sûr ce message d'erreur ici.
‫Et cela signifie donc qu'il n'a trouvé aucun utilisateur
‫qui possède ce jeton ou qui a un jeton qui remonte
‫à plus de 10 minutes.
‫Et donc, en effet, ce que je voulais faire, c'est oublier le mot de passe.
‫Alors, attendons-le.
‫Donc 8. 6 secondes,
‫mais c'est peut-être à cause de ma connexion Internet.
‫Donc, si vous l'exécutez sur un
‫serveur, ce sera probablement beaucoup plus rapide.
‫Alors récupérons cela ici, revenons à Postman, et maintenant nous réinitialisons
‫le mot de passe, encore une fois avec ce mot de passe, peu
‫importe s'il est le même que l'ancien.
‫Et, maintenant nous avons notre succès ici.
‫Et maintenant, revenons à Compass.
‫Rechargeons, et en effet, nous obtenons le mot de passeChangedAt.
‫Et c'est en fait maintenant.
‫Et donc, si nous essayions maintenant d'utiliser réellement ce jeton, par exemple,
‫pour accéder à cette route protégée, alors cela
‫devrait fonctionner à cause de cette
‫petite balise d'une seconde que nous avons faite.
‫Donc, c'est fait et juste comme ça, nous avons maintenant terminé notre
‫fonctionnalité de réinitialisation de mot de passe.
‫C'était donc pas mal de code, mais bien sûr, ça en
‫vaut vraiment la peine.
‫Donc, vous devriez toujours proposer cette
‫fonctionnalité dans votre application web, car sinon,
‫un utilisateur qui oublie son mot de passe est complètement foutu,
‫il ne peut plus utiliser votre application,
‫et donc, c'est bien sûr une pratique terrible.
‫Quoi qu'il en soit, ce
‫genre de finitions, déjà, la partie authentification et autorisation de cette section.
‫Donc encore une fois, c'est assez complet
‫et je me suis beaucoup amusé à le mettre en œuvre.
‫Donc, cette partie pour moi est l'endroit où les applications
‫Web commencent vraiment à prendre vie.
‫Je sais que ce n'est pas vraiment visible à
‫ce stade, avec tout cela, juste des jetons et copier
‫des jetons et les coller ailleurs.
‫Ce n'est pas l'idée habituelle que nous avons de nous connecter,
‫je sais, mais bien sûr encore, un peu plus
‫tard, lorsque nous commencerons enfin à créer le site Web
‫dynamique, nous continuerons
‫bien sûr à utiliser cette authentification que
‫nous venons de créer et puis deviendra également visuel sur ce site Web dynamique.
‫Ensuite, nous allons implémenter des fonctionnalités pour mettre à
‫jour l'utilisateur et également le supprimer,
‫et après cela, nous parlerons également de sécurité.
‫C'est donc ce qui vous attend pour le reste de la
‫section, alors assurez-vous de ne pas manquer cela.