Coding

JWT Route Protection | Creating a REST API with Node.js

  • 00:00:01 welcome back to this series in the last
  • 00:00:04 two videos we added user signup and user
  • 00:00:07 login in this video I want to make sure
  • 00:00:10 that we can also protect routes we got
  • 00:00:14 that token which we issue since the last
  • 00:00:16 video that token we can store on our
  • 00:00:19 client not want to make sure that we can
  • 00:00:21 use that token to access certain routes
  • 00:00:24 on our back-end that are protected
  • 00:00:26 before that we need to add protection to
  • 00:00:28 some of these routes first so let's do
  • 00:00:30 all of that in this video
  • 00:00:35 time to add some protection we got our
  • 00:00:38 products and we got our order routes
  • 00:00:40 here right now there are some routes
  • 00:00:43 which we don't want to protect for
  • 00:00:45 example getting all products that makes
  • 00:00:48 sense to be an unprotected route because
  • 00:00:50 if we were to create a front-end for our
  • 00:00:53 shop here we probably want to be able to
  • 00:00:55 present all our products even to an
  • 00:00:57 authenticated users the same is true for
  • 00:01:00 the get route to a specific product
  • 00:01:03 typically we want to allow the user to
  • 00:01:05 get more product information before he
  • 00:01:07 has to sign up and that maybe buy it or
  • 00:01:10 whatever the plan is however other
  • 00:01:12 routes like creating a new product or
  • 00:01:16 deleting products or changing products
  • 00:01:19 and maybe all order related routes
  • 00:01:21 including the GATT roads here are
  • 00:01:23 protected routes we want to make sure
  • 00:01:25 that not every user can access them and
  • 00:01:28 therefore we need some way of protecting
  • 00:01:31 these routes a good approach would be to
  • 00:01:34 add some kind of middleware which we can
  • 00:01:37 easily add to a given route that runs
  • 00:01:41 prior to that route getting processed to
  • 00:01:44 actually determine does it make sense to
  • 00:01:46 continue or aren't you Afeni cated
  • 00:01:48 anyways so we need to add some
  • 00:01:51 middleware that checks for a valid token
  • 00:01:54 to be there and only if the token is
  • 00:01:57 there and valid can be verified so it
  • 00:01:59 hasn't been filled around with on the
  • 00:02:01 client only if that is the case we
  • 00:02:04 continue so that's the plan we'll add
  • 00:02:06 such a middleware for this I'll create a
  • 00:02:08 new folder in my API folder I'll need a
  • 00:02:11 name it middleware you could name it off
  • 00:02:13 whatever you want and in there I'll make
  • 00:02:16 add my check off J's file because this
  • 00:02:20 is what I want to do I want to check
  • 00:02:21 whether the user is authenticated or not
  • 00:02:23 I'll start with module exports to export
  • 00:02:26 something and that something is going to
  • 00:02:28 be a Aero function where I get the
  • 00:02:30 request response and next you probably
  • 00:02:33 know this pattern it's the default
  • 00:02:35 middleware pattern we use in Express
  • 00:02:37 Apps now I just want to create this
  • 00:02:39 function and export it in this file so
  • 00:02:42 that I can import it in my order and
  • 00:02:45 product route file and then use it in
  • 00:02:47 there to add
  • 00:02:48 to certain routes if you remember in the
  • 00:02:52 product route here we have the post
  • 00:02:54 route where we also have that upload
  • 00:02:57 signal middleware which will parse a
  • 00:03:00 file and this is exactly the same
  • 00:03:02 pattern I want to use we can add as many
  • 00:03:05 handlers as we want and they will be
  • 00:03:08 executed in the order from left to right
  • 00:03:10 so if I then add my check-off middleware
  • 00:03:13 here I will actually run through that
  • 00:03:15 first and only continue if we succeed
  • 00:03:17 that's the plan now for that will create
  • 00:03:19 it here now what do we have to do in
  • 00:03:22 this file we have to call next if we did
  • 00:03:27 successfully authenticate and we want to
  • 00:03:30 not call it we want to return an error
  • 00:03:32 instead if we did not succeed so for
  • 00:03:36 that we need some information from the
  • 00:03:38 JWT package or we need some help from
  • 00:03:41 that package we used in the last videos
  • 00:03:43 there we have a way of creating a token
  • 00:03:46 now there all this a verify method which
  • 00:03:49 allows us to verify an incoming token
  • 00:03:51 that is what I mentioned in the last
  • 00:03:52 videos the server can verify whether a
  • 00:03:56 token is valid or not for that of course
  • 00:03:58 we need to that key we stored on the
  • 00:04:00 server and we need to token so let's do
  • 00:04:04 just that
  • 00:04:05 let's import this JSON web token package
  • 00:04:08 in this file here so I'll create my JWT
  • 00:04:11 constant and simply require JSON web
  • 00:04:14 token and then in there I will simply
  • 00:04:20 create a new concept which I named
  • 00:04:22 decoded because the verify method will
  • 00:04:26 actually return the decoded token if it
  • 00:04:28 succeeds and then I will call JWT verify
  • 00:04:33 now you might see there are always a
  • 00:04:35 decode method this will just decode the
  • 00:04:38 token and not verify it and as I
  • 00:04:40 mentioned in the last video the token is
  • 00:04:42 not encrypted so decoding alone doesn't
  • 00:04:45 ensure it's valid it just ensures its
  • 00:04:47 valid base64 encoding but that doesn't
  • 00:04:50 help us
  • 00:04:51 so decode is only helpful if you want to
  • 00:04:54 get to the internals of the token after
  • 00:04:57 having verified it if you didn't verify
  • 00:05:00 it yet then decode
  • 00:05:02 makes no sense and verify we'll do both
  • 00:05:04 verify it and then return the decoded
  • 00:05:07 value so here is what I'll use
  • 00:05:09 verify and then I assume to get to token
  • 00:05:13 as part of the request body if we don't
  • 00:05:16 well then we will always fail here and
  • 00:05:19 that is exactly what we want to do if we
  • 00:05:21 go into this middleware which we only do
  • 00:05:24 if we added it to a route if we go in
  • 00:05:27 here and there is no token present then
  • 00:05:30 we should fail because then we got note
  • 00:05:32 Okin right so we pass the token here the
  • 00:05:38 key is what we stored in our environment
  • 00:05:40 variables to process end and then it was
  • 00:05:42 JWT key in my case here I added this in
  • 00:05:45 the last video and then the next
  • 00:05:48 argument are some options I don't pass
  • 00:05:50 any special options here and then a
  • 00:05:53 callback you can specify so I will omit
  • 00:05:55 the last two values here I just want to
  • 00:05:58 verify it like this now this method
  • 00:06:01 verify will actually throw an error if
  • 00:06:03 it fails so what I'll do is I'll add a
  • 00:06:06 try-catch block here I'll try decoding
  • 00:06:10 the token so I'll try this code here but
  • 00:06:13 if I fail then I will catch my error and
  • 00:06:19 in here I will then return a response
  • 00:06:23 where I set the status code to 401 on an
  • 00:06:27 authenticated and return a JSON object
  • 00:06:29 with the message off failed just like
  • 00:06:33 that
  • 00:06:34 if we succeed here in the try block
  • 00:06:37 though then we got a decoded value and I
  • 00:06:42 will eventually call Next to continue
  • 00:06:44 and what I also can do is I can set
  • 00:06:47 request user data equal to decoded so
  • 00:06:52 I'm adding a new field to my request
  • 00:06:54 make sure to pick one which you don't
  • 00:06:56 accidentally which you don't already
  • 00:06:58 have so that you don't overwrite it and
  • 00:07:00 now in future requests which use these
  • 00:07:03 map that this middle where in front of
  • 00:07:04 it we could extract a user data the
  • 00:07:06 decoded user data on that field now with
  • 00:07:10 that we get a middle where let's now try
  • 00:07:12 it out and let's go to the products
  • 00:07:15 file a page maybe and let's add it to
  • 00:07:18 the post product route so GAD should be
  • 00:07:21 publicly accessible but we want to add
  • 00:07:24 our middleware to the post route so I'll
  • 00:07:27 first of all import it I'll add and you
  • 00:07:29 can't set the top here check off and I
  • 00:07:32 will require it from middleware check
  • 00:07:36 off and this is now the function we're
  • 00:07:39 exporting there which is a valid
  • 00:07:41 middleware function and now in the post
  • 00:07:43 route here I will simply add check off
  • 00:07:46 in front of this upload single and the
  • 00:07:51 other handlers so this will run first
  • 00:07:53 I don't execute it I will just edit like
  • 00:07:57 this and Express will automatically pass
  • 00:07:59 requests and so on into this now let's
  • 00:08:01 try it out let's go back to postman and
  • 00:08:04 let's create a new post request to
  • 00:08:07 products let's switch to foreign data
  • 00:08:11 again and make sure you enter your valid
  • 00:08:13 data here and choose a file I added a
  • 00:08:16 very Christmasy file here again and
  • 00:08:19 let's now simply try this out let's now
  • 00:08:22 hit send and first of all go to headers
  • 00:08:26 and disable this content type and hits
  • 00:08:29 send again and we get all failed because
  • 00:08:32 the token is missing we're checking if
  • 00:08:35 we got a token in the body
  • 00:08:38 so let's simply add one let's first of
  • 00:08:41 all make sure we can login I'll create a
  • 00:08:44 new tab with a post request again queue
  • 00:08:49 localhost
  • 00:08:52 3000 slash user slash login and asked
  • 00:08:58 before I'll add a header here content
  • 00:09:01 type application Jason and a body raw
  • 00:09:09 JSON data where I will set an email
  • 00:09:13 password combination that exists in
  • 00:09:15 database well again use test free at
  • 00:09:17 test calm and your password was tester
  • 00:09:22 now if we use that and we sent this
  • 00:09:24 request I get my token so I'll copy that
  • 00:09:27 and now go back to the first tab where
  • 00:09:30 I'm setting the post request to products
  • 00:09:32 and I'll add my token key here and add
  • 00:09:35 token as a value I now hit Send I still
  • 00:09:39 get all failed though now it can be
  • 00:09:42 difficult to find out why the reason is
  • 00:09:45 that in this post route remember we're
  • 00:09:47 getting full and data were not getting
  • 00:09:49 JSON data now in the app J's file we
  • 00:09:53 only get body parsers for URL encoded
  • 00:09:56 and JSON content type requests neither
  • 00:10:00 is the case here we got form data and
  • 00:10:02 we're taking care about this with the
  • 00:10:04 upload single mail aware which simply
  • 00:10:06 parses form data requests and extracts
  • 00:10:10 the file along the way but it does this
  • 00:10:12 parsing since check off runs prior to
  • 00:10:15 this request body will not be populated
  • 00:10:19 because it hasn't been parsed yet
  • 00:10:21 now one workaround of course is to
  • 00:10:23 switch the order here we can put check
  • 00:10:26 off as the second middleware if we do
  • 00:10:29 this and ahead send here then we do
  • 00:10:32 simply a continue we just fail with the
  • 00:10:35 creation of the uploads directory here
  • 00:10:38 because I deleted that in the meantime I
  • 00:10:40 would have to re-add it if I do that
  • 00:10:44 uploads
  • 00:10:48 and I had send again it succeeds and we
  • 00:10:53 can see the file here then so then it
  • 00:10:56 works this is one way changing the order
  • 00:10:58 yeah we can do this we're doing some
  • 00:11:01 unnecessary work here with the parsing
  • 00:11:03 since we don't check prior to it if we
  • 00:11:06 are authenticated and in general we
  • 00:11:09 might not want to pass the token as part
  • 00:11:12 of the request body because what do we
  • 00:11:14 do for get requests sure we could add it
  • 00:11:17 to the URL in a query parent or example
  • 00:11:20 and we can't do all of that but a
  • 00:11:22 typical pattern is to put the token into
  • 00:11:25 the header so I'll remove this key here
  • 00:11:28 I don't want to send to token like this
  • 00:11:30 I want to send it as part of my headers
  • 00:11:33 and there's one common header we use for
  • 00:11:35 this the authorization header
  • 00:11:38 authorization as the name implies should
  • 00:11:40 hold any information we need for
  • 00:11:42 authorizing a request and there a token
  • 00:11:45 is typically sent by adding bearer
  • 00:11:48 whitespace and then the token so the
  • 00:11:52 token you previously previously sent
  • 00:11:54 here in the request bodies let me copy
  • 00:11:57 it again
  • 00:11:57 and put it here after Bearer now why
  • 00:12:01 bearer notice is simply a convention
  • 00:12:04 used to indicate that this authorization
  • 00:12:06 header bears a token it's basically an
  • 00:12:09 alternative to basic HTTP authentication
  • 00:12:11 so hence bearer the tokens of course the
  • 00:12:14 interesting part with this setup we
  • 00:12:16 don't need to parse the body to get the
  • 00:12:18 token we just need to have a look at the
  • 00:12:20 header so in check off we now get to
  • 00:12:24 token from the header so I'll first of
  • 00:12:27 all simply try to get my token from the
  • 00:12:30 header and I can do this by accessing
  • 00:12:32 request header a headers authorization
  • 00:12:38 if I do this let's simply lock to token
  • 00:12:41 here if I now again sent my post request
  • 00:12:45 to create a product it now fails here
  • 00:12:49 because I removed the token from the
  • 00:12:51 from the body remember I unchecked this
  • 00:12:54 year I'm no longer sending it so it
  • 00:12:57 fails in the console log we see this out
  • 00:13:00 though so we did successfully parse the
  • 00:13:02 header and that of course means we can
  • 00:13:05 now use the token from there we just
  • 00:13:06 have to make sure that we don't include
  • 00:13:08 the bearer and the whitespace so what
  • 00:13:10 I'll actually do is I'll get my request
  • 00:13:13 headers authorization and split it by a
  • 00:13:17 white space I then access the first
  • 00:13:21 segment where the second one actually
  • 00:13:23 with index one which will be this part
  • 00:13:25 after the white space and then I can
  • 00:13:28 pass my token here to the verify method
  • 00:13:31 all safe doesn't go back to products and
  • 00:13:33 now revert the order here again so I'll
  • 00:13:37 now add check off prior to trying to
  • 00:13:40 parse the body here
  • 00:13:41 and now if we save this make sure that
  • 00:13:44 authorization is set to your token with
  • 00:13:46 bearer in front of it and if I try to
  • 00:13:48 send us again it now succeeds if I
  • 00:13:52 change to token by for example removing
  • 00:13:54 the e and therefore all basically send
  • 00:13:56 an invalid token it fails and this is a
  • 00:14:00 nice middleware we can now use to make
  • 00:14:02 sure that we protect our routes let's
  • 00:14:06 add it to multiple routes then instead
  • 00:14:08 of just adding it in front of or in our
  • 00:14:11 post route here I'll also add in the
  • 00:14:14 patch route
  • 00:14:15 so before we there have our main
  • 00:14:17 handling function and in the delete
  • 00:14:20 route not in the to cat routes as I
  • 00:14:22 mentioned and the same in orders there
  • 00:14:26 I will now also import my check off
  • 00:14:29 middleware by requiring this from the
  • 00:14:33 middleware folder check off and I will
  • 00:14:36 add it there to in front of all routes
  • 00:14:38 actually because all should be protected
  • 00:14:40 including the GATT routes so getting all
  • 00:14:43 orders posting new orders getting a
  • 00:14:46 single order and deleting order
  • 00:14:48 everything is now protected and now if I
  • 00:14:52 save this we can try it out we train can
  • 00:14:55 try getting products and this should
  • 00:14:57 succeed even if we remove that header
  • 00:15:00 because it's not a protected route if I
  • 00:15:04 try to delete a product though for this
  • 00:15:08 let me quickly get an ID like this one
  • 00:15:10 so if I now add this in the URL as we
  • 00:15:13 have to do it
  • 00:15:14 for a delete request and I sent this
  • 00:15:15 without the authorization header I get
  • 00:15:18 all failed if I add the authorization
  • 00:15:20 header I still get it because it's still
  • 00:15:24 the invalid token so let me reverse this
  • 00:15:26 by adding de again now it succeeds and
  • 00:15:29 if I remove it just to prove this again
  • 00:15:31 and of course fails so this is now an
  • 00:15:34 option and now let's check our orders
  • 00:15:36 there we can't just try to get all
  • 00:15:38 orders if I don't add the token it fails
  • 00:15:41 if I do add it it succeeds so this is
  • 00:15:46 how we can now use the token how we did
  • 00:15:48 add our middleware which we can now
  • 00:15:50 conveniently add to any route that
  • 00:15:52 should be protected and we're using the
  • 00:15:55 tray WT concept for this authentication
  • 00:15:58 flow and for making sure that the client
  • 00:16:01 can identify himself to our server and
  • 00:16:04 access protected resources without the
  • 00:16:07 server having to store any information
  • 00:16:09 about the connected clients this is now
  • 00:16:11 true stateless authentication
  • 00:16:14 implemented in our restful service
  • 00:16:16 through JSON web tokens now with that we
  • 00:16:20 made a huge step forward we added
  • 00:16:23 authentication