So we did now implement user signup.
The next step now is to implement user login
so that users can submit their login form
and you then, well, do what?
Well, that's the big question.
How do we now continue with our authentication?
Well, in the end, when a user logs in
and entered an email and password on the server,
we wanna take that entered data and verify it.
We wanna check if we find a user
with that email address in our database,
because if we don't,
the user certainly won't be able to log in
because there is no account with that email address.
And if we found an entry in the database,
then of course we wanna check if the password was correct
because just having the email address alone
is, of course, not enough.
So now here, we got this login page
and this login form, when it is submitted,
leads to a post request being sent to /login.
And that post request contains the email and the password.
Here, I don't have a confirm email field
because this is for logging in anyways.
We are not creating an account here.
We don't need to check for typos here
because if you have a typo in your email or password,
the login will fail anyways,
so there's no need to add an extra check
whether you have a typo in the email or not.
So therefore now here in demo.js, in the login post route,
we now want to see if we find a user
for the provided credentials,
so for the provided email and password.
Hence, just as before in the signup route here,
we can start by extracting some data
from the incoming request body, so I'll copy this here.
I won't extract confirm email because as I mentioned,
this doesn't exist and matter here,
but I do extract the password and email.
And now the first step is to check
whether we have a user for the given email.
Hence, we wanna check if we got an existing user.
And for this, we await the result of reaching out
to our database, to the user's collection here.
And in there, we try to find one user
where the email should be equal to our entered email,
enteredEmail, like this.
So that's our logic for checking if we have a user
with that email address in our database.
If we don't have a user with that email in the database,
findOne will return null or undefined.
So it will not cause an error,
but it also won't return a result.
So we can add a if check and check if not existing user,
so if data is falsey
which it would be if findOne does return null or undefined
because those values are treated as falsey.
So if you don't have an existing user,
that's what we're checking for here,
then, of course, we can't log the user in.
So in this case, I'll return to avoid that any other code
thereafter gets executed.
And I'll redirect back to the login page like this
so that the user is able to try again.
And here for the moment,
I'll just console.log could not log in.
Of course, the user won't see that,
but it's a little info log for us as a developer
whilst we are working on this website.
Now let's assume we made it past this if check though.
That means that we do have a user with that email address,
but of course, then we still have to check if the password
that was entered is correct.
And for this, we can again use the bcrypt package
because keep in mind
that the password in the database is hashed.
It looks something like this.
Of course, the user hasn't entered this password.
The user entered his or her original password.
So now we need a way of finding out
if that unhashed password is equal to this hashed password.
Now, as I mentioned, the hashed password can't be decoded,
but the bcrypt package,
which used an internal algorithm for creating the hash,
is able to take a raw unhashed string
and find out if that string could lead
to that hash we have here
if the same algorithm would be used on it.
So the bcrypt package is able to compare
unhashed values to hashed values
because it knows how it did hash the value.
And hence here we can use bcrypt again
and use the built-in compare function,
which does what the name implies.
And here, compare then wants two parameter values
where the first value is the unhashed value
and then the second value is the hashed value
to which you wanna compare the unhashed value.
So the first value here, the unhashed one,
is the entered password.
The second value is the password we have in the database,
which is hashed, and we find this on our existing user.
So here we can access existingUser.password
and that is the hashed password stored in the database.
Now, compare, just like hash, does return a promise.
So here, since we already have async here,
we awaited the database operation,
now we also await this comparison.
And as a result, we get a Boolean that is true
if the passwords are equal,
and that is false if they are not.
So here, I'll name it passwordsAreEqual
and store it like this.
So this is now how we can compare passwords.
And now I simply check if not passwords are equal,
which means the passwords are not equal, they are different,
in which case I will again redirect to the login page
and return so that no other code gets executed.
And again, I will say could not log in,
passwords
are not equal.
Again, this here is just an info message
for us, the developer.
Now, if we made it past this if check though,
then we know that the passwords are equal,
so the user did enter the correct password,
and therefore then we wanna grant access
to protected resources.
Now, this will be a third step
for which we then also need sessions and cookies,
but for the moment we at least know
that the user is authenticated,
and hence I'll add this info message for now.
And for the moment, I will then simply redirect to admin,
though I again wanna emphasize that at this point of time,
we don't have any protection for admin
so we could redirect to this page
no matter if the user entered correct credentials or not.
So therefore, if I now save everything here,
we should be able to go to login.
And then here, if I enter a correct email,
but an incorrect password and I click log in,
I'm redirected back to login and on the server side,
I see could not log in, passwords are not equal as expected
because I deliberately entered a wrong password.
If, on the other hand,
I enter an email that doesn't exist in the database
and then any password of my choice,
I'm also redirected back.
This time, the message is just could not log in,
which is the info message I am outputting
if I couldn't find a user.
So that's also working as expected.
But now finally, if I do enter the correct email
and the correct password for it,
then I'm redirected to the admin page
and my password manager starts and wants to save this,
which proves that we got a working
authentication pattern here.
But of course, I won't save it here.
And therefore, we succeeded.
We did go through that login route successfully.
Again, we're not finished.
We're not really managing any kind of protection
for the admin page
and we're not tracking the user's authentication status,
but we at least validated those credentials.