Now that you had a chance to mess around with be corrupt in isolation we're going to actually integrate
password hashing in to the task manager API.
Now there are two main places where plaintext passwords are provided to our application and we can explore
both by heading over to the user router.
The first and the most obvious is right here.
Post users this is when a user is created and it's impossible to create a user without providing a password.
So when someone is signing up we definitely want to make sure to hash that plaintext password.
The next one is down below.
When a user is updated now they could be updated and have the password stay the same.
Then maybe just update the name.
But there is a chance that a new password is provided and if it is provided we want to make sure we
hash it.
So these are the two places where plain text passwords might be provided to our application.
Now we're actually not going to change the code in here at all.
Instead what we're going to do is customize the User model Mongoose supports what's known as middleware
middleware as a way to customize the behavior of your Mongoose model and it's going to allow us to do
some pretty interesting things to explore this.
Let's head over to the Mongoose documentation and under guides we have a middleware with middleware
we can register some functions to run before or after given events occur.
So for example validate.
I could run some code just before or just after a user is validated.
I could also run some code just before or just after a user is saved and we have other events down below
as well.
In our case we want to focus on Save Our job is to run some code just before a user is saved.
What are we going to do.
We're gonna check if there's a plain text password and if there is we'll go ahead and hash it.
Now to actually get this done we'll need to head over to the user model and do a little bit of restructuring
to take advantage of this more advanced feature.
When we create a mongoose model we're passing an object in as the second argument to model that's the
object that starts right here and runs all the way down to the end of our attribute definitions.
Now when we pass an object in as that second argument behind the scenes Mongoose converts it into what's
known as a schema in order to take advantage of the middleware functionality.
All we have to do is create this schema first and pass that in.
It's going to require about one line of additional code to get done but once it's done we'll be able
to use more advanced features.
So right here what we're going to do is create a constant called User schema and we're going to set
it up by using the new operator with the following Mongoose dot capital S schema and we're going to
pass to it an object which defines all of the properties for that schema.
Now that's just the object we have down below so we'll take this object.
The second argument to model I'll highlight the entire thing.
Cut it out of its current location and paste it right here as the first and only argument to the schema
method.
Now when I do that we have access to the user schema and all we're going to do is pass that in as the
second argument to model.
So if we were to pass an object in Mongoose was doing that behind the scenes anyways in this case we
are creating a separate schema and a separate model.
And this is going to allow us to take advantage of middleware.
So right here what we're going to do is use a method on user schema to set the middleware up.
That's user schema dot and there are two methods accessible to us for middleware.
We have three for doing something before an event like before validation or before saving.
And we have a post for doing something just after an event such as after the user has been saved.
In our case we want to do something before.
So we'll be using pre and we passed to two arguments.
The first is the name of the event that would be save in our case.
And the second is the function to run right here.
This needs to be a standard function not an arrow function because the this binding plays an important
role and as we know arrow functions don't bind this.
Now I will be using async await inside of here so we can go ahead and set that up.
And we also get access to an argument which I'll talk about in a moment called next.
For now let's just go ahead and name it.
So inside of here we have access to the value on this which is equal to the that's being saved.
So right here we're saying I want to do something before users are saved.
This gives us access to the individual user that's about to be saved.
Now it's kind of annoying to reference this throughout our function.
So what I'll typically do though it's not required is create a new variable cost.
User equals this.
Now I can access user down below which is a bit easier to understand.
Now let's take a quick moment to talk about next.
The whole point of this is to run some code before a user is saved.
But how does it know when we're done running our code.
Now it could just say when the function is over.
But that wouldn't account for any asynchronous process which might be occurring.
So that's why next is provided.
We simply call next when we're done right here.
I'm going to call next at the end of the function.
Now if we never call next.
It's just going to hang forever.
Thinking that we're still running some code before we save the user and it will never actually save
the user.
So it's really important to make sure that next gets called now right here in between.
This is where we want to go ahead and hash the password before we do anything like that.
Let's go ahead and just use console dot log to print a message just before saving what we're gonna do
is fire off some operations from postman and watch that message printed in the terminal down below with
the programs saved.
I'll head over to the postman and we're going to start by going over to the create user request.
This is one of the two places where we can provide a plain text password and if I fire things off we
can see what we get in the terminal.
So first off will note that things did finish.
We have the data down below and our 2 0 1 status and over inside a Visual Studio code what do I get
I get just before saving printing and on user I could access the various pieces of data that were provided
for the user such as the name email or password and we'll do that in just a moment.
Now currently things are not going to work if we were to try to update the user so I just created this
brand new user.
Let's grab their I.D. and try to update something I'm going to take the I.D..
Bring it over to the update user request and paste it in.
Up above from there we're going to change one of the attributes and I can leave it as it currently is
where I'm changing the name to Jess.
Now if I fire that off what are we going to see down below.
Things work.
The name is now Jess and everything else looks as expected.
The problem is that over in Visual Studio code we didn't get our message a second time so certain Mongoose
queries bypassed more advanced features like middleware which means that if we want to use them consistently
we just have to do a tiny bit of restructuring.
So all we're going to do is head over to that update route in the router file.
I have that just down below and we're going to make a small change a change to how we perform what's
done on this line.
The actual update process the find by I.D. and update method bypasses mongoose.
It performs a direct operation on the database.
That's why we even had to set a special option for running the validators.
We can go ahead and do it.
The more traditional Mongoose way to make sure that our middleware runs correctly we're going to replace
one line of code with three lines of code.
So not a big change.
The first thing we need to do is create a variable const user.
And what we're going to do is find that user by I.D. so it still starts off in a very similar way.
I'll use a wait with user dot find by I.D. And right here we just passed that I.D. in exactly as we
had it before request dot pyramids dot I.D. which was the value provided right here.
Now we have access to the user.
So we have access to an instance of our user model and it's time to update the properties that are actually
being changed.
So for example changing the name would be me setting user dot name equal to the new value like something
else.
Now in this case we can't hard code us updating those properties because it could be a different set
of updates every single time.
So all we're going to do is iterate over our updates array the list of updates that are actually being
applied and will apply them right here.
We will use updates which is an array dot for each.
To get that done.
So we provide our callback function which gets called one time for each update.
We're trying to apply and we have access to the individual update field right here.
Remember updates was an array of strings.
So what we're getting right here is a string.
It would be something like name email password or whatever field they're trying to update.
Now what we need to do is correctly set the property on user for that we'll use bracket notation.
So here I'm updating a property on user but it's dynamic so I can't type it out like name because I
don't know what it is.
It's stored on that update variable.
So we'll use bracket notation to access a property dynamically in this case we're accessing the property
whose name it comes from the update variable.
Now we're going to set that equal to the value the user passed in so request body.
And once again we can't use dot notation because we would have to explicitly know the property and it's
going to change.
So we'll use bracket notation like the following.
This is going to achieve the exact same thing but it's dynamic.
So if the values they're updating and the Keys they're updating change it'll keep working over time.
Now we could always take advantage of these shorthand syntax by grabbing this expression and taking
it up above removing the curly braces and pasting it in.
And the last thing we do is something we've done plenty of times before.
I just await a call to user dot save.
And this is where our middleware is actually going to get executed now that we have this in place.
Let's go ahead and actually make sure we get our message printing.
So over inside of postmen I'm going to run the exact same update operation though let's change the name.
I'll change it from Jess to Jessica I'll fire it off down below it does indeed work.
And the name has indeed changed correctly but over inside a visual studio code we're now getting our
middleware running.
So this is a small adjustment that's required in order to get middleware to consistently run.
Now that it is consistently running we'll do what we actually want to do which is to hash the password.
And that's going to happen right here.
Now the first thing we want to do is make sure the password is actually being changed if the password
is already hashed.
We don't want to hash it again.
We only want to hash the password if it's been modified by the user and Mongoose gives us a really easy
way to figure out if that's the case.
So what I'll be doing is setting up an if condition in here.
This is where we'll hash the password but only under the following condition if the user has a modified
password property user dot is modified as the method we can use and we pass in the name of the field
we're looking for.
In this case we're looking to see if password was modified.
This will be true when the user is first created.
And it will also be true if the user is being updated and password was one of the things changed in
here.
We can actually hash the password and we've done that before.
I'll be setting user dot password equal to a hash I'll use a weight with the following that is B crypt
dot hash not ash.
And we're going to pass in those two arguments.
The first is the thing to hash.
I have that on user dot password.
And the second is the number of rounds and once again I'll use eight.
So here we're taking the plain text password and hashing it then using that hash value to override the
plaintext value.
Now with this in place the only other thing we need to change in this file is to import B correct.
So up above.
Let's get that done before saving and running it a new constant B script equals.
We'll use require grabbing the B script J.S. NPM module.
Now we can actually test things out and make sure the password is getting hashed so over inside of robo
three T in the user's collection.
I have a few different users all with plain text passwords.
Let's create a fifth one right here.
I'll be heading over to create user and I'll be firing off the exact same request I fired off before.
Still providing the plaintext password when I send that off.
What do I get.
I get a two 0 1 and down below I can see not a plain text password but a hashed one.
Now for the moment we're still going to send the password back as part of the response.
Later on we'll fix that locking down the password even more.
The goal for now though is to just hash it and that's what we have now over in the database.
We should see the hash value showing up here as well.
And if I refresh the collection that is exactly what I have.
That last user document does indeed have a password but it is the hashed one we saw before.
Now let's go ahead and update the user to make sure that works too.
So I already have their I.D. of that old user as part of the request for update user over here we found
that a few moments ago what I'll do is change the name back to Andrew but I'm also going to change that
password right here I'll set a new value for password and I'll make the value something like test one
two three and then I'll use some special characters.
Now if I go ahead and fire that off what are we going to see down below.
I can see that things did work and we now have a hashed password value.
So when someone creates a new user or when they update their existing user if a plain text password
is provided it is indeed going to get hashed and middleware allows us to enforce that without having
to add this password hashing logic into multiple places like the two routes that are actually related
to this situation.
We just provide it once and it works everywhere.
So that's where we're going to stop for this one.
But before we go there is a quick challenge I'd like you to work through.
Your job is going to be to make a change to the task router.
We have our post.
Excuse me.
Patch request right here for updating a task and what I'd like you to do is alter the logic we use for
updating it.
Now currently we're not using any middleware for task but if we were to it would be nice to use methods
that make sure that middleware actually runs.
So we'll be doing the same thing here that we did for user.
So your goal is to change how tasks are updated.
Step 1 instead of using find by I.D. and update you will first find the task then you'll use for each
to apply all of the updates onto the task.
Finally you'll save it and then you'll test your work from postmen make an update to an existing task
and ensure that it gets applied correctly.
So take some time to knock that out.
Feel free to use what we did with the user router as a reference and when you're done come back and
click play
how that go.
Let's go ahead and kick things off together down below we have the one line we had before.
I'm going to comment that out and instead we'll go ahead and do things the new way.
So first stop is defined that task.
I will create a new task variable much like we did for the user router over here and I'll use a wait
with task Dot.
Find a buy I.D. passing the I.D. in exactly like we had done before.
Next up we want to apply those updates.
I'll be using updates dot for each to loop over them we'll get access to the individual update and like
we did before.
I'll be setting the property on task equal to the value they passed in request that body using bracket
notation to get it dynamically.
And the only other thing we needed to do was make sure to save the individual task so a weight task
dot save.
And we're done.
I'll remove the old line down below.
I'm going to save the file.
Actually I'll also remove the challenge comments up above now I'll save the file and we can test our
work to make sure that updates still function.
The first thing I'm going to do is get a task I.D..
So right here I will read all of the tasks I have a single one I'll grab its I.D. and I'll change the
completed value from true to false.
I'll then head over to update task.
I'll put that I.D. right inside of the U.R.L. and I will switch it from true over to false.
So I'm only changing the completed property now if I go ahead and send that off down below I do get
my 200 status code which is fantastic.
And any response body I can see my updated task and everything looks correct.
The other fields haven't been altered and the one that I did update did indeed change meaning that our
code is still working even though we've refactor it.
That's where we're gonna stop for this video in the next video we're going to continue on talking about
authentication and we'll move on to the next topic.
We're going to cover which is the concept of logging a user in.
We'll figure out how we're going to get that done.
Let's go ahead and jump right in.