Role-Based Authorization with Firestore

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
rule-based authorization is the cornerstone of any user moderated site a good example is Stack Overflow which provides users various privileges based on the amount of reputation that they've gained on the site in this episode I'm going to show you how to use angular 5 and firestore to build your own role-based authorization feature my goal is to give you a solution that is both flexible and secure on the front end and the back end it works by assigning a role to a user that saved in fire store then that role is used to walk down certain actions in angular the default role is a subscriber which only has the ability to read data then we have an editor that can read and update data and of course an admin user that can read update create and delete if you're brand new to the channel make sure to subscribe and you can follow along with the source code at angular firebase com I'm starting from a brand new angular app with angular fire two installed you can find the install instructions on the official repo the first thing I'm going to do is generate a core module to isolate all the logic for authentication and authorization then inside that module I'm going to generate a service called the auth service now before we get into the actual auth code I want to warn you that I'm going to go through it very quickly because I covered it in episode 55 which was fires store Roth if you get lost refer back to that but let's quickly review the sign-in signup process first I'm going to import our angular fire libraries and then I'm going to define a custom user interface but before we define the user let's actually define our roles as I mentioned before the three roles that we have our subscriber editor and admin the roles will be saved as an object in fire store with each possible role being set to a boolean value this allows you to assign multiple roles to a single user if that's what you desire then on the user itself we'll just have a user ID and email and then the roles property will be set to this roles object interface let's go ahead and switch back to the service and then we can set up an observable that is type two this user interface to authenticate a user and use that fire store document we'll first listen to the angular fire off state and once we get that we can then retrieve the actual user document from fire store so if the user is defined then we'll pull that document from fire store and call value changes to get it as an observer otherwise what is returned and observable at null from there we need a way for the user to authenticate so we're just going to use Google OAuth for this example and when the user successfully authenticates we're going to run this update user data method the purpose of that method is to create or update the user document in firestore the way we do that is we create a reference to the actual user document with the authentication user ID and then we can set up an object here type to our user with a default role of subscriber then we can update this data without actually overriding it by calling that reference set and then pass it the merge true option this will create or update the existing document in a non-destructive way so at this point we have a basic authentication system but we still don't have any role based rules in our app what I've done is created a couple extra components here this one happens to pull some data from firestore and we'll set up rules that prevent the user from updating or deleting this post the test post just has some content and a title property then I set up another component called the super secret component that's only for admin users and that's where we keep all of our bitcoins the way I like to implement rule-based authorization is to create a set of abilities and then assign those abilities various roles that are authorized to perform them what I'm doing first is creating a helper method that takes the user and the allowed roles from one of these abilities and we'll check and see if the user has the appropriate role to perform it so if the user is not defined then we'll just return false otherwise we're going to loop over the allowed roles and if the user has that role then we can return true otherwise we'll just return false now I'm going to define the abilities right here in the service but you could extract this out into its own class if you have a really complex set of roles and abilities so the first one is can read and this will determine if a user can read a document or not so right now the admin editor and subscriber all have access to read as you'll see later we can use this method to easily hide or block content in our components or in a router guard to easily control access throughout the application now we can repeat this pattern for whatever ability is that we want to provide to these roles so I'll go ahead and create another one here for can edit and then I'll create one more for can delete which is only allowed to be performed by the admin user keep in mind that anything you do in angular only provides front-end security it would be trivial to reverse engineer and circumvent these rules so before we do anything on the front-end we're going to write some back-end rules in firestore that will guarantee data security what we want to see is the spire Store error whenever we try to read or write any unauthorized data currently I don't have any security on the test post in my fire store rules in the firebase console but I'm going to use the get method to retrieve a document from the fire store database based on this request auth user ID so that authenticated users document is going to have the roles object so we can read the data then look at the roles and for the subscriber we want to make sure that that value is true in other words if the current user tries to read this document but they don't have the subscriber role then it's going to block access to that document the one glaring problem here is that this line is super long and it's going to get completely out of control if we try to repeat it for multiple roles luckily fire store rules allow you to write functions so we can write this in a much more readable and expressive way I'm going to create this get role function that takes the role as a string as an argument then I'm going to repeat that whole get line that I wrote just a second ago but instead of calling subscriber will call the argument from this function now we can just delete the previous rule and we can rewrite it using this function so we'll say if get roll with the subscriber roll and that should equal true then we can do the same thing for the editor and admin roles as well and we can lock these down based on the type of operation so we can say allow update if get role editor equals true and then for admin we'll say allow create or delete if the admin role equals true now I'm going to go into fire store on the user document and update their editor role to true then I'm going to log into the app and if I try to delete a page I should still get an error because they don't have the admin privilege however if this user performs an update then it goes through in fire store and doesn't trigger any kind of error in the console that's how you keep your data 100% secure in fire store but it's better if we never have to trigger these rules in the first place I'm jumping over to a component called subscribe page and this is where we're pulling the actual test pose from fire store so I have a reference and a post observable and then I have a variable for the user itself if you want to walk down a specific method in the component you can subscribe to the user during ng on an it and then use it to lock down certain code based on whatever users have that ability I'm going to create a couple methods here one to edit a post and another to delete a post then if you want to lock down a method based on a certain ability you can just do if can edit and pass it the user object then just wrap the important part inside of this if statement that's one way to use the auth service but it's actually a lot easier if you perform this logic inside of the template itself in most cases you have buttons or other elements that the user will interact with to trigger some kind of method we can just hide these elements from the Dom using ng-if and are off service and that's usually a much more maintainable way to go but there's actually still an even better mechanism that we can use in angular to prevent unauthorized access to content and that's what the router guard to lock down certain routes based on the user's role so first I'm going to create a guard called the admin guard and add that to the core module inside the guard I'm going to import our auth service as well as tap map and take from rxjs then the auth service is injected in the constructor and we're going to return an observable from this guard inside of can activate and it's important that you map this observable to a boolean so first we'll call pipe and then we'll pipe in a few are X operators here first one is take one to prevent a running subscription then inside a map we'll see if the user object exists and if so we'll also check for the user roles object for the admin property then we'll do a ternary if statement here and return true if true otherwise false then we can use that tap operator which recently replaced the do operator to console.log an error if the user is trying to access an unauthorized area but you could also use the router here to redirect the user to a different route so this is going to lock down a route based on a certain role but we can also lock down round a certain ability because if you remember multiple roles can be assigned to a specific ability all of the codes exactly the same we just have to change one thing down here in the map operator so instead of looking at the actual role itself will pass the user to the can read method from our off service and that will authorize any roles that can read a document to put these guards to use will go into the router and then we'll import the guards and add them to the can activate array and each of the route objects if you can lock things down at the route level it's going to simplify your code base overall and it also has the added benefit of not requiring a read in fire store to see if a rule evaluates to true or false as you can see here we have an unauthenticated user and they are denied access to any of these pages just based on the router guard then if we log in with the editor role we can go to the main content page but that admin page is still locked down so on a final note just to sum up the bottom line you should be using back end fire scorer rules for your actual data security and then on the front end use router guards and additional logic to enhance the user experience that's it for role based access control with fire store if this video helped you please like and subscribe and if you want to learn more consider becoming a pro member at angular firebase com look at access to exclusive content a free copy of my book and one on one project support thanks for watching and I'll see you soon [Music]
Info
Channel: Fireship
Views: 103,184
Rating: undefined out of 5
Keywords: angular, angular 2, firebase, webdev, app development, typescript, javascript, lesson, tutorial, acl, role based access control, firestore user roles, firestore rules, angular 5, firebase auth, firebase user rules, angular firebase users, user permission, user management firebase, role based auth
Id: 1PEdd2rtG30
Channel Id: undefined
Length: 10min 32sec (632 seconds)
Published: Fri Dec 08 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.