Coding

Adding User Login & JWT Signing | Creating a REST API with Node.js

  • 00:00:01 welcome to this video great to have you
  • 00:00:03 on board in the last video we added
  • 00:00:05 users sign up so the functionality to
  • 00:00:08 create users in this video I want to
  • 00:00:11 make sure that users can also lock
  • 00:00:12 themselves in and get such a token I was
  • 00:00:15 referring to in the last video the token
  • 00:00:18 which we then need to attach to future
  • 00:00:20 requests which reach our protected
  • 00:00:22 routes in our back-end we don't have
  • 00:00:24 protected routes yet we'll add these
  • 00:00:26 later but for now let's make sure we can
  • 00:00:28 get such a token at least so that is
  • 00:00:30 what we'll focus on in this video
  • 00:00:35 time to you create some tokens in our
  • 00:00:38 user routes file we got a sign-up route
  • 00:00:41 now I want a second route before I
  • 00:00:44 delete users where I also handle post
  • 00:00:47 requests but to slash login and the idea
  • 00:00:50 here is to all to get email and password
  • 00:00:52 but not create a new user but instead
  • 00:00:55 have a look at the database and see if
  • 00:00:57 we got a fitting user and if we do then
  • 00:01:00 actually create such a token so here
  • 00:01:02 first of all I'll create response
  • 00:01:06 request response and next and the goal
  • 00:01:09 here will be to see if we got a user so
  • 00:01:12 I can use my user model and simply find
  • 00:01:15 any user for a given email address so
  • 00:01:19 we're email is actually equal to request
  • 00:01:22 body email and that approach is pretty
  • 00:01:26 similar to the sign up route where we
  • 00:01:28 also find such users in this case to
  • 00:01:30 make sure we don't create a new user for
  • 00:01:32 an email that has already been taken in
  • 00:01:34 this case down here in order to lock the
  • 00:01:37 user in so here we then have X act to
  • 00:01:41 get a real promise with them and catch
  • 00:01:44 and now for caching I'll use my default
  • 00:01:47 error handling code and yes we could
  • 00:01:49 outsource this into a separate method I
  • 00:01:51 might do this later for now let me stay
  • 00:01:53 to this more where both but also
  • 00:01:55 understandable way of coding this now
  • 00:01:58 the then block is of course interesting
  • 00:02:00 to us because they're Estelle's where we
  • 00:02:01 get our user and to be precise we should
  • 00:02:05 name this users because as you learned
  • 00:02:07 this will be an array it's just an empty
  • 00:02:11 array if we got no user and I'm naming
  • 00:02:13 it user because it will at most be one
  • 00:02:15 user because we prevent the creation of
  • 00:02:18 duplicate entries due to our check here
  • 00:02:21 in signup so I get my user array and
  • 00:02:25 therefore first of all I'll check if
  • 00:02:27 user length is smaller than one so if we
  • 00:02:33 got no user if that's the case then I
  • 00:02:36 want to return a response where I set a
  • 00:02:39 status code of 404 and basically tell
  • 00:02:44 the user Ted login fail however
  • 00:02:48 this might not be the best pattern
  • 00:02:50 because what we could do is we could
  • 00:02:52 send a JSON response where we send a
  • 00:02:54 message like mail not found user doesn't
  • 00:03:01 exist
  • 00:03:02 whoops doesn't exist like this the issue
  • 00:03:08 with this approach is that we open our
  • 00:03:11 app to some kind of brute-force attack
  • 00:03:14 users could now just try out different
  • 00:03:16 email addresses and they will at least
  • 00:03:19 find out which ones are there and which
  • 00:03:21 ones are not so once they get a list of
  • 00:03:24 available email addresses they could
  • 00:03:25 focus on these two then get out the
  • 00:03:28 password or things like that so this
  • 00:03:30 might not be the best pattern not as
  • 00:03:33 super big flaw if we add it but also not
  • 00:03:36 great and can be a big flaw so let's not
  • 00:03:38 do it like this instead let's just
  • 00:03:41 return for a-1 which simply means
  • 00:03:43 unauthorized and I'll also simply return
  • 00:03:47 off failed which could fail because we
  • 00:03:50 got no email or because the password is
  • 00:03:51 wrong so that's the first check now
  • 00:03:54 let's assume we make it past this if
  • 00:03:56 block here so we don't return this
  • 00:03:58 response and therefore this code gets
  • 00:03:59 executed now we found the user the next
  • 00:04:03 step is to make sure that the password
  • 00:04:05 sent with the request matches the
  • 00:04:07 password in our database the thing just
  • 00:04:09 is we do hash our password here with
  • 00:04:12 beak Ritesh and in the last video I
  • 00:04:15 mentioned that we can't reverse this so
  • 00:04:18 how can we match the new password which
  • 00:04:22 is coming in plain text queue the
  • 00:04:24 password in our database if we can't
  • 00:04:27 rebuild it well be crypt of course used
  • 00:04:31 a certain algorithm for hashing the
  • 00:04:33 password and we basically have some kind
  • 00:04:37 of way of comparing values even though
  • 00:04:41 they're hashes won't be the same just by
  • 00:04:44 making sure that a plaintext password
  • 00:04:46 which we hash again with the same
  • 00:04:48 algorithm in the end yields us a
  • 00:04:51 comparable hash so this is a check this
  • 00:04:54 package can do for us and you can dive
  • 00:04:57 into the be trap documentation on the
  • 00:04:59 get up pass a repository to find out
  • 00:05:01 this works here with this compare method
  • 00:05:04 for this we need to pass the plaintext
  • 00:05:06 password and the hash we want to compare
  • 00:05:09 it to and it will return true if it
  • 00:05:11 basically finds out yeah both passwords
  • 00:05:13 were created with the same algorithm the
  • 00:05:16 same key and therefore they are the same
  • 00:05:20 passwords even though the hashes don't
  • 00:05:22 match so this is what we can do here so
  • 00:05:28 in my part here after checking whether
  • 00:05:33 we got the email I'll use be tripped
  • 00:05:35 again and call the compare method and
  • 00:05:37 first of all passed request body
  • 00:05:40 password here plain text password and
  • 00:05:43 then the hash which is of course part of
  • 00:05:45 the user we retrieved so user and then
  • 00:05:49 here we can simply access the first
  • 00:05:52 element and by the way and alternative
  • 00:05:54 to find would also be find one this
  • 00:05:57 would ensure that we don't get an array
  • 00:05:59 but just one user I'll just stick to
  • 00:06:01 this general syntax but you could
  • 00:06:04 definitely use find one so here we have
  • 00:06:06 user is zero and this is the only user
  • 00:06:10 we found this user will have a password
  • 00:06:12 field we stored this in the database
  • 00:06:15 because our user model has a password
  • 00:06:17 field now with that we can compare these
  • 00:06:21 two and now the third argument here is a
  • 00:06:23 callback where we get an error or a
  • 00:06:26 response
  • 00:06:27 now our error is not returned if we
  • 00:06:30 can't compare these it's just an error
  • 00:06:32 we get if the comparison generally fails
  • 00:06:34 so if we got an error here I simply also
  • 00:06:38 want to return off failed don't need to
  • 00:06:40 pass more information than that if we
  • 00:06:43 don't have an error I'll check if
  • 00:06:45 response because of that excuse we
  • 00:06:48 shouldn't name this rest because that
  • 00:06:50 again is a rest object so let's name
  • 00:06:52 this result so I'll check if result and
  • 00:06:56 as we see in the official doc this is
  • 00:06:59 just true if it succeeds or false if it
  • 00:07:01 fails so what I can do here is I can
  • 00:07:06 simply check if result so this will be
  • 00:07:08 true if it's exceeded so if the password
  • 00:07:10 is the correct one in this case I will
  • 00:07:13 return a response where
  • 00:07:15 said status code to 200 and then I
  • 00:07:18 returned my response where I say all
  • 00:07:22 successful and of course the token is
  • 00:07:25 still missing here I'll come back to
  • 00:07:26 this
  • 00:07:27 I also need another response here at the
  • 00:07:30 very end if we don't make it into this
  • 00:07:32 if block here then we return all failed
  • 00:07:35 because then the password was incorrect
  • 00:07:37 and we always return the same error code
  • 00:07:39 and message to give no indication about
  • 00:07:42 whether authentication failed because
  • 00:07:44 the wrong password or an incorrect email
  • 00:07:47 address and with that we get this setup
  • 00:07:50 we should now be able to give us back to
  • 00:07:52 Soph successful information let's now
  • 00:07:55 save this and try it out again back in
  • 00:07:57 postman I did create a user with test
  • 00:08:00 free at test common to test for password
  • 00:08:02 in last video so let's now target slash
  • 00:08:05 login which is the route we created here
  • 00:08:09 and let's send the same information and
  • 00:08:12 we should get back off successful now if
  • 00:08:15 I change the password and add one extra
  • 00:08:18 art we get all failed and the same if I
  • 00:08:22 enter a password and email which doesn't
  • 00:08:24 exist we also get all fails then and if
  • 00:08:27 I reword this to a valid combination and
  • 00:08:29 again as successful so this is working
  • 00:08:32 as it should now we're not done yet
  • 00:08:35 though we want to return such a token
  • 00:08:37 don't we now for that will add another
  • 00:08:40 library to our project note JSON web
  • 00:08:43 token it's a library that will do this
  • 00:08:46 token generation and signing and so on
  • 00:08:50 for us which of course is a bit of a
  • 00:08:52 more complex process and we simply have
  • 00:08:55 to install it and then follow the usage
  • 00:08:57 instructions down there so to do that
  • 00:09:00 I'll quit my process and run NPM install
  • 00:09:03 JSON web token I'll also add – – safe to
  • 00:09:07 add an entry to package JSON and now
  • 00:09:10 this will execute and we'll add this
  • 00:09:12 token to our project just as a packaged
  • 00:09:15 or project I'll restart the server
  • 00:09:17 thereafter and now how do we use that
  • 00:09:19 now you see here we got a couple of
  • 00:09:22 methods like JWT sign and signing
  • 00:09:25 actually is what we need to create a
  • 00:09:27 signed token
  • 00:09:29 there you'll see we pass a payload so
  • 00:09:31 some data we want to pass into the token
  • 00:09:33 we then pass a secret key so that is the
  • 00:09:37 key which only is known to the server
  • 00:09:39 and we also can pass more options and a
  • 00:09:43 callback that is executed once the
  • 00:09:45 signing is done and then the options you
  • 00:09:47 can choose the algorithm though I will
  • 00:09:49 keep the default change how long the
  • 00:09:51 token is valid this shouldn't be too
  • 00:09:53 long for security reasons because the
  • 00:09:55 token is stored on a client and if the
  • 00:09:58 client somehow is insecure and someone
  • 00:10:00 steals to token there he got full access
  • 00:10:02 to your API so you want to make that
  • 00:10:04 hook and short left and these are a
  • 00:10:07 couple of harbor things you can define
  • 00:10:10 and add the default should be fine all
  • 00:10:12 tweak expires in though and I will also
  • 00:10:15 make sure that we will sign the token to
  • 00:10:18 begin with so you can also see some
  • 00:10:20 usage examples down there to create a
  • 00:10:24 token let's first of all import this
  • 00:10:26 package I'll name it JWT and require
  • 00:10:29 JSON web token that's the package we
  • 00:10:31 just installed and then in my login
  • 00:10:33 route here if we got a user so here we
  • 00:10:36 return off successful I will actually
  • 00:10:40 use JWT and call the sign method now
  • 00:10:43 here first of all the payload what do we
  • 00:10:45 want to pass to the client
  • 00:10:47 maybe we want to pass the user email
  • 00:10:50 address and ID certainly not the
  • 00:10:52 password even though it's hashed but
  • 00:10:54 don't do that so I will add an email
  • 00:10:57 here email key and this will of course
  • 00:11:01 be my extracted user email so I can
  • 00:11:05 simply access user 0 which is the user
  • 00:11:07 we got from the database and there the
  • 00:11:10 email field and now the same for the ID
  • 00:11:12 user ID can be user 0 and then
  • 00:11:15 underscore ID so these are the two
  • 00:11:18 values I want to put into my JWT so in
  • 00:11:22 my JSON web token then we need a private
  • 00:11:25 key and I'll again use an environment
  • 00:11:29 variable for this so I'll add it to my
  • 00:11:32 environment variable file there along of
  • 00:11:35 my or next to my Mongo Atlas password
  • 00:11:39 I'll add my che WCET
  • 00:11:42 key and I'll name it secret here
  • 00:11:45 obviously you would typically use a more
  • 00:11:47 elaborate string than that but I'll just
  • 00:11:49 use that here and then I will use it
  • 00:11:52 here too as a second argument I'll
  • 00:11:55 access process dot and dot JWT key so
  • 00:12:00 that environment variable we just added
  • 00:12:02 now let's continue adding arguments here
  • 00:12:06 the next argument are the options so
  • 00:12:09 I'll put this into a new line here and
  • 00:12:11 then another new line will be a
  • 00:12:13 JavaScript object where we can define
  • 00:12:15 the options of this signing process and
  • 00:12:18 here I'm interested in the expires in
  • 00:12:20 field and this can be set as you can see
  • 00:12:23 in the documentation to a value in
  • 00:12:25 seconds or a string describing time
  • 00:12:27 spans like one hour and one hour is a
  • 00:12:30 good duration for security reasons and
  • 00:12:34 finally the last argument here is a
  • 00:12:37 callback where we get our token you can
  • 00:12:42 however also omit this callback and just
  • 00:12:46 assign it to constant like I do here
  • 00:12:50 token and it will then run synchronously
  • 00:12:51 and give you that token and with that we
  • 00:12:54 can return the message but also the
  • 00:12:57 token here which is stored in this token
  • 00:13:00 constant and now if we restart the
  • 00:13:02 server since I changed the environment
  • 00:13:04 variables if we go back and we again
  • 00:13:07 login for this valid email password
  • 00:13:09 combination I get back off successful
  • 00:13:12 and this token now if we copied that
  • 00:13:15 token and I go to channel ut dot IO a
  • 00:13:19 link to which can of course be found in
  • 00:13:21 the video description but I guess the
  • 00:13:22 URL also isn't that hard there we can
  • 00:13:25 scroll down and copy in our token and
  • 00:13:28 this will now give us a decoded value
  • 00:13:31 because remember it's not encrypted it's
  • 00:13:33 just encoded in a base64 string but it's
  • 00:13:36 not encrypted and this is what's inside
  • 00:13:40 our token the email address and the user
  • 00:13:42 ID expiration information the algorithm
  • 00:13:47 which was used and some verification
  • 00:13:49 information which the end will be used
  • 00:13:50 by the server to verify it and if we
  • 00:13:53 were to change anything here like let's
  • 00:13:55 say changed
  • 00:13:56 email you will see the token on the left
  • 00:13:58 also changed and therefore it wouldn't
  • 00:14:01 be verifiable by the server anymore
  • 00:14:03 you see that the verification the blue
  • 00:14:06 part here switches if I switch that so
  • 00:14:09 that is what ensures that our token
  • 00:14:12 stays valid and we can't fiddle around
  • 00:14:14 with it so this is how we now add a
  • 00:14:18 token and return it to the user to the
  • 00:14:21 client in the next video we'll actually
  • 00:14:24 use that token to then also send it with
  • 00:14:27 requests to resources we want to protect
  • 00:14:30 you learn how we protect such resources
  • 00:14:32 and how we can verify the token