1 00:00:00,180 --> 00:00:04,900 In the last video you set up Mongoose to hash user passwords in this video. 2 00:00:04,920 --> 00:00:10,950 You're gonna provide users with a brand new endpoint that will allow them to log in with their existing 3 00:00:10,950 --> 00:00:16,980 account so they'll have to provide their credentials their email and password and it will be the job 4 00:00:16,980 --> 00:00:21,780 of that route to verify that there is a user with those credentials. 5 00:00:21,780 --> 00:00:27,030 So to get started we'll be setting up the new route in the user router and we'll also be working with 6 00:00:27,030 --> 00:00:34,530 the User model to setup a reusable function for finding a user by their credentials by their email and 7 00:00:34,530 --> 00:00:35,610 password. 8 00:00:35,610 --> 00:00:40,400 To start though let's head over to the user router and create our brand new route. 9 00:00:40,470 --> 00:00:48,300 I'm gonna add this one just below the route for signing up and then this one will also use post so router 10 00:00:48,330 --> 00:00:53,100 dot post we will have our route followed by our function. 11 00:00:53,100 --> 00:00:57,370 I'll define that as an async function with request and response. 12 00:00:57,510 --> 00:01:04,780 And this path is going to be the following forward slash users forward slash log in. 13 00:01:04,800 --> 00:01:09,930 This is the end point you'll use to log in with your existing account. 14 00:01:09,960 --> 00:01:17,870 Now it's the job of this function to find a user by those credentials by the email and the password. 15 00:01:17,880 --> 00:01:19,970 There are two ways we could structure this. 16 00:01:19,980 --> 00:01:24,160 The first way would be to add all of that code right inside of this function. 17 00:01:24,240 --> 00:01:30,750 We find a user by their email and then we go ahead and use that B crypt compare method to compare the 18 00:01:30,780 --> 00:01:35,330 plaintext password provided with the hash stored in the database. 19 00:01:35,340 --> 00:01:40,710 The alternative option would be to create a reusable function that does all of that for us and that's 20 00:01:40,710 --> 00:01:42,630 the approach that we're gonna take. 21 00:01:42,630 --> 00:01:44,250 So right here what are we going to do. 22 00:01:44,250 --> 00:01:51,700 We're gonna start off with a try catch block and when we catch an error we'll send back an error but 23 00:01:51,700 --> 00:01:58,910 we'll focus on that a little bit later inside of try what we're going to do is call a new method. 24 00:01:58,930 --> 00:02:00,210 We're gonna be setting up. 25 00:02:00,250 --> 00:02:04,720 So right here const user we're gonna get a user back. 26 00:02:04,720 --> 00:02:11,310 We're going to use a wait for this async operation and we're going to call user dot and we're going 27 00:02:11,310 --> 00:02:14,080 to call something that will define in just a moment. 28 00:02:14,080 --> 00:02:15,120 So right here. 29 00:02:15,190 --> 00:02:17,830 Well we do have pre-built in options. 30 00:02:17,830 --> 00:02:20,000 We can also define our own. 31 00:02:20,080 --> 00:02:24,650 I'm going to create one called Find by credentials. 32 00:02:24,670 --> 00:02:31,660 This is going to take in the email and the password and it'll try to find a user by the email it will 33 00:02:31,660 --> 00:02:33,380 verify the password. 34 00:02:33,400 --> 00:02:36,430 It'll either return the user or it won't. 35 00:02:36,430 --> 00:02:39,490 So right here let's go ahead and knock that out. 36 00:02:39,490 --> 00:02:46,120 What I'm gonna do is pass in the email and the password both of which will be provided as the body of 37 00:02:46,120 --> 00:02:47,260 the request. 38 00:02:47,290 --> 00:02:57,670 So that would be a request dot body dot email as the first argument and request dot body dot password 39 00:02:57,730 --> 00:02:59,440 as the second. 40 00:02:59,480 --> 00:03:02,450 Now let's go ahead and actually create this function. 41 00:03:02,470 --> 00:03:04,600 So this code works as expected. 42 00:03:04,720 --> 00:03:07,210 This happens over in the user model. 43 00:03:07,330 --> 00:03:14,170 And once again it's only something we can do when we create a separate schema first then pass that into 44 00:03:14,170 --> 00:03:15,070 the model. 45 00:03:15,070 --> 00:03:20,590 If I was still passing an object in as the second argument right here we wouldn't be able to do what 46 00:03:20,590 --> 00:03:22,180 we're about to do. 47 00:03:22,330 --> 00:03:28,870 So right here for the code we wrote before I'll leave a little comment hash the plain text password 48 00:03:30,420 --> 00:03:36,470 before saving then up above we're gonna attach something else to user schema. 49 00:03:36,510 --> 00:03:44,050 It's gonna be user schema dot statics dot the method name. 50 00:03:44,050 --> 00:03:49,720 In this case I'm choosing to create defined by credentials but I could call this anything that I want 51 00:03:49,720 --> 00:03:50,400 to. 52 00:03:50,410 --> 00:03:56,590 So by setting up a value on user schema dot statics we're setting that up as something we can access 53 00:03:56,620 --> 00:03:58,430 directly on the model. 54 00:03:58,480 --> 00:04:00,310 Once we actually have access to it. 55 00:04:00,670 --> 00:04:08,820 So right here user defined by credentials will call whatever is defined on user schema dot statics defined 56 00:04:08,970 --> 00:04:10,560 by credentials. 57 00:04:10,600 --> 00:04:15,310 Now we're going gonna set this up as a function and we'll set it up as an async function. 58 00:04:15,310 --> 00:04:18,940 It's going to accept those two arguments that we chose to provide. 59 00:04:19,180 --> 00:04:25,260 Right here we can indeed use an arrow function as the this binding isn't going to play a role and we're 60 00:04:25,260 --> 00:04:31,390 gonna have our arguments the email as the first one and the password as the second one. 61 00:04:31,390 --> 00:04:38,470 Now it's our job to attempt to find the user by those pieces of information we want to start by finding 62 00:04:38,470 --> 00:04:45,400 them by the email we can't find them by the email and password because we have the plain text password. 63 00:04:45,490 --> 00:04:48,760 But the hashed password is stored in the database. 64 00:04:48,760 --> 00:04:50,700 So we'll find them by the email first. 65 00:04:50,710 --> 00:04:54,160 Then separately will verify that password to get started. 66 00:04:54,160 --> 00:04:57,090 Let's go ahead and find the user by the email. 67 00:04:57,100 --> 00:05:04,900 So const user and then we'll use a weight with user dot find one find one is going to return a single 68 00:05:04,900 --> 00:05:11,560 user and it's similar to find by I.D. though instead of providing the I.D. we provide an object with 69 00:05:11,560 --> 00:05:13,130 our search criteria. 70 00:05:13,270 --> 00:05:19,750 In this case we're looking for a user whose email equals the argument value passed in for email. 71 00:05:19,750 --> 00:05:24,210 Now in this case we can just use the shorthand syntax to set that up. 72 00:05:24,280 --> 00:05:30,790 Now if there is no user that means there was no user with that email and we want to go ahead and throw 73 00:05:30,820 --> 00:05:32,050 a new error. 74 00:05:32,110 --> 00:05:37,370 So right here the condition if there is no user what are we gonna do. 75 00:05:37,540 --> 00:05:45,040 We will use throw new error which will immediately end the execution of this function and we will throw 76 00:05:45,040 --> 00:05:48,660 the following error unable to log in. 77 00:05:48,670 --> 00:05:54,070 So here we're saying let's try to find that user by the email if that doesn't work then obviously we 78 00:05:54,070 --> 00:05:55,480 can't log in. 79 00:05:55,750 --> 00:06:01,780 Now if that does work the next thing we want to do is verify that password using the compare function 80 00:06:01,780 --> 00:06:03,290 we've used before. 81 00:06:03,310 --> 00:06:12,490 So const right here is match and we will set that equal to we'll use a wait with B script dot compare 82 00:06:12,490 --> 00:06:20,200 which we have used before we pass in as the first argument the plain text password and as the second 83 00:06:20,260 --> 00:06:21,330 the hash. 84 00:06:21,400 --> 00:06:28,030 Now we know we found a user otherwise this code wouldn't run and we have user dot password which stores 85 00:06:28,150 --> 00:06:29,530 the password. 86 00:06:29,530 --> 00:06:32,340 Now if it's not a match that's a problem too. 87 00:06:32,350 --> 00:06:35,860 So if the opposite of is match. 88 00:06:35,860 --> 00:06:40,150 So here we are checking if it's not a match if it's not a match. 89 00:06:40,150 --> 00:06:46,820 This code will run and we'll go ahead and throw a new error to stop the function execution and signal 90 00:06:46,820 --> 00:06:48,990 that the log process didn't work. 91 00:06:49,000 --> 00:06:56,230 Once again unable to log in when providing error messages for things like logging in it's best to not 92 00:06:56,230 --> 00:06:57,610 be too specific. 93 00:06:57,610 --> 00:06:59,420 You don't want to say something like that. 94 00:06:59,420 --> 00:07:05,140 Emails registered but your password is wrong because it exposes more information than you might want 95 00:07:05,140 --> 00:07:11,110 to someone who's trying to gain access to the account now knows more than they otherwise should. 96 00:07:11,110 --> 00:07:17,980 In general it's best to provide a single error saying the operation didn't work in this case unable 97 00:07:17,980 --> 00:07:20,580 to log in is nice and generic. 98 00:07:20,680 --> 00:07:26,290 Now the last thing we want to do is actually return to the user if they were found and the password 99 00:07:26,320 --> 00:07:28,030 is a match right here. 100 00:07:28,090 --> 00:07:29,780 Return user. 101 00:07:30,070 --> 00:07:36,230 Now with this in place we can actually finish filling out the root over here in the user router. 102 00:07:36,250 --> 00:07:41,980 Now on the very next line if an error didn't get thrown we would have access to the user and all we're 103 00:07:41,980 --> 00:07:44,590 gonna do for the moment is send them back. 104 00:07:44,620 --> 00:07:47,110 So response dot dots end user. 105 00:07:47,110 --> 00:07:52,210 We're not actually going to worry about providing them with details that they can use to perform other 106 00:07:52,210 --> 00:07:54,410 privileged operations later. 107 00:07:54,460 --> 00:08:00,190 So it's not like we're logging in long term we're really just logging in in order to see our own user 108 00:08:00,190 --> 00:08:00,880 profile. 109 00:08:00,910 --> 00:08:06,700 But once again we're going to build off of this to create the complete system down below in catch. 110 00:08:06,700 --> 00:08:12,630 I'll use response dot status to set up something like a four hundred and I'll use send sending back 111 00:08:12,640 --> 00:08:19,590 no content the 400 alone is enough of a signal to let them know that logging in did not work. 112 00:08:19,690 --> 00:08:24,630 Now that we have this in place we can actually test things out but there is a problem. 113 00:08:24,670 --> 00:08:31,000 One of the most basic things you should set up when creating a login system is a restriction on the 114 00:08:31,030 --> 00:08:31,800 email. 115 00:08:31,900 --> 00:08:38,980 If a user already has an account registered with a specific email a another user should not be able 116 00:08:38,980 --> 00:08:44,010 to come along and use that same email to register an account again. 117 00:08:44,180 --> 00:08:51,710 So before we even try to log in let's go ahead and fix that by adding a single line over here to the 118 00:08:51,770 --> 00:08:54,680 Mongoose schema under email. 119 00:08:54,680 --> 00:08:58,610 We're going to add a brand new property which can go anywhere you like. 120 00:08:58,640 --> 00:09:02,600 It is unique and we're going to set it equal to true. 121 00:09:02,600 --> 00:09:10,250 Now when we do this it's going to create an index in our Mongo DB database to guarantee uniqueness similar 122 00:09:10,250 --> 00:09:14,900 to how our ideas are unique are emails for users. 123 00:09:14,900 --> 00:09:17,410 Now need to be unique as well. 124 00:09:17,420 --> 00:09:18,820 Now here's the catch. 125 00:09:18,860 --> 00:09:23,700 In order to get this to work we have to wipe the database and have it get re created. 126 00:09:23,720 --> 00:09:30,290 So the index can be set up if you tried to create users right now even though this code is in place 127 00:09:30,320 --> 00:09:33,890 you'd be able to create users with these same emails. 128 00:09:33,890 --> 00:09:39,680 So to start I'll be heading over to robo three TI and we're gonna go ahead and just completely drop 129 00:09:39,680 --> 00:09:40,670 our database. 130 00:09:40,670 --> 00:09:42,890 So I'll right click the database. 131 00:09:42,890 --> 00:09:47,040 Yes I do have other databases that I've created for development purposes. 132 00:09:47,150 --> 00:09:50,790 Right now though you want to delete the one that you've been working with. 133 00:09:50,840 --> 00:09:57,170 We're gonna right click it go down to drop database and once it's dropped we can go ahead and just refresh 134 00:09:57,170 --> 00:09:58,820 things in the application. 135 00:09:58,820 --> 00:10:02,250 Getting it to restart by saving any of the files. 136 00:10:02,300 --> 00:10:08,120 Now we're in a position where users shouldn't be able to create an account with an email if it's already 137 00:10:08,120 --> 00:10:11,810 registered and log in should always work as expected. 138 00:10:11,810 --> 00:10:14,770 Let's head over to postman and test all of this out. 139 00:10:14,810 --> 00:10:19,170 So over here I have the response data for the last time I created a user. 140 00:10:19,190 --> 00:10:21,210 But they have indeed been deleted. 141 00:10:21,230 --> 00:10:25,430 Let's go ahead and create our first user with a name email and password. 142 00:10:25,430 --> 00:10:27,290 I already have in place. 143 00:10:27,440 --> 00:10:31,610 I'm gonna send that off and down below I get a 2 0 1 which is great. 144 00:10:31,610 --> 00:10:35,990 There is no user with that email since I just deleted everyone. 145 00:10:35,990 --> 00:10:39,060 We have the user created with their hashed password. 146 00:10:39,170 --> 00:10:43,270 All I'm going to do is file your off the exact same request again. 147 00:10:43,400 --> 00:10:49,010 This time though it should fail and that is exactly what's happening down here we can see we have a 148 00:10:49,010 --> 00:10:50,630 duplicate key error. 149 00:10:50,630 --> 00:10:54,940 The email is already registered and that's a problem. 150 00:10:54,980 --> 00:11:01,550 So now that we can not sign up with the same email twice let's go ahead and mess around with the log 151 00:11:01,550 --> 00:11:03,090 in and request wipes. 152 00:11:03,110 --> 00:11:05,350 I don't want to create a new collection. 153 00:11:05,480 --> 00:11:09,510 I want to add a new request to my existing collection. 154 00:11:09,560 --> 00:11:12,180 I'll call that log in user. 155 00:11:12,350 --> 00:11:18,370 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. 156 00:11:18,410 --> 00:11:21,970 So we're keeping things nice and organized by what they do. 157 00:11:22,100 --> 00:11:27,260 Next up I'm going to click on that and that's going to bring us to our brand new request where we'll 158 00:11:27,260 --> 00:11:28,610 set things up. 159 00:11:28,610 --> 00:11:35,890 We have post we have forward slash excuse me we start off with local host on three thousand then forward 160 00:11:35,900 --> 00:11:39,280 slash users forward slash log in. 161 00:11:39,320 --> 00:11:42,120 Now we do have to provide our credentials as well. 162 00:11:42,200 --> 00:11:50,300 That is part of the request body so body raw and we switch from text over to Jason and then down below 163 00:11:50,300 --> 00:11:56,200 we provide the email and we're also going to provide the password. 164 00:11:56,260 --> 00:11:59,670 The two things needed to log in correctly. 165 00:11:59,680 --> 00:12:05,750 Let's go ahead and actually test this out with the credentials that we just used to create that user. 166 00:12:05,770 --> 00:12:08,850 So it's Andrew at example dot com and red. 167 00:12:08,860 --> 00:12:13,450 One two three four five exclamation mark for me over here. 168 00:12:13,570 --> 00:12:23,560 Email Andrew at example dot com password Red One two three four five exclamation mark. 169 00:12:23,560 --> 00:12:26,460 I send this off and down below what do I get. 170 00:12:26,530 --> 00:12:34,460 I get a two hundred and I see my user profile being sent back correctly which is excellent from here 171 00:12:34,730 --> 00:12:37,060 we can go ahead and try to mess up the data. 172 00:12:37,310 --> 00:12:43,520 So for example if I try to use two exclamation marks in the password I would expect things to fail and 173 00:12:43,520 --> 00:12:45,950 we're getting our four hundred down below. 174 00:12:46,040 --> 00:12:53,090 If I fix the password but mess up the email adding in two A's I would expect that to fail as well. 175 00:12:53,180 --> 00:12:57,350 I send that off and down below I'm still getting a four hundred. 176 00:12:57,350 --> 00:13:03,800 Only when the email and the password perfectly match the user information do we get a two hundred. 177 00:13:03,800 --> 00:13:06,740 And do we get our user profile back. 178 00:13:06,740 --> 00:13:12,780 Now this is not the complete process for logging in but it is a great step in the right direction. 179 00:13:12,800 --> 00:13:19,490 We have the new route set up and the new route knows how to verify the user's credentials making sure 180 00:13:19,490 --> 00:13:23,820 there's actually a user that has that email and password. 181 00:13:23,930 --> 00:13:27,450 We're going to continue to build off of this in the next video. 182 00:13:27,500 --> 00:13:29,990 So let's go ahead and jump right into that.