‫So in this lecture
‫we're gonna implement the functionality
‫of logging users in based on a given password
‫and email address.
‫And just like before, the concept of logging a user in
‫basically means to sign a JSON web token
‫and send it back to the client.
‫But in this case we only issue the token
‫in case that the user actually exists,
‫and that the password is correct.
‫So, let's start to implement that.
‫Exports.login is...
‫request, responds, and next.
‫And the first thing is we is to actually read the email
‫and the password from the body.
‫So let's say const email equals req.body.email.
‫Okay, and now eslint gives us this warning,
‫or this error actually, and it says
‫that we should use object destructuring, okay?
‫And so I let this happen here on purpose
‫just so that I can show you how we can do it
‫with ES6 destructuring.
‫And I think I talked about this before in the course,
‫and I also hope that you're already familiar
‫with the concept, but anyway,
‫since the name of this property here
‫is the same as the variable name,
‫we can simply do it like this
‫and get rid of the property name here, okay?
‫And also we want to get the password.
‫And so that would be the same,
‫so basically password equals to a request.body.password.
‫And instead of doing that,
‫we can simply do email and password.
‫And so just like this, we basically create
‫these two variables of the body object, okay?
‫So basically this is how the user
‫is gonna send in the login credentials for us to check.
‫Okay, and that check process has a couple of steps,
‫and so let's actually lay them out here
‫before we start coding.
‫So first we need to check if email
‫and passwords actually exist, all right?
‫Then number two, check if the user exists,
‫and at the same time if the password is correct.
‫And then third, if everything is okay,
‫send the token, so the JSON web token back to the client.
‫Okay, so the first one is very easy.
‫So if there is no email or no password,
‫then we want to send an error message to our client.
‫Now how are we gonna do that?
‫Well we're gonna do that using the tools
‫that we implemented right in the last section.
‫So basically our app error, remember that?
‫So we will simply create a new error here,
‫and our global error handling middleware
‫will then pick it up and send that error back to the client.
‫And so let's actually start by importing that error.
‫So remember it's this one here
‫from our AppError class, right?
‫So let's say AppError, and then require.
‫So one level up from here, then in the utilities,
‫and then appError.
‫Okay, and so we need to call the next middleware,
‫and then this is where we pass in the error, remember that?
‫So new AppError, and the message is:
‫please provide email and password.
‫And the HTTP error code is 400 for bad request, okay?
‫And now I've got some errors here,
‫and so that again is eslint helping me to fix some bugs.
‫So I see that up here, again,
‫I used it with a T instead of D,
‫and if I save it now, everything is correct.
‫Okay, and so let's actually start testing this right away.
‫And for that I will simply start
‫by creating a fake token here for now, okay.
‫And so then let's say res.status 200 for okay.
‫JSON, and then of course, as always,
‫the status set to success, and then also our token.
‫And that's actually all that we're gonna send
‫as a response to the login, okay?
‫So no user object because that's not necessary at all.
‫All we want as a response for logging in
‫is actually the token.
‫That's all that matters when the user logs in.
‫Next up, we need to implement the route,
‫so let's do that again in our user router, okay?
‫And this one is actually pretty similar to this one.
‫Let's just duplicate it and then replace signup
‫in both instances here with login, okay?
‫And again, this is only valid for a post request,
‫because of course we want to send in
‫the login credentials in the body.
‫And so again it's a post,
‫but not a get, not a patch, and not a delete,
‫because that doesn't make any sense in this case.
‫So let's test it right away.
‫I'll copy this URL here as well.
‫It's gonna be post, login,
‫and then the body of course.
‫So again, raw and JSON.
‫So let's create a simple body here.
‫All right, and so let's try to send this
‫without any password, and see what happens.
‫And indeed it works, so we get our message here:
‫please provide email and password.
‫And again, since we're in development,
‫we're getting all of this error stack here,
‫and the complete error with our status code,
‫the status, and also of course saying
‫that it is an operational error.
‫So just like we defined in the previous section, okay?
‫Now let's try the same with the password.
‫So password, set it to I think this was the one,
‫and then get rid of the email.
‫And that of course triggers the same.
‫Okay, and with everything correct that should then work,
‫and indeed we get our fake token here.
‫Now just one thing that we should do here
‫is to also return.
‫So we did that many times before,
‫and let me explain it again.
‫It's simply because after calling the next middleware,
‫we want to make sure that this login function here
‫finishes right away, all right?
‫And since we didn't have that before,
‫you now see this error here again.
‫So this is already kind of familiar at this point, right?
‫So cannot set headers after they are send to the client,
‫because again, we sent two responses.
‫First the error response, and then also this code here run,
‫which of course shouldn't have done.
‫And so again we use return here to fix that.
‫Okay, next up, let's check if there actually is a user
‫for the email that was posted.
‫So const user, and so let's now use the findOne actually,
‫because this time we're not selecting a user by the ID,
‫but instead by its email, right?
‫And so we need to pass in that filter object
‫where we can say email equal to email, okay?
‫So the field is called email,
‫and the variable is also called email.
‫And so in ES6 we can abbreviate that simply as this.
‫Now before we move on here,
‫there's actually something important
‫that I need to do for security.
‫So let me show that to you in Postman,
‫where we signed up for a new account.
‫And so you see here that in the user output,
‫we actually get the password, okay?
‫It is encrypted actually, but still,
‫it's not a good practice to leak the password data
‫out to the client, okay?
‫For example, if we had our get all users here,
‫and this route is not yet implemented,
‫but if we were to get all the users from the collection,
‫then all of them would have the password visible,
‫and we don't want that, okay?
‫And so fixing it is actually quite easy,
‫because we did it before.
‫All we have to do is to say password,
‫and then select and set it to false.
‫And so like this it will automatically never show up
‫in any output.
‫And to let me actually prove that to you
‫by signing up as something else, so like this.
‫And let's see, oh, actually we already have a user
‫with that email, so let's just do test3.
‫And now the password is actually still here,
‫but I guess that is simply because we could just create it,
‫this new document here,
‫and so therefore it's not really selecting it
‫because we're not actually reading it from the database.
‫So let's just very quickly
‫actually implement this route here,
‫that's only gonna take a minute.
‫So just to see if this here actually works.
‫Because this step is actually important
‫for what we're gonna do next, okay?
‫So let's actually go ahead and copy this code
‫from the getAllTours, and not really copy,
‫but what we're gonna do is quite similar to this of course.
‫So I'm simply gonna copy this,
‫but without the API features.
‫So I'm not really interested in that, okay?
‫And so this one I'm actually gonna do
‫in the user controller, because getting all the users
‫has nothing to do with authentication.
‫And so in this case the user controller
‫is the perfect place for doing this.
‫Now of course I also need to quickly import
‫the user model here, and so let's also copy that
‫from the tourController, which is not the same but similar.
‫So let's grab actually all of this
‫into our user controller.
‫This one we don't need, but we also need to catchAsync.
‫Okay, so tour is gonna be user,
‫and here it will be userModel, all right?
‫Then here we have tours, and down here and down here,
‫so I hit Command + D to select all of them
‫and change it to users.
‫And then here of course what we are awaiting is User.find.
‫Now since we have an await here,
‫we need async, then we also need next,
‫and since we have an async here,
‫we should then wrap the entire thing into a catch async.
‫So let's do that.
‫Okay, and so at this point
‫this is nothing new to you anymore,
‫we're already used to doing this kind of stuff, right?
‫So, this should already work at this point,
‫so let's try it out.
‫So get all users, and now indeed we see
‫that their password is not included in the output.
‫And that is important because actually, in the find,
‫so back here in the AuthController,
‫this here will now also not contain the password, okay?
‫And so the output of this here
‫will now also not contain the password.
‫But we do need the password in order to check
‫if it is correct, right?
‫And so we need to explicitly select it as well.
‫So remember how we used select before
‫to basically simply select a couple of fields
‫from the database, only the ones that we needed?
‫Now in this case, when we want the field
‫that is by default not selected,
‫we need to user plus and then the name of the field.
‫So password in this case.
‫And so like this, it will be back in the output, okay?
‫Of course we need to await this query,
‫and then mark the function as async.
‫And then just like before,
‫in order to avoid that ugly try catch block,
‫wrap this entire function into catchAsync.
‫All right, makes sense?
‫So let's now quickly just log the user to the console,
‫just to see if it works.
‫So this one here.
‫We get our success, and indeed we get the user here.
‫So with exactly the email that I just posted,
‫and then the password is now also back to being included
‫in the output, all right?
‫And so again, that's because we explicitly selected it here.
‫And so now it's time to actually compare
‫the passwords that we have in the database
‫with the one that the user just posted.
‫But how are we gonna do that?
‫Because for example, the password might be,
‫or is in this example, pass1234, but the one
‫that we have stored in the document looks like this.
‫So how are we gonna compare this?
‫There's not really a way of doing it, right?
‫But actually there is, all we have to do
‫is to again use the bcrypt package, okay?
‫So we used bcrypt to generate this hashed password,
‫and we can also use the same package
‫to basically compare an original password like this here
‫with the hashed password.
‫Of course this password here, since it's encrypted,
‫there's no way of getting back the old,
‫so the original password from this string, right?
‫So that's the entire point
‫of actually encrypting a password.
‫And so the only way of doing it
‫is for this package for this algorithm
‫to actually encrypt this password as well,
‫and then compare it with the encrypted one, all right?
‫So let's implement a function that's gonna do that,
‫and for that we will use, again, the bcrypt package.
‫And we will do that in the user model.
‫And you might ask "Why we're doing it in a model
‫"and not just here," but that's, again,
‫because this is really related to the data itself.
‫And also we already have that package in there,
‫and so it's easier to simply do it there.
‫So let's just get rid of this string,
‫and then create a function here,
‫which will check if the given password
‫is the same as the one stored in the document.
‫So for the first time now we're gonna create something
‫called an instance method.
‫So an instance method is basically a method
‫that is gonna be available on all documents
‫of a certain collection, okay?
‫And it works like this.
‫So again, it's defined on a userSchema,
‫and then we say methods, and now in this case
‫we want to call the function correctPassword, all right?
‫So function, now this function is gonna accept
‫a candidatePassword, so the password
‫that the user passes in the body,
‫and then also the userPassword, okay?
‫Now inside of these instanced methods,
‫since they are available on the document,
‫the this keyword actually points to the current document.
‫But in this case, since we have the password
‫set to select false, so this here, remember?
‫Okay, and because of that,
‫this.password will not be available.
‫So ideally we would do it like this,
‫and so this way we would only need
‫to pass in the candidatePassword and not the userPassword.
‫But again, right now that's not possible
‫because the password is not available in the output.
‫And so that's why we actually
‫have to pass in userPassword as well.
‫So the goal of this function
‫is to really only return true or false.
‫So basically true if the passwords are the same,
‫and false if not.
‫So return, and then bcrypt which we already know,
‫and then we are gonna use the compare function, okay?
‫And the compare function is really easy,
‫all we need is to pass in the candidatePassword
‫and the userPassword, not userSchema, userPassword, okay?
‫And just like the hash function up here,
‫this one is also an asynchronous function.
‫And so just like before we use await, and then here async.
‫Okay, make sense?
‫So again, this compare function here
‫will very simply return true
‫if these two passwords here are the same, and false if not.
‫And again, we cannot compare them manually
‫because the candidate password is not hashed,
‫so it's the original password coming from the user,
‫but userPassword is of course hashed,
‫and so without this function here
‫we would have no way of comparing them, okay?
‫So here we return true or false,
‫now all we need to do is to actually call this function
‫in the authController.
‫Let's close a couple of these.
‫All right, so this here was only here for demonstration.
‫So let's simply call this here correct,
‫and now remember that the function that we just defined
‫is an instanced method.
‫And so therefore it is available on all the user documents.
‫And so this variable here right now
‫is a user document, right?
‫Because it's a result of querying the user model.
‫And so we can now say user.correctPassword.
‫Now all we need to do is pass in the candidate password,
‫which is password, remember.
‫So this one here, and then the userPassword.
‫And so that's in user.password, all right?
‫And so this here will now be either true or false.
‫All right, and now let's actually use these two variables
‫in order to figure out if the user exists
‫and the password is correct.
‫So we already figured that out,
‫but now we need to actually write our if statement.
‫So if there is no user, or the password is incorrect,
‫so basically correct is false, which is what this means.
‫In that case, we want to, again,
‫return and go straight to our next middleware
‫with a new AppError.
‫And in this case saying incorrect email or password, okay?
‫And then the status code is 401,
‫which means unauthorized, all right?
‫Now we could've done this separately,
‫so first checking for the user,
‫and then checking for the correct password.
‫But in that case we would then give a potential attacker
‫information whether the email or the password is incorrect.
‫And this way here it's a bit more vague.
‫So we're not really specifying what is incorrect here.
‫So if it's email or if it's the password.
‫And so if there's some attacker
‫trying to put in some random data,
‫then they will not know if the email actually exists,
‫or if it's just the password that's wrong, all right?
‫Now just two things here, and the first one
‫is that we actually need to await
‫this asynchronous function, okay?
‫So remember that correct password
‫is an asynchronous function,
‫and so here we also need to await for that.
‫And then also there's another problem,
‫because of this user doesn't exist here,
‫then this next line of code cannot really run, okay?
‫Because for example, user.password
‫is not gonna be available.
‫And so we actually need to move all of this,
‫or actually only this code,
‫we're gonna move it here into the if else statement.
‫All right, and so this way,
‫if the user does not exist, so if this is true,
‫well then it will not even run this code here,
‫and then there's not gonna be any problem.
‫But if the user exists, then it will also run this code
‫and check if the password is actually right.
‫And so if the password is correct,
‫only in that case we ever reach this piece of code here.
‫So that's the whole idea of this function.
‫So basically we check for the negatives.
‫So if there's no email and no password,
‫then we get this error.
‫If there's no user, or if there's a wrong password,
‫then create this error.
‫But if there was no error at all,
‫well in that case we reach this part of the code,
‫where we now generate a token
‫and then send it back to the user.
‫Now creating this token is gonna be the exact same thing
‫as we did before here.
‫And so instead of repeating all of this code,
‫let's actually create a function for this, okay?
‫So I'm gonna copy this code,
‫and then here very quickly const signToken,
‫which is gonna receive as the only input the user ID.
‫So ID, and it will then return the token, okay?
‫So it will simply sign this and return it right away.
‫All right, and so here what we're gonna do
‫is signToken and pass in this ID.
‫And then of course we also need to change it up here, okay?
‫So set this ID to this ID,
‫which as you already know at this point is the same as this.
‫All right, and so let's now go ahead and do the same here.
‫signToken, and then in this case it is user._id.
‫Phew, that was a long lecture,
‫let's now go ahead and try it out.
‫So it should now work.
‫So remember, well this is now another user,
‫but I know that this is the password that I used.
‫So let's for now let's use another one,
‫so just test12, and let's see what happens.
‫Let's wait, and indeed, incorrect email or password.
‫And so we get of course also our 401,
‫and indeed it means that our code is currently testing
‫for the password already.
‫So if we now choose the correct password,
‫then it should pass, so let's wait for it.
‫But, we still get this error.
‫Well, so let's simply get rid of all of our users here,
‫just to make sure that it doesn't have to do anything
‫with the users, and then create a new one, all right.
‫So hello@jonas, and pass1234.
‫Let's go ahead and copy it, I will send it now.
‫And so these credentials are now correct for sure.
‫So if it doesn't work now there's some kind of bug.
‫But now actually it worked, great.
‫So here is our token, and it looks exactly like before,
‫so we can assume that it is correct.
‫Now let's now try it again, deleting this, okay.
‫And so with the wrong password it does not work.
‫Let's now put the password right and the email wrong,
‫and then we get the same error.
‫So incorrect email or password.
‫And again if we put everything back,
‫then we get a new token.
‫And so we are now really logged into the application.
‫And I know this might look kind of abstract,
‫so all we really get is a token, and then that means
‫we are logged into the application, right?
‫But, well, that's how it works on stateless authentication.
‫Later on when we really build the dynamic website,
‫then of course it's gonna be a lot more visible
‫if the user is either logged in or logged out, right?
‫But it will still work behind the scenes
‫using this same token.
‫So if there's no token, then the website will look one way,
‫and if there is a token then the website's
‫gonna look another way, with the user image
‫and the username right there in the website, for example.
‫Anyway, this was quite a long lecture.
‫Make sure to go back here and really try to understand
‫all of this code that we have here, all right?
‫Because I know that some of this
‫might be a bit confusing.
‫For example, this correctPassword function here.
‫And then I'm sure everything will make sense to you.