In the last video you set up Mongoose to hash user passwords in this video.
You're gonna provide users with a brand new endpoint that will allow them to log in with their existing
account so they'll have to provide their credentials their email and password and it will be the job
of that route to verify that there is a user with those credentials.
So to get started we'll be setting up the new route in the user router and we'll also be working with
the User model to setup a reusable function for finding a user by their credentials by their email and
password.
To start though let's head over to the user router and create our brand new route.
I'm gonna add this one just below the route for signing up and then this one will also use post so router
dot post we will have our route followed by our function.
I'll define that as an async function with request and response.
And this path is going to be the following forward slash users forward slash log in.
This is the end point you'll use to log in with your existing account.
Now it's the job of this function to find a user by those credentials by the email and the password.
There are two ways we could structure this.
The first way would be to add all of that code right inside of this function.
We find a user by their email and then we go ahead and use that B crypt compare method to compare the
plaintext password provided with the hash stored in the database.
The alternative option would be to create a reusable function that does all of that for us and that's
the approach that we're gonna take.
So right here what are we going to do.
We're gonna start off with a try catch block and when we catch an error we'll send back an error but
we'll focus on that a little bit later inside of try what we're going to do is call a new method.
We're gonna be setting up.
So right here const user we're gonna get a user back.
We're going to use a wait for this async operation and we're going to call user dot and we're going
to call something that will define in just a moment.
So right here.
Well we do have pre-built in options.
We can also define our own.
I'm going to create one called Find by credentials.
This is going to take in the email and the password and it'll try to find a user by the email it will
verify the password.
It'll either return the user or it won't.
So right here let's go ahead and knock that out.
What I'm gonna do is pass in the email and the password both of which will be provided as the body of
the request.
So that would be a request dot body dot email as the first argument and request dot body dot password
as the second.
Now let's go ahead and actually create this function.
So this code works as expected.
This happens over in the user model.
And once again it's only something we can do when we create a separate schema first then pass that into
the model.
If I was still passing an object in as the second argument right here we wouldn't be able to do what
we're about to do.
So right here for the code we wrote before I'll leave a little comment hash the plain text password
before saving then up above we're gonna attach something else to user schema.
It's gonna be user schema dot statics dot the method name.
In this case I'm choosing to create defined by credentials but I could call this anything that I want
to.
So by setting up a value on user schema dot statics we're setting that up as something we can access
directly on the model.
Once we actually have access to it.
So right here user defined by credentials will call whatever is defined on user schema dot statics defined
by credentials.
Now we're going gonna set this up as a function and we'll set it up as an async function.
It's going to accept those two arguments that we chose to provide.
Right here we can indeed use an arrow function as the this binding isn't going to play a role and we're
gonna have our arguments the email as the first one and the password as the second one.
Now it's our job to attempt to find the user by those pieces of information we want to start by finding
them by the email we can't find them by the email and password because we have the plain text password.
But the hashed password is stored in the database.
So we'll find them by the email first.
Then separately will verify that password to get started.
Let's go ahead and find the user by the email.
So const user and then we'll use a weight with user dot find one find one is going to return a single
user and it's similar to find by I.D. though instead of providing the I.D. we provide an object with
our search criteria.
In this case we're looking for a user whose email equals the argument value passed in for email.
Now in this case we can just use the shorthand syntax to set that up.
Now if there is no user that means there was no user with that email and we want to go ahead and throw
a new error.
So right here the condition if there is no user what are we gonna do.
We will use throw new error which will immediately end the execution of this function and we will throw
the following error unable to log in.
So here we're saying let's try to find that user by the email if that doesn't work then obviously we
can't log in.
Now if that does work the next thing we want to do is verify that password using the compare function
we've used before.
So const right here is match and we will set that equal to we'll use a wait with B script dot compare
which we have used before we pass in as the first argument the plain text password and as the second
the hash.
Now we know we found a user otherwise this code wouldn't run and we have user dot password which stores
the password.
Now if it's not a match that's a problem too.
So if the opposite of is match.
So here we are checking if it's not a match if it's not a match.
This code will run and we'll go ahead and throw a new error to stop the function execution and signal
that the log process didn't work.
Once again unable to log in when providing error messages for things like logging in it's best to not
be too specific.
You don't want to say something like that.
Emails registered but your password is wrong because it exposes more information than you might want
to someone who's trying to gain access to the account now knows more than they otherwise should.
In general it's best to provide a single error saying the operation didn't work in this case unable
to log in is nice and generic.
Now the last thing we want to do is actually return to the user if they were found and the password
is a match right here.
Return user.
Now with this in place we can actually finish filling out the root over here in the user router.
Now on the very next line if an error didn't get thrown we would have access to the user and all we're
gonna do for the moment is send them back.
So response dot dots end user.
We're not actually going to worry about providing them with details that they can use to perform other
privileged operations later.
So it's not like we're logging in long term we're really just logging in in order to see our own user
profile.
But once again we're going to build off of this to create the complete system down below in catch.
I'll use response dot status to set up something like a four hundred and I'll use send sending back
no content the 400 alone is enough of a signal to let them know that logging in did not work.
Now that we have this in place we can actually test things out but there is a problem.
One of the most basic things you should set up when creating a login system is a restriction on the
email.
If a user already has an account registered with a specific email a another user should not be able
to come along and use that same email to register an account again.
So before we even try to log in let's go ahead and fix that by adding a single line over here to the
Mongoose schema under email.
We're going to add a brand new property which can go anywhere you like.
It is unique and we're going to set it equal to true.
Now when we do this it's going to create an index in our Mongo DB database to guarantee uniqueness similar
to how our ideas are unique are emails for users.
Now need to be unique as well.
Now here's the catch.
In order to get this to work we have to wipe the database and have it get re created.
So the index can be set up if you tried to create users right now even though this code is in place
you'd be able to create users with these same emails.
So to start I'll be heading over to robo three TI and we're gonna go ahead and just completely drop
our database.
So I'll right click the database.
Yes I do have other databases that I've created for development purposes.
Right now though you want to delete the one that you've been working with.
We're gonna right click it go down to drop database and once it's dropped we can go ahead and just refresh
things in the application.
Getting it to restart by saving any of the files.
Now we're in a position where users shouldn't be able to create an account with an email if it's already
registered and log in should always work as expected.
Let's head over to postman and test all of this out.
So over here I have the response data for the last time I created a user.
But they have indeed been deleted.
Let's go ahead and create our first user with a name email and password.
I already have in place.
I'm gonna send that off and down below I get a 2 0 1 which is great.
There is no user with that email since I just deleted everyone.
We have the user created with their hashed password.
All I'm going to do is file your off the exact same request again.
This time though it should fail and that is exactly what's happening down here we can see we have a
duplicate key error.
The email is already registered and that's a problem.
So now that we can not sign up with the same email twice let's go ahead and mess around with the log
in and request wipes.
I don't want to create a new collection.
I want to add a new request to my existing collection.
I'll call that log in user.
I'm going to save it and then I'm going to take it and I'll move it up above just below create user.
So we're keeping things nice and organized by what they do.
Next up I'm going to click on that and that's going to bring us to our brand new request where we'll
set things up.
We have post we have forward slash excuse me we start off with local host on three thousand then forward
slash users forward slash log in.
Now we do have to provide our credentials as well.
That is part of the request body so body raw and we switch from text over to Jason and then down below
we provide the email and we're also going to provide the password.
The two things needed to log in correctly.
Let's go ahead and actually test this out with the credentials that we just used to create that user.
So it's Andrew at example dot com and red.
One two three four five exclamation mark for me over here.
Email Andrew at example dot com password Red One two three four five exclamation mark.
I send this off and down below what do I get.
I get a two hundred and I see my user profile being sent back correctly which is excellent from here
we can go ahead and try to mess up the data.
So for example if I try to use two exclamation marks in the password I would expect things to fail and
we're getting our four hundred down below.
If I fix the password but mess up the email adding in two A's I would expect that to fail as well.
I send that off and down below I'm still getting a four hundred.
Only when the email and the password perfectly match the user information do we get a two hundred.
And do we get our user profile back.
Now this is not the complete process for logging in but it is a great step in the right direction.
We have the new route set up and the new route knows how to verify the user's credentials making sure
there's actually a user that has that email and password.
We're going to continue to build off of this in the next video.
So let's go ahead and jump right into that.