How to Authorize User Roles and Permissions | Node.js & Express Authorization Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] hello and welcome hi i'm dave this tutorial is part of a nodejs and express for beginners tutorial series i'll put a link to the full playlist in the description below since creating the last tutorial i realized there's a lot of confusion concerning the concepts of authentication and authorization they're often used interchangeably or simply abbreviated as auth but they are not the same things authentication refers to the process of verifying who someone is authorization is the process of verifying what specific resources a user has access to when we log in with the username and password we are verifying who we are and that is considered to be authentication after logging in our express api issues users jwt tokens jwt stands for json web tokens while it's true that the tokens confirm the authentication process has already taken place these tokens also allow access to our api endpoints which provide our api data this is authorization a hint towards this fact is that a jwt token uses the authorization header today we will expand the authorization process by adding user roles with specific permissions to our api authorization process we're going to start with the code repository from the last tutorial but if you don't have it you should be able to easily follow along without it or you can download or clone the starter source code from the link i've provided in the description below we're going to start by going to the config folder in the file tree and creating a new file and we'll call this file roles underscore list dot js in the roles list we're going to create the user roles now this could be in a data table in a database this is just how we're going to do this and you'll find that user permissions can be constructed in an assortment of ways and we're going to apply a fairly simple structure today with three different user roles so we'll start out with our roles list constant and we're going to set this equal to an object and the keys will actually be the names of the roles so we'll have admin and then we'll have a code that identifies the role and then after that let's create an editor role and we'll call this 1984 and then let's have just a user role and we'll give that 2001. okay now that we've created our roles list object all we need to do here is module.exports and set that equal to roles underscore list okay we've saved that now we're ready to go ahead and modify our users model that we have in the users.json file and you might find that once it has been written by the read and write file process that we use in node we're using this user jsons file it might all be in one line but i believe if we go ahead and just put a return and edit and then save in visual studio code it should go ahead and format that file for you so it's a little easier to read now this is what we had after the last tutorial we have a username walt1 and a username walt2 they all have their own passwords and of course we're tracking the refresh tokens i'm going to copy in the file i've created it adds one more user and so you can copy this file out of the completed source code that i'm going to link to there's a starter source code link and then a completed source code link or you can go ahead and use the register route create your own new user and you can see i've added dave one as well as our walt one and walt ii dave one just has the role of user while walt 2 has the roles of user and editor and walt one is not only a user but also an editor and admin so you can see we're going to support multiple roles i'll go ahead and save this file once again you can copy it from the completed source code or just create your own third user if you want to and we'll just edit these manually now there could be a admin area for whatever service you were creating and that is where these additional roles could be created but what we will do is just ensure when everyone registers they're given the user role and then of course later on you'd think an admin that was in charge could add the additional roles if those roles were to be granted to other users so now we can collapse the model folder and the config folder but let's look inside the controller folder and let's look at that register controller that we have what we want to do is add that role of user with the 2001 code when any user registers you can see we're creating our new user right here on line 19 and we have the username and the hashed password so let's just go ahead and add this extra roll i'll break this out on separate lines and that way we can see the new part that we add and what we want to do is just in between username and password add roles let's keep that lowercase and after that let's put this in an object so then we have user and then we have the code 2001 and then remember to put a comma after roles so it goes before the password and save now let's go to the auth controller and when we authorize and create the access token we will want to send this information in the access token so we're going to change our payload for the jwts now first when we know we have a match and we have verified our user so everything is good we want to go ahead and grab the roles that we put in our users json file so let's go ahead and define roles and set this equal to object dot values and then we can pass in the found user that we have above and we want found user dot roles and so now we'll get those values inside of roles and now let's change our access token payload just a little bit since we're not just going to send the username let's create a new namespace here with user info and then this will be an object and inside this object we'll have username let me go ahead and add the closing curly brace for the object here so we'll have the username but then oh we don't need that closing curly brace but then we also want to add some more information here inside of the user info object and the next one will be rolls with quote and we'll just pass the rolls that we've created it looks like i actually did need that extra curly brace or at least we need to indent here and then we're going to need to close out our payload that looks correct i believe and if you want to break this onto another line you can do that as well go ahead and save and there we go it gets formatted a little bit better already so we're using this user info as a different name space and that's good because this is considered to be a private jwt claim because there are some res reserved abbreviations and words for public jwt claims and you can find out more about that at jwt dot io i believe i'll confirm that and leave a link in the description below so you can check out more information on the jwt claims if you're interested now there is no reason to send the roles in the refresh token and ideally the access token will only be stored in memory on the front end but we don't have control over that so when we do send the roles we're just sending the codes and not actually the word admin or editor or anything like that so we just are kind of hiding what each one is by using codes but at the same time ideally that access token would only be stored in memory anyway but there is no need whatsoever to send the roles in the refresh token refresh token is only there to verify that you can get a new access token okay now speaking of the refresh token we need to go to the refresh token controller and add some of the same code because that refresh token does issue a new access token so right underneath where we have verified the or we've actually decoded the refresh token here and we have the decoded and now we have no error and everything is good so we want to come down to this line right before we create the token and here once again let's define roles and set it equal to object dot values and we'll pass in our found user once again and the roles that are associated with that user and now we can use the name new name space as well so we'll use user info again and then we'll create an object and inside this object we'll have the username which is the decoded username but then we'll also want to put the roles once again so now we'll have roles and here we can just pass in roles once again i need a comma it looks like and now let's save and it will format a little bit better and that looks correct so we have user info with username and roles now that we've updated our tokens to include the roles our access tokens specifically we're ready to go to middleware because we're going to have to create some new middleware to verify those roles but first let's look at the verified jwt file that we already have i'd like to make a couple of quick updates one is when we define the auth header this works and especially when we have control of the front end as well and we know we would define authorization with a lower case a however it can also come in with an upper case so instead of this let's go ahead and just say dot authorization which is essentially the same thing that we had but then we can also say or and then we could have request dot headers dot authorization with a capital a as well just in case it comes in with a capital now we know we're going to grab it either way after that let's go ahead and change our if auth header as well and let's look for an optional chaining method here so we say if we have or if we do not have the auth header actually are saying and also starts with and here we want to have bearer and this is supposed to be a capital b by the standard so we don't really have to look for lowercase and uppercase like we were here so we're verifying that first of all if we do have an off header or if it starts with this and we're actually looking to say if we don't have an auth header and then this optional chaining says okay well even if we do have an auth header if it doesn't start with bearer with a capital b and then the space after bear then we're going to return a 401 because it is not a correctly formed authorization header that starts with bearer and then the token as it's supposed to it looks like we left in a console log from the last tutorial let's go ahead and take that out where we were just viewing the bearer token in the console and now after we decode this token when we're verifying the jwt we also want to set the roles here not just the user on the request so let's have request dot roles and we'll set this equal to decoded and now we've got a different name space so it's dot user info dot roles and we need to make the same change up here when we get the username because now it's decoded dot user info dot username and now we've got the correct namespace for both and we can save our verify jwt middleware and now it's time to create our new middleware i'm going to collapse the open editors so we have a little more room and in the same middleware folder let's create a new file and let's call this verify roles.js inside of verify roles we're going to create middleware named verify roles and it's going to accept a lot of parameters if we want it to it's however many roles we want to pass in and the way we do that is with the rest operator it looks just like the spread operator but it lets us pass in as many parameters as we wish and we're just going to call them allowed roles now from there we need to go ahead and have a middleware function and you know that takes a request response and next so what we need to do and this allows us to pass in the allowed roles by having this on the outside but we need to return a middleware function essentially an anonymous function here with request response and next and now inside of this function we'll have everything that our middleware would do so the first thing we need to do is say if we do not have a request which we should because our verified jwt will come before this but let's go ahead and do that just to be thorough and then let's use optional chaining to say okay even if we do have a request it needs to have roles or this should not be valid and if it's not valid we're just going to return a response and send the status 401 which is unauthorized now we need to define a roles array let's keep that lowercase camel case actually roles array and set this equal to the allowed roles that were passed in now we're spreading those into a new array here now that we have this let's go ahead and log this to the console these will be the different roles that we're passing in and of course there will be the codes associated with the roles but let's go ahead and log roles array so when we go ahead and test this we can see everything that we expect to and we'll be comparing this to the request.rolls that we just set inside of the verified jwt that will be executed as middleware before the verifies roles middleware okay so we'll log both of those just so we can see what's going on but now we know we have an array of user roles that are coming from the jwt and then we have the roles that are passed in that will be allowed and that's what we're going to compare so we're comparing arrays and i'm going to define just a result here and let's set this equal to request dot roles dot map which map creates a new array and we'll have a role and for each role essentially we'll compare to the roles array and we'll see if the roles array includes the role that we're passing in and if it does it will return true that's what includes does and if not it will return false it's a boolean so we'll have a new array and we'll have a true or false for every thing that was in the request role so if we had three different roles here we'll have possibly true true true or true false true who knows for sure but that's what we'll have so we need to filter this array and all we need is one true to know that the role can access the route that we're verifying so what we want to do then is chain find and we can just say for each value the true and false in the new array that was mapped each value will check the value to see if it's equal to true and if it is it will return if it finds a match it will return the very first one it finds or if it finds no matches then we won't have a result and so that's how this works i'm using two higher order functions here essentially so we're mapping over the roles that are sent from the jwt and they're assigned in the verified jwt to request roles and we're mapping those comparing them getting true and false results back to the roles array that will be passed into this route so we'll pass in uh however many roles we want to this array could possibly only have one role or it might have three or four or however many we're checking for or want to allow to this route so once we compare those and get all the true false results we're just using find to say hey find the first true and if there is any truths it will be good and if there's not it won't and that's what we'll do next so we'll just say if there's no result essentially we did not find a true result we're going to return and then we'll say res send status and again we'll send a 401 unauthorized otherwise we'll just call next because everything's good and we're ready to move on and we're going to let the route be accessed so let's save our middleware and we'll know when it runs we'll see the arrays that we're comparing in the console and before i forget we also need to add module dot exports and set that equal to verify roles and save now let's collapse the middleware folder and open up the routes folder and then the api folder and let's go to our employees.js that has the different routes get post put and delete we have a couple of imports to make here so let's go ahead and define our roles list and set this equal to require and now we need to go up and up again and then we'll look in the config folder and then we'll find our roles list and after that we need to go ahead and define our verify roles middleware set this equal to up a folder up another folder middleware and now we're going to verify roles and i'll save just because i've completed the imports but now we need to add this to the different routes now let's just leave the get route open because any one could access that or if we wanted to put verify roles and at least verify their user but they already have to have the jwt because we required that ahead of time so they'll have to have the jwt to access the get route so kind of the user is the default but after that in the post route let's put in verify roles and now we can pass in the different roles list values that we want to let access this route so we'll type roles underscore list and now dot admin will work remember this is the key and then the code is the value so admin is the key and then we could also put in roles underscore list dot editor and that makes sense because you could have an editor that could post a new value as well okay then i'm going to copy the verify roles that we just added here and it's like maybe i'll put a space here and then input i'll do the same thing and add the space but now in delete let's say only an admin can delete anything from our database so we'll save that which gives it just a little bit of a change so now everyone can access the get route but the post route should be limited to any user that's an admin or an editor the same for the put route but then the delete route could only be used by an admin i think we're ready to test this out now so let's go ahead and open a new terminal window you can do that from the terminal menu or i'll just use control and backtick i'm on windows and i'll type npn run dev to get our dev server up and running with our api and then we'll test this out with thunder client okay i'm going to drag the terminal window up just a little give it some more room so we can see the console log notices that we get and from there i'm going to click on thunder client here on the left if you don't have thunder client installed you can get it through the extension over here and then just search for under client and there you see it and now that you have it or once you have it you'll be able to click on it over here on the left circle with the lightning bolt and you can create collections and of course we've done this in some previous tutorials if you have followed along in the series i've got an auth collection here and the very first thing we'll need to do is authenticate a user let's look at which user we're going to authenticate first let's just do dave one remember this user only has the user permissions it does not have an editor or an admin permission so i'll send and i get the access token i'll copy this access token and now when i go to the employees api and go to git employees i'll go to auth here paste in the new access token and send and everything is good but if i do this for post if i'm in time within the 30 seconds it was given and send i got a 401 unauthorized so that means our verify rolls is working as we look down here we've got the two different arrays logged to the console too so this is the array that has the roles that we were looking for that we passed in 5150 is the admin 1984 is the editor but you can see our user only had role 2001 which is just a user okay now let's go back to my auth collection here and the auth route and let's change who we're getting a token for so now this will be walt 2 he is an editor and also a user but he is not an admin so we can send this we get a new token i'll copy the token and now we can go back to the employees api and we can post a new employee or we should be able to we'll put in the new token here and send and yes we posted john doe so he's now number three has the id of three in the employees list but if we go to the delete route and go to auth i'll post in the same token and attempt to delete and oh we're out of time we got forbidden so let me go ahead and try to re-verify here we once again got walt 2 we get a new token and get that new token once again i didn't copy it right the first time there we go back to employees now we'll attempt to delete i'll pass in the auth token and send and we got a 401 unauthorized and that is because the role did not verify now you can see you had to have the 5150 code which is admin that was passed into the route but walt 2 only had the 2001 and the 1984 code in his roles list okay once again let's go back to the auth route now and we will change to where we are authorizing walt one walt one should be an admin as well as a user and an editor and so we'll send that get a new token and now we can go back to our employees delete route that was just denied to walt 2 paste in this new token and attempt to send the delete request and employee id 3 is not found that's because nodemon restarted i believe and of course employee id 3 isn't in there so let me go ahead and request another token and we'll see if we can create id 3 again quickly and then delete it on the same so here's our new token for our admin user copy that go to the employees post route paste in the auth and created now let's go to delete in the new auth and we deleted with an okay so i just had to be quick enough to beat the 30 seconds with that token but here you can see the user our walt one user had 2001 1984 and 5150 in his roles and the api route needed the 5150 so when it matched everything was good hey just a quick note on testing with thunder client and i want to point this out in the controllers and then under the auth controller but when you're testing with thunder client it honors the cookie setting for secure true or not so if we were to set a cookie and then use the refresh token here you would need to remove this or the cookie would not work with thunder client however this is required as i noted in a previous tutorial when working with chrome so just a note if you're testing the refresh endpoint with a refresh cookie you'll have to comment this part out or at least take it out for testing purposes with thunder client but then when you work with chrome and in production you both want that secure true back in here when you create that refresh token that is saved in a cookie we did not use that today and the refresh token does not store any information about the user roles and it shouldn't however just wanted to note that for you in case you were testing out that refresh token with thunder client so there you have it we created our verify roles middleware and you can see both of those that we've been logging to the console i'll go ahead and delete those now before i commit it to github and get the console logs out of there there's our verify roles middleware and you of course saw how we applied it to the routes in our api for our employees and you can just pass in the different roles that you want to let access that given route give this video a like if it helped you get started with the express framework for nodejs and thank you for watching and subscribing you're helping my channel grow have a great day and let's write more code together very soon
Info
Channel: Dave Gray
Views: 1,684
Rating: undefined out of 5
Keywords: How to Authorize User Roles and Permissions, user roles, permissions, authentication vs authorization, Express authorization, node.js authorization, node.js auth, express auth, node.js user roles, node.js permissions, authorization middleware, node.js express, authorization tutorial, node.js tutorial, express tutorial, how to authorize users, authorization how to, user permissions, auth, js, express, node.js, user roles nodejs, user roles and permissions, user roles in node js
Id: fUWkVxCv4IQ
Channel Id: undefined
Length: 29min 4sec (1744 seconds)
Published: Tue Oct 12 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.