Afrikaans
Akan
Albanian
Amharic
Armenian
Azerbaijani
Basque
Belarusian
Bemba
Bengali
Bihari
Bosnian
Breton
Bulgarian
Cambodian
Catalan
Cebuano
Cherokee
Chichewa
Chinese (Simplified)
Chinese (Traditional)
Corsican
Croatian
Czech
Danish
Dutch
English
Esperanto
Estonian
Ewe
Faroese
Filipino
Finnish
French
Frisian
Ga
Galician
Georgian
German
Greek
Guarani
Gujarati
Haitian Creole
Hausa
Hawaiian
Hebrew
Hindi
Hmong
Hungarian
Icelandic
Igbo
Indonesian
Interlingua
Irish
Italian
Japanese
Javanese
Kannada
Kazakh
Kinyarwanda
Kirundi
Kongo
Korean
Krio (Sierra Leone)
Kurdish
Kurdish (Soranรฎ)
Kyrgyz
Laothian
Latin
Latvian
Lingala
Lithuanian
Lozi
Luganda
Luo
Luxembourgish
Macedonian
Malagasy
Malay
Malayalam
Maltese
Maori
Marathi
Mauritian Creole
Moldavian
Mongolian
Myanmar (Burmese)
Montenegrin
Nepali
Nigerian Pidgin
Northern Sotho
Norwegian
Norwegian (Nynorsk)
Occitan
Oriya
Oromo
Pashto
Polish
Portuguese (Brazil)
Portuguese (Portugal)
Punjabi
Quechua
Romanian
Romansh
Runyakitara
Russian
Samoan
Scots Gaelic
Serbian
Serbo-Croatian
Sesotho
Setswana
Seychellois Creole
Shona
Sindhi
Sinhalese
Slovak
Slovenian
Somali
Spanish
Spanish (Latin American)
Sundanese
Swahili
Swedish
Tajik
Tamil
Tatar
Telugu
Thai
Tigrinya
Tonga
Tshiluba
Tumbuka
Turkish
Turkmen
Twi
Uighur
Ukrainian
Urdu
Uzbek
Vietnamese
Welsh
Wolof
Xhosa
Yiddish
Yoruba
Zulu
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.
Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.