Node express authorization | login roles in Node

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
now what is going on guys today we are going to implement authorization in knowledge that means how do you actually prevent someone from performing a specific operation that he or she does not have the rights or the privileges for and this is what we are going to do today but before we do that i just want to dive back into the topic of authentication and authorization and i already have like a pretty long video series on this channel on how to implement a session authentication so how to log someone in with sessions and what we're now going to do is we're going to build the authorization and we're going to build this on top of our session implementation right so if you want to block someone because he does not have sufficient rights then obviously that person needs to be logged in and one of the ways on how to be logged in is by using a session now i highly encourage you to check out like this series about the sessions i'm just going to explain it like on a conceptual level here and then we're just going to reuse what we have written before and then we're going to implement like a role system basically so when we're talking about sessions or about authentication we're talking about logging someone in and the way this works is you have like some email and some password like on your browser and what you do is the user fills out the form you submit like the data and then you make like some form of requests to your servers for example like a post request with the credentials and what your server is going to do is your server is going to check like the password so what the user has entered and it's going to hash that password and it's going to compare it with the password hash that it has stored in the database so this is like the caveat right we don't compare plain text passwords but we only compare like password hashes so b crypt hashes basically so i cover all of that in the previous series like where i talk about sessions and then if the passwords passwords match what we are going to do is we are going to create like a session which contains a couple of things so for example language settings and also the roles typically and you store that like in your session stories so for example in redis and then you return like a cookie with a session id so once a person logs in and the password hashes match you create a session which has a specific id and this i and you take this id put it inside of a cookie and return it and obviously like the cookie should not be accessible via javascript yeah so this is like how you actually log someone in and what we want to cover today is how do you actually prevent someone from performing like an operation that he or she does not have the rights to and the way this works is let's just imagine you are locked in and you want to perform a particular operation that is critical so for example you might want to delete an item and the way that you typically handle this on the server is that you make like this request and then you somehow check what roles the user has okay so if you have a session implementation you would fish out like the session from your session storage and then you would check okay what roles does this person have and if this person has like the has sufficient roles then you allow the operation however if the person does not have sufficient roles you block the operation in that case you give back a uh 4 3 forbidden error and the important thing here is that this authorization thing it's not like always coupled to sessions right it it doesn't matter what authentication uh flow you use under the hood you could use any other flow the only important thing is that you are somehow able to retrieve what roles a particular user has okay so actually this whole flow is kind of like agnostic regarding the underlying authentication flow so you could have also used json web tokens although i would not recommend that because json web tokens are not like good session tokens i also have a video on that one where i explain why that is okay and this is what we want to implement today so we don't want to mess with all the stuff over here so that's why we can just take the result of this session series and then just build that stuff here on top okay so this is what we're going to do so let me go to the repo so i'm going to put the link in the description down below this is my github you can see the project over here and if you go to code then you can either like download it with https or you can just clone it and this is what i'm going to do so i'm going to copy this and i'm going to open my terminal and i'm going to say git clone and then i'm going to paste this okay and i should soon have it yes okay so let me open this one up so what is it called it's called express session with redis okay so here it is nice okay let me open like a terminal over here this one a little bit like this or maybe let's do it in here we can directly npm install uh all of the dependencies oh i have to first uh switch change to the directory and then npm install everything okay so now you should have the result of the series that we did before and that is going to work now one more thing i want you to know is that in order to actually run this project you need to have redis running locally so if i have redis running locally it's called brew services or it's maybe a little bit big like this so you can see i even have uh redis and postgres running at the moment and in this repository in the readme you will find like instructions on how to do this at least like for mac os so obviously in order to do all of this so to store like us like to create the session we need to store it somewhere in the session storage and in the previous tutorial we just use redis we could have used any other solution but redis is like typically the tool of choice for something like that so i have red is already running and that's why i can just uh start up this project so i'm just going to go over here and i think we have like a if you go to the package.json you can see we have a dev script and this one has like hot reload so this is uh really nice so i am just going to run npm run dev and i think you probably don't see the command because my image is here but yeah now you can see that it's running on port 8080. and i just want to show you like what we have already so there is a no let me go to the routes and there are a couple of endpoints uh three of them no yeah these ones here uh no it's two um you can log in so if you make a post request to slash login with the email and the password and the email and the password is correct then you are logged in and then if you uh make like another follow-up request then you will be able to get this or to reach out to this endpoint which is just going to uh return the contents of the session just to show you like what we have okay and i just want to demonstrate that to you so i'm just going to go to postman so i already have like a collection over here and the important thing is that in this tutorial we kind of hard coded the users that we have because we did i did not want to you know connect to a real database and do all of that so that's why i said okay let's just take two users and that's it okay so i'm just going to put this over here and if i now make like this request you can see that i have a cookie over here so first of all let me delete all of this so that you see it's not a leftover so if i make this request you can see over here that we have some form of of cookie and there then you can do i'm just going to duplicate this one then you can do uh i'm going to take this i'm going to take profile and i think we also need to have this origin header simply because um of security reasons so that means the browser will not take your cookie if you have uh origin star so this was like one little detail so if you now make like this request bam you can see what you actually have in the session so as you can see we have like an id and we have like a roles array and at the moment this is like admin so obviously like on production you would not return something like that um but yeah in our tutorial we do so that we know what's going on okay so this is basically the full recap of what we did before and the question now is how do we actually well implement all of the all of this authorization logic and what i want to do here i'm going to take this terminal i'm just going to say i'm going to make a directory and i'm going to call it authorization and the important point here to realize is that we first have to define what roles we are going to allow so this is the first thing that we will do so i'm just going to create a role.js file and instead of this file i'm just going to hard code like some roles that i want so let's see let's say we have two roles okay because we want to keep things simple let's say i can be a compliance officer right so a guy who kind of makes sure that everything works according to like the law and the rules and the guidelines and i can just copy that and this is like yeah right so we have a role and the role name is basically uh compliance officer in all caps and then let's say we also have customer service okay i'm like don't worry about these things um they are just arbitrary more or less they're like for our example here so let's just do two rolls because otherwise uh it's going to get really big and i don't i want to keep it as simple and small as possible okay so now we have two roles so let's say a compliance guy can only read but a customer service can read create and delete something right because the compliance guy is not involved in the day-to-day operations so that's why he's not allowed to change anything so to create something or to delete something and at the moment we only have this profile route here and this is what we need to change so we need like more routes so we like i can demonstrate you how that works so i'm just going to say okay i'm going to make a new route and this time i'm going to not make it like profile but i'm going to make it slash order right so we're just going to pretend you're like a company you have a customer service and the customer service can take orders right so they need to read orders they need to be able to write orders so to create new ones in case their customers on the phone and they should also be able to delete orders in case like something gets cancelled okay so i want to have a get request to slash order i want to have a post request to slash order and i also want to have a delete request to slash order okay and the idea is that only customer service is allowed to do all of these three and that the compliance guy is only able to read so this is like the goal and the first thing that we want to do is let's just get rid of this and i'm just going to rename this real quick right so i want to rename this to order and i am going to delete this for now and i'm going to say okay i will have like an order controller and in case you're wondering why i'm using classes is because typically i am using dependency injection and there it's kind of handy to have classes okay so i'm just going to create three methods so it's like get an order create an order and delete an order okay so these are the the methods that we are going to use in the routes that we just created and since we don't have any dependency injection at the moment we can just export like one instance okay so this is kind of like you don't really need that technically you also don't need this class but later on if you want to add dependency injection might be worthwhile okay and for this order thing um what are we going to do well i think we're just going to hard code like something we're just going to say type and order and content uh lorem ipsum okay it doesn't really matter and to create order we're just going to create to return [Music] maybe let's return 201 and then say uh order created because that's what you should do right you should say okay to one if it's created and uh let's just say here we just say order deleted okay so we're basically pretending like we're doing something but actually we don't so now we have this controller we have these three methods and now we want to wire them up over here so for one i want to have an order controller right and this guy and then i can say here order controller dot get order and what don't invoke this method don't do something like this but just pass the reference to the method okay and then you can also say order controller dot order controller dot create and then you can say order controller dot delete okay and i by accident imported this so these would be all the three routes that we want to support so read create and delete and now what we want is we only want to allow uh compliance someone with a compliance role to kind of like have access to this route and customer service should have access to all of these three obviously in reality these things are more complex but let's just try to keep them simple okay and the question is how do we actually do that and now it gets pretty interesting because this is how you would actually implement an authorization so i am going to create a new file and i'm going to call this authorize.js and this is the actual like logic that you need to understand to to authorize it so the idea is that we want to have maybe i can show to you first that we want to have some middleware where we say okay we want to authorize like a particular role um let me just function authorize and i'm just going to export this then we can already wire it up you know and at the moment we're not doing anything but and then we want to import the role require just that you know how it's going to look so we are going to have like a this function and in here we want to pass like all the the roles that should have access to this role okay so for example i want the compliance guy to have access so i'm going to pass compliance and i also want customer service so we can put this in line here because we only have two roles obviously if you have more roles you want to put like this to a different file and for the other two we actually want we only want the customer service to be authorized right so what we want is like this and we want roll dot cos customer service and this is how we want to define who has access to what i don't see it because the camera is just in the way is there now a comma i don't know yeah i think it is okay yeah so at the moment we're not doing anything but you i think you get the idea right basically what we're doing is we want to plug in an additional middleware which is like this authorized function and this authorized function is going to block you if you don't have the proper route and if you do have the proper route you can execute it and now the good or the valid question is how does this function look like so at the moment what we want is we have a variated parameters a parameter and what this means is this function has an arbitrary amount of parameters and it's going to be passed as an array why ah just because i'm lazy and i don't want to write something like authorize and then pass an array right i could have done that as well but it's kind of ugly so let's just use this little feature so to have an arbitrary amount of parameters in the in the function method or in function signature and these are like the roles that i want to allow okay so in that case when we call this function we're passing here we're passing two roles or maybe we pass one so we don't know how many are going to be passed and this one or roles is going to be an array okay and now let's think about what we want to do so for one what i typically do is i say i create a set of this and a set is a hash set like that's how it's called in java so it's basically a data structure that has the same semantics as a set in mathematics and while that sounds complicated it basically just means that there are no duplicates in this set and there's a very specific reasons on why we do that because one of the reasons is that from this set you can retrieve or you can check whether an item is contained in that set with a constant amount of time so it's just a performance kind of thing now if you're not sure what this means and just check out like hashing and check out um yeah hash sets in general so there's like a lot of tutorials and basically the thing is if you have a very long list of roles you don't want to iterate through all of them to check whether one item is contained in it but you just want to calculate a hash and then figure out whether it's contained okay and what we are now going to do is we're going to return a function and this might look a little bit weird because this is a function and this function itself will return a function and it will return a middleware so that means it's more or less like a middleware factory so you give it like a couple of parameters and it's going to return you like a custom middleware and this custom middleware is going to block the request if you don't have sufficient privileges and the first thing that we want to do just to be on the safe side if there's no session or if there's no session no user in the session or if there's no wait if there's no requested session.user.roles then we are going to block right away and now you might wonder like why is this dot user and why don't roles well it is because we defined it like that so if you look at the if you look at the response so this is the exact session and what you see is it has like one the the top level object and then it has user and then it has roles so this is just how i defined it in the project it could have been defined in a totally different way so if i think if you go to auth and yeah if you go to auth and to log in you see that we are basically attaching like the data we fetch from our fake database to the user object in the session that's why it's nested like if we had done this actually we could do that if we had done this then we would not have this user object okay but i'm just going to leave it in here okay cool so now that we have oh no here now that we have all of this we need to basically return forbidden and we're going to do this afterwards okay so the idea is if we don't have any information about the roles we're going to block right away and now the other question is well what happens if we actually do have roles in our session so user is logged in there's like a session array inside of the redis store that is associated with this session how do we actually check whether that person is authorized or not and i'm just going to make another function and i'm going to call it is authorized okay and what i'm going to pass is i'm going to pass the actual roles so the roles that the logged in user has and the roles that we want to allow so in our case the allowed roles is you know customer service or compliance right so in here right in here the allowed roles would be compliance officer and customer service and here the allowed roles would only be customer service okay and now we're going to use a little bit of fancy es6 syntax so we're going to say return user roles dot sum role and then allowed whoops i still don't see it because the camera is over here uh wait over here allowed rolls dot has a roll now this looks a little bit weird i would assume so let me just explain what this does so what we are doing here is we say okay we're going to iterate over all the roles that the user has okay so if you're logged in you have a particular role and we're going to iterate over these roles and then we're going to check if at least one of the roles that our user has so this is basically the sum method if at least one of the roles a user has is contained in our allowed role set and this has method is a like a method on the set that we created over here and just for you guys to know in case you are concerned about uh like runtime complexity right this is off and runtime where n is the amount where n is the amount of roles the user has and now you might also see why we are actually using this set because if we had not used the set what would we like how would we have written it or we would need to write it differently we would need like two loops right for one we would need to loop over the roles that our user has and then for every role we would need to check if it's in the loud role so we would actually need to do something like user roles dot you don't need to write this by the way for each and then user role and then you would need to do something like this okay and then you would need to say okay now i need to iterate over the allowed roles i know this is complicated i'm not going to write it like that maybe in a for loop maybe it would be easier in a for loop okay so i'm going to say user role in user roles just for you to understand this okay so we would iterate over how does this work i think it works like this how does this work in javascript i think it's in you know sometimes i get confused with different programming languages so you would need to like if you want to write this out in real loop you would need to say okay i iterate over these roles and if the allowed roles were an array i would need to say okay roll in allowed roles and then you would need to check okay if r is the user role then return true and as you can see this would this would have like two different roles or two different loops and we want to avoid that and this is basically the trick with a hash set because this operation here has like constant run time so we save ourselves in a loop but that's more like details and this is assuming obviously that both are arrays okay so let me delete all of this um cool so this is like our method and what we're now going to do is we will need to check if the user is not authorized okay so we're going to check if the we're going to pass the user roles roles and we're going to pass the allowed roles okay allowed roles is supposed to be a set obviously and if the guy is not allowed then we're going to return forbidden maybe it makes more sense to return unauthorized and if the guy is allowed to do it we're just going to call next and we haven't implemented like these two things here yet but maybe just for to recap okay what we do is we first take like the roles that we want to allow then we put them inside of a set and then initially we check if the logged in user has some roles if he does not have the roles we block right away and then if the person has roles we check whether at least one of the user roles is contained in this set here and if yes then the person like can perform the operation if not then the person cannot perform the operation okay so these two things like they will work um another thing that i want to do here technically speaking what we could do is we could do something like okay i want to return a 401 and i'm going to say you are not logged in okay so like this and yeah i think maybe i'm going to do it like that uh insof forbidden insufficient privileges previous okay so in that case we return unauthorized and in the second case we return forbidden so forbidden means you are logged in but you don't have right to perform the operation and this guy here means you are not logged in so we can't do anything and by the way normally what i would recommend is that you have a dedicated like error handling middleware and that you do not hard code like these values in here yeah i'm just going to to leave them in like that because the video is already 30 minutes so i already have i also have like a video about error handling like a note please check that one out this is like how you can handle the errors properly maybe i'm going to add it later on but i don't want to make this video too long after all okay so that's it pretty much i would say yeah so anything anything else we forgot i think not i think we could actually go ahead and try this out but here's like one problem authorize is not defined okay oh i didn't import it right const authorized equals require [Music] authorization authorize okay okay and let's see yeah now it's running so let's see how that works oh there's one more thing we almost forgot uh we need to create users with the proper role so i'm going to just hard code this so i'm going to say compliance and i'm going to give this like the compliance role ah you know actually require authorization role so i don't want to hardcode this in there because it's uh you know what happens if the name changes so i'm just going to give this guy like compliance and here i'm going to give this guy a customer service and i'm just going to call the service and i'm going to give both password because i don't want to type in different passwords okay something like that cool so now our server is starting and actually this should already work unless we did like a mistake so let's try it out let's log in as a compliance guy okay so we have compliance and we have password let's see okay so now we are logged in that should be fine let's see we should actually be able to get to make a request to this thing yes so you can see yes we can make a request to slash order simply because we have uh like we're authorized to do so okay so everything is the same now let's try to create an order now remember we're still locked in as the compliance guy so let's see whether we implement it correctly bam you see nice so now you can see this guy like he has like the compliance role so he's not allowed to log in and that's why we are immediately blocking him in here so we just say oh sorry insufficient privileges so this is where we kind of kick him out um yeah and also normally okay maybe i'm going to do it later let's let's first i'm going to implement the proper error handling middleware at the end okay so you can decide whether you want to watch it or not and let's try with delete so i'm just going to say okay delete and bam yeah we're also forbidden that's good let's try to do it differently so now we're logged in as customer service okay bam as you can see we get a different session cookie let's try to make a request yes we have it let's try to create something bam and now you see okay cool apparently if we are customer service we can create an order because we allowed them to do so in our server and we are also allowed to delete an order at least that's what i think yeah and you can see here that we get like a 200 status code and in here we got a 201 so everything is fine yeah so this is basically the easiest way of or the easiest form of implementing an authorization now obviously this can become much more sophisticated so for example um you might not be able to to uh write to to like you might not be able to create all orders but you only might be able to create specific orders right so depending on what item we're talking about you might have different privileges you can think of this like think of it like facebook groups right just because you're an admin in one group doesn't mean like you're an admin in all other groups so then you need to dynamically load like the roles from the database and that kind of stuff but i don't want to show it here because it's too complex so once you understand like this principle here it's going to work yeah so that's it pretty much for the authorization i'm just wondering whether i should whether i should do this here properly because i don't i don't want to have it like that okay i'm just going to really quickly do it okay um what i want to do is i want to make a directory and i want to call it errors and the reason i want to do that is i don't want to hard code like these status codes in here that's very ugly so i want to create an another file which is called api error and by the way i'm going to do it very quickly but i already cover in another video on how to do this so please check out this video if this is a little bit confusing so i'm going to say code message and i'm going to say this dot code equals code and this dot message equals message so i'm basically going to say i will define that there are specific types of errors unauthorized that are expected and where i want to return the actual error message turn you api error 401 message okay because what is important typically on a production setup as well is that you do not expose any information on like how your server is actually working because this is dangerous right people can hack you and that's why you want to be very careful with what you return as an error message because you never know if the person who's actually using the service actually wants to hack you or is like malicious in any way and that's why we just define okay we just define an api error class we say it has a few static methods so if i call this it's going to return an api error just an instance of this class and then i'm going to say api error handler and this is by the way best practice so every node server that you write really yeah i don't want to say should i actually yeah i would actually say it must have it's a must in my opinion but technically speaking it's not a must because you can write a server without having an error handler but this is it's just a very bad idea so um this is like an error request response next and by the way an error handling middleware has like this additional error parameter right so normally you have request response next and the error handling or the error handler has the arrow property over here and i'm just going to lock this by the way um again i think i've mentioned this 5000 times in prod do not use a console.log or console error or console warn or whatever console warn because they are not synchronous because they are not async so they are blocking the main thread and you don't want to have that so what i'm just going to say is if the error that i'm receiving is an instance of api error so that means if it is an expected error i'm just going to return like the error code and i'm going to return the exact error message and i'm going to return and then if not i'm just going to say oops something went wrong here something went wrong oops like this and by the way some people say oh but you could do it like this yes but it's it looks very confusing because this thing here is like a callback and you know like you don't want to return like this thing because the return value is actually like undefined so you actually only have this return to kind of bail out of this method um okay cool so you're still with me okay uh yeah so now you have this error handler over here and the only thing we've got to do now is we got to plug this thing in so we api error handler require and then is it errors and then api error handler and then i'm going to say app.use api error handler and now what we can do and you might wonder like why do we do all of that well the reason is that we can now get rid of these hard-coded status codes so we can say api error require errors api error i'm going to do you see i'm doing this as fast as i can because i don't want to uh annoy you and unauthorized um you are not logged in oops like this and here i'm going to say api error dot forbidden uh forbidden you don't uh insufficient privilege insufficient privileges okay cool so that's it pretty much i hope this works there's one more thing we can do in this project but it's actually more related to um to the course middleware so as you can see here in this course middleware we can actually make use of this as well so i'm going to say uh wait errors api error so the thing is sometimes you will forget like in your postman to actually set um dot unauthorized uh blocked by course so i just wanted to do this for the sake of completeness because sometimes if you're trying this out on your own and you forget to put like the course header so in the postman over here then you're going to get like a weird error message and now i just want to return a proper error message so in case you're running this on your local machine you know what's going on okay so hopefully this now works uh let's just try it out again so i'm going to log in as service i'm going to get like the order that works i'm going to post an order that should also work okay and then the delete order okay let's try it out as well it will but it will work and let's say compliance okay now i'm logged in as compliance and yeah now i can read but i should still be blocked over here yes perfect yeah yeah i kind of put this at the end because i don't want to you know teach you like bad code so always make sure you've got like this api error handler over here without the console.log and use a proper locking library okay cool yeah so that's it pretty much this is how you implement like a simple form of authorization uh let me know what you think about this in the comments below uh if you have any questions just let me know i'm going to try to answer it as in the best way i can you can also send me tweet my twitter is at productioncoder so thank you so much for watching like i'm glad you sticked around for so long um i hope it was useful i hope you kind of now understand okay you have authentication which is like logging in and then on top of this you build like the authorization which is you block someone if he or she does not have the proper rights yeah so thank you so much for watching leave a like and subscribe to the channel and i'll see you in the next video guys bye
Info
Channel: Jan Goebel
Views: 5,558
Rating: undefined out of 5
Keywords: node authentication, node js authentication, node authorization, node session authentication, express authorization middleware, express js role based authorization, javascript authorization basic, authorization javascript, expressjs role, node role based access control, node access control, role based access control node js, express roles, express roles and permissions, authorization nodejs, authorization node js express, authentication and authorization node js
Id: ci0c8UhV08c
Channel Id: undefined
Length: 43min 40sec (2620 seconds)
Published: Sun Jan 24 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.