‫And now let's finally create the last part
‫of the Password Reset Functionality,
‫where we actually set the new password for the user.
‫And so, just like before, let's start by defining the steps
‫that we're gonna take for this resetPassword flow.
‫So, first off, get user based on the token.
‫Then as the second step, we will set the new password
‫but only if token has not expired,
‫and there is a user.
‫So in that case, set the new password.
‫Then after that, we need to update
‫the changedPasswordAt property
‫for the current user,
‫and then finally, as is usual in this functionality,
‫is to log the user in.
‫Basically, send the JSON Web Token to the client.
‫Okay, so a lot of work to do, and so let's get started.
‫And so, remember from the last video, that the reset token
‫that is actually sent in the URL
‫is this non-encrypted token here.
‫So, actually this one.
‫But the one that we have in the database
‫is the encrypted one.
‫So we talked about that before, and so what we now
‫need to do, is to basically encrypt
‫the original token again,
‫so we can then compare it with the one that is stored,
‫so the encrypted one in the database.
‫So, we actually did something similar before
‫with the password, but with the password,
‫we couldn't compare it as easily as we can with this one,
‫again because for the password
‫we used the quite complex bcrypt algorithm,
‫which in this case, we didn't.
‫So, here it's very straightforward.
‫All we need to do again, is to encrypt the token
‫and compare it with the encrypted one in the database.
‫So, let's say hashedToken, and so we will actually now
‫need the crypto package here as well.
‫Const crypto and then require('crypto').
‫Now right.
‫Then let's go back, and so,
‫we use crypto.createHash.
‫Remember, then the name of the algorithm again, sha256,
‫then .update, basically for the place, for the string
‫that we want to hash.
‫And so, that one is, remember in req.params.
‫So we then use this one for a long time .token.
‫And so again, it is a parameter,
‫because we specified so here in the URL, so like this.
‫So, it's now a parameter called token.
‫And so, of course, here it's req.params.token.
‫And then, finally, we also need to say digest
‫and convert it to hexadecimal.
‫Now this is basically the same
‫as we had before, where we encrypted the original one,
‫and so we could refactor this into its own function,
‫but let's just keep it simple here.
‫So, now let's actually get the user based on this token.
‫Because that is actually, the only thing
‫that we know about the user right now.
‫We have no email, we have nothing, so this token
‫is the only thing that can identify the user.
‫And so we can now, basically, query the database
‫for this token.
‫And it will then find the user which has this token.
‫So, await, as we already know, and then User.findOne.
‫So, that property is called passwordResetToken
‫and we are looking for the hashedToken.
‫And now of course, we need to declare it as async
‫and prep it into catchAsync.
‫Give it a save, that should fix this bug,
‫and indeed it does.
‫So, this will find user who has the token
‫that will send via URL.
‫But, right now, we're not taking
‫the token expiration date into consideration.
‫And so how could we do that?
‫Well, basically, what we want is to check
‫if the passwordResetExpires property
‫is greater than right now.
‫Because if the expires date is greater than now,
‫it means it's in the future, which in turn means,
‫that it hasn't yet expired.
‫And so, that's a very easy way
‫in which we can actually do this right with this query.
‫So, passwordResetExpires,
‫which is where that date is stored,
‫and now all we need to check
‫if it is actually greater than right now.
‫And so we know how to do that already with MongoDB, right?
‫So, new object and then the greater operator
‫and then what we want to compare it with is Date.now,
‫and this will actually be a timestamp of right now,
‫but behind the scenes, MongoDB will then convert everything
‫to the same, and therefore be able
‫to compare them accurately.
‫And so with this we can, at the same time,
‫find the user for the token and also check if the token
‫has not yet expired.
‫So, great.
‫So, next up we want to, of course, send an error
‫if there is no user, or basically, if the token has expired.
‫But that's, in this case, the same,
‫because if the token has expired, well then it will simply
‫not return any user.
‫And so all we need to do is to say, if no user,
‫well then, as always, return next, that's not mext.
‫So new AppError, and let's say
‫Token is invalid or has expired.
‫And then 400, so bad request.
‫And so then, if there is no error,
‫and if next is not called,
‫well then let's actually set the password.
‫So, we already got the user and now it's very simple:
‫user.password is equal to req.body.password.
‫And that's because we will of course, send the password
‫and also passwordConfirm via the body.
‫So let's duplicate that
‫and passwordConfirm as well.
‫And then also, let's basically delete the reset token
‫and the expired.
‫So passwordResetToken, so just like we did before,
‫we set it to undefined, and now user.password expires
‫equals to undefined.
‫All right.
‫And again, of course, we now need to save it,
‫because this only modifies the document,
‫it doesn't really update.
‫So it doesn't really save it.
‫So, await user.save.
‫And in this case, we actually don't have to turn off
‫the validators, because indeed we want to validate.
‫For example, we want the validator to confirm
‫if the password is equal to passwordConfirm.
‫And so that validator automatically
‫does all that work for us.
‫Then the third step, what we're gonna do actually
‫in the end, and so what we're gonna do next
‫is to basically lock the user in.
‫So in other words, send the JSON Web Token.
‫And let's get that code from here, so this one.
‫And again, we're already doing this here
‫in three different places.
‫So here in the login, also in signup,
‫and now for the third time, down here.
‫And so, sometime in the future, we will refactor that
‫into its own function.
‫But for now, we're good like this.
‫And so, let's actually now go ahead and test this.
‫So this reset token that we had before
‫has already expired, and so we need to ask
‫for a new one, basically.
‫So let's come to Postman and hit our forget password route.
‫Let's just reduce the clutter here
‫and get rid of all of these open tabs
‫that we no longer need.
‫Actually here we're gonna need this test
‫for this Reset Password,
‫because remember, that this one actually gets back
‫a JSON Web Token, and so we want to save that
‫into the environment variable,
‫just like we did with all the others.
‫So I'm doing that now, just so that I don't forget it.
‫All right, anyway, let's start with, basically,
‫forgetting the password.
‫So sending that request out, which again,
‫takes some times because of sending the email,
‫but here we go, and let's now go to our email,
‫and so that just arrived a few seconds ago.
‫So, it is this, of course, this token.
‫So let's grab it, copy it and now back to Postman,
‫we use it in the Reset Password, as the URL.
‫Okay, make sense?
‫So again, we're sending that token right in the URL.
‫Then here, let's specify the body,
‫because now, we need to actually specify our new password.
‫So password and let's say newpass.
‫And then...
‫And here let's call it something else,
‫because for now, I actually want to see an error.
‫And of course, this is called passwordConfirm.
‫So let's see what happens when we try to do this.
‫Let's wait for it, and we get password is shorter
‫than the minimum allowed length.
‫Okay, so let's change that, 123, and here let's say 1234.
‫So I want them to be different.
‫But you see that the validation here worked just fine,
‫even when updating the password with save.
‫So, and now we get Passwords are not the same!
‫So again, that's a validation error.
‫And remember, actually, that this is the whole reason
‫why we need to use save and not update.
‫So before, for updating tours,
‫we used to use findOneAndUpdate, but now,
‫for everything related to passwords and to the user,
‫we always use save, because we always want to run
‫all the validators, and above all,
‫the save middleware functions.
‫So, for example, the ones where the passwords are encrypted.
‫So let's end it now.
‫Oh, I didn't actually correct it, sorry for that.
‫And but now it should actually work.
‫And indeed, we get success, and we get a new token.
‫So great, let's see if this token is actually valid.
‫So if we can, get all the tours using this brand new token.
‫And here we go.
‫So, our new token actually works, and now for this user,
‫so for hello@jonas, these two properties
‫should actually be gone.
‫So the password expires and the token should be gone,
‫since, well, since that's what we did in our code.
‫And so, yeah, they are no longer here.
‫Now all we need to do actually, is this missing step here,
‫which is to update the passwordAt property
‫for this current user.
‫But that shouldn't be all too hard,
‫and so let's quickly go back to the userModel,
‫which is where we are gonna do that using middleware.
‫And let's actually put all the middleware
‫together here at the top.
‫So, userSchema.pre and again dot save,
‫and then a function with next.
‫Again, this function here is gonna run
‫right before a new document is actually saved.
‫And so, it's the perfect place
‫for actually specifying this property.
‫And I could, of course, have done it in a controller
‫right next to here to this code, for example.
‫But I really want this to happen, kind of, automatically.
‫So, kind of behind the scenes.
‫Because later on, we will have another place
‫where we update the password and then we would make sure
‫that we're including the same code there.
‫And like this, again, it happens,
‫kind of, behind the scenes,
‫without us having to worry about it at all.
‫Now, when exactly do we actually want to set the
‫passwordChangedAt property to right now?
‫Well we only want it when we actually modified
‫the password property.
‫And I'm not sure if we used this trick before,
‫but anyway, let's use it now.
‫So if we have not modified, so if not this.isModified,
‫so just like this,
‫and then the name of the property, so password.
‫So in that case, return right away
‫and run the next middleware.
‫Okay, not like this, but like this.
‫So again, if we didn't modify the password property,
‫well then of course,
‫do not manipulate the passwordChangedAt.
‫But what about creating new document?
‫Well, when we create a new document,
‫then we did actually modify the password,
‫and then we would set the passwordChangedAt property, right?
‫Well, in the current implementation we actually would.
‫But there is something else that we can use here.
‫So, basically, we want to exit this middleware function
‫right away, if the password has not been modified
‫or if the document is new, and so we can use
‫the isNew property.
‫And again, this is one of these very nice things
‫that are learned by reading your documentation.
‫And so, I cannot stress enough how important it is
‫to really read the documentations when you need something
‫that you cannot find anywhere.
‫Because, there really is so much stuff in there
‫that is completely impossible to teach in one course.
‫Anyway, if the code passes this verification here,
‫well, then let's very simply say,
‫this.passwordChangedAt = Date.now.
‫And then, we call next.
‫Now, in theory, this should work just fine,
‫but actually, in practice,
‫sometimes a small problem happens.
‫And that problem is that sometimes saving to the database
‫is a bit slower than issuing the JSON Web Token,
‫making it so that the changed password timestamp
‫is sometimes set a bit after
‫the JSON Web Token has been created.
‫And so that will then make it so that the user
‫will not be able to log in using the new token.
‫Because, remember, the whole reason this timestamp here
‫actually exists, is so that we can compare it
‫with the timestamp on the JSON Web Token, right?
‫So, just to remember,
‫it is, well, so right here, where we check if the user
‫has changed the password after the token was issued.
‫And so, down here, where we then created this new token
‫in reset password.
‫So right here, remember, we create this new token,
‫and so again, sometimes it happens that this token
‫is created a bit before the changed password timestamp
‫has actually been created.
‫And so, we just need to fix that by subtracting one second.
‫So, basically, a thousand milliseconds.
‫And so that then will put the passwordChangedAt one second
‫in the past, okay, which will then of course,
‫not be 100% accurate, but that's not a problem at all,
‫because one second here doesn't make any difference at all.
‫It's a small hack, but again, it's no problem.
‫So putting this passwordChanged one second in the past,
‫will then ensure that the token is always created
‫after the password has been changed.
‫So, this works now, but as always,
‫let's also quickly test it.
‫Okay, so back to Postman.
‫Let's do a new Reset Password,
‫or actually, that's not what I wanted at all,
‫but it's a great thing to see
‫that the code is actually working.
‫So, The token is invalid or has expired,
‫and that's because, well, 10 minutes have passed
‫since I actually created that token.
‫And I think we hadn't yet tested this,
‫and so it's great that it now accidentally, actually did it.
‫So again, this comes, in case you're wondering
‫what the hell happened,
‫so that's of course this error message here.
‫And so it means, that it didn't find any user
‫which has this token or which has a token that is
‫more than 10 minutes in the past.
‫And so, indeed what I wanted to do, is forget password.
‫So, let's wait for it.
‫So 8.6 seconds,
‫but that might be because of my internet connection.
‫So if you run this on a server,
‫it's probably gonna be a lot faster.
‫So let's grab that here, back to Postman, and now
‫we reset the password, again with this password,
‫doesn't really matter if it's the same as the old one.
‫And, so now we have our success here.
‫And now back to Compass.
‫Let's reload, and indeed, we get the passwordChangedAt.
‫And so that is actually right now.
‫And so, if we now tried to actually use this token,
‫for example, to access this protected route,
‫well then that should work because of that small,
‫one second tag that we did.
‫So, it did and so just like this, we actually now finished
‫our Password Reset Functionality.
‫So that was quite a bit of code, but of course,
‫it's totally worth it.
‫So, you should always offer this functionality
‫in your web application, because otherwise,
‫a user that forgets his password is completely screwed,
‫they can non longer use your application,
‫and so, that's of course a terrible practice.
‫Anyway, this kind of finishes, already,
‫the authentication and authorization part of this section.
‫So again, it's quite complete
‫and I had a lot of fun implementing this.
‫So, this part for me is where web applications
‫really start coming to life.
‫I know that it's not really visible at this point,
‫with all these, just tokens and copying tokens
‫and paste them somewhere else.
‫That's not the usual idea that we have of logging in,
‫I know, but of course again, a bit later,
‫when we finally start building the dynamic website,
‫then we will of course,
‫keep using this authentication that we just built
‫and then it will also become visual on that dynamic website.
‫Next up, we will implement functionality for updating
‫the user and also deleting it,
‫and after that, we will then also talk about security.
‫So that's what's ahead for the rest of the section,
‫so make sure not to miss that.