Give your users specific roles and abilities. In this episode, I use Angular 5 with Firestore to build an role-based access control feature. https://angularfirebase.com/lessons/role-based-authorization-with-firestore-nosql-and-angular-5/

- Firestore rules: https://firebase.google.com/docs/firestore/security/get-started
- Guards: https://angular.io/guide/router
- Role-based access control: https://en.wikipedia.org/wiki/Role-based_access_control
2024-02-04T07:51:59.854Z
nova-2
2-general-nova
2024-01-09.29447
30089e05-99d1-4376-b32e-c263170674af
1700e455-c025-464d-9718-4b899cb0fc86
23ac93eee7d8bfa64456c99ffcc745743f384c3411ef0a0f1b6c280e7179b0cf
67875a7f-c9c4-48a0-aa55-5bdb8a91c34a
deprecated
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 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's saved in Firestore.
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 add angularfirebase.com.
I'm starting from a brand new Angular app with AngularFire 2 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 is called the auth service.
Now before we get into the actual auth code, I wanna warn you that I'm going to go through it very quickly because I covered it in episode 55, they have, which was Firestore OAuth.
If you get lost, refer back to that, but let's quickly review the sign in sign up process.
First, I'm going to import our AngularFire libraries, and then I'm going to define a custom user interface.
But before we define the user, let's actually define our roles.
They as I mentioned before, the 3 roles that we have are subscriber, editor, and admin.
The roles will be saved as an object in Firestore 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.
They.
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 typed to this user interface.
To authenticate a user and use that Firestore document, we'll first listen to the AngularFire auth state.
And once we get that, we can then retrieve the actual user document from Firestore.
So if the user is defined, then we'll pull that document from Firestore and call value changes to get it as an observable.
Otherwise, we'll just return an observable of 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 Fire they store.
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 typed to our user with a default role of subscriber.
Then we can update this data without actually overriding it by calling the 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 will set up rules that prevent the user from updating or deleting this post.
The test post just has some content and a title they'll 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 role based authorization is to create a set of abilities and then assign those abilities various roles that are authorized to perform them.
They 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.
They.
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 abilities that we want to provide to these roles.
So I'll go ahead and create another one here for Can Edit, they.
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.
They 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.
They what we wanna see is this Firestore error whenever we try to read or write any unauthorized data.
Currently, I don't have any security on the test post in my Firestore rules in the Firebase console, But I'm going to use the git method to retrieve a document from the Firestore database based on this request auth user ID.
So that authenticated user's document is going to have the roles object.
So we can read the data, then look at the roles.
And for the subscriber, we wanna 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, Firestore 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 getRole function that takes they 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, we'll call the argument from this function.
Now Now we can just delete the previous rule and we can rewrite it using this function.
So we'll say if get role with the subscriber role and that should equal true.
They 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 git 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 Firestore on the user document and update their editor role to true.
Then I'm going to log in to 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 Firestore and doesn't trigger any kind of error in the console.
That's how you keep your data 100% secure in Firestore, but it's better if we never have to trigger these rules in the first place.
Now I'm jumping over to a component called subscribe page, and this is where we're pulling the actual test post from Firestore.
They so I have a reference and a post observable, and then I have a variable for the user itself.
If you wanna lock down a specific method in the component, you can subscribe to the user during NG on a net and then use it to lock down certain code based on whatever users have that ability.
I'm 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 they 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 we can just hide these elements from the DOM using n g f and our auth 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 with 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, they, 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 RX operators here.
First one is take 1 to prevent a running subscription.
They.
Then inside of 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 the 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 Ral on a certain ability.
Because if you remember, multiple roles can be assigned bind to a specific ability.
All of the code's 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 auth service, and that will authorize any roles that can read a document.
They to put these guards to use, we'll go into the router, and then we'll import the guards and add them to the can activate array in each of the route objects.
They 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 Firestore 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 the admin page is still locked down.
They so on a final note, just to sum up the bottom line, you should be using back end Firestore 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 Firestore.
If If this video helped you, please like and subscribe.
And if you wanna learn more, consider becoming a pro member at angularfirebase.com.
You'll get access to exclusive content, a free copy of my book, they and 1 on 1 project support.
Thanks for watching, and I'll see you soon.
They

Speaker 0: 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 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's saved in Firestore.

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 add angularfirebase.com.

I'm starting from a brand new Angular app with AngularFire 2 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 is called the auth service. Now before we get into the actual auth code, I wanna warn you that I'm going to go through it very quickly because I covered it in episode 55, they have, which was Firestore OAuth.

If you get lost, refer back to that, but let's quickly review the sign in sign up process. First, I'm going to import our AngularFire libraries, and then I'm going to define a custom user interface. But before we define the user, let's actually define our roles. They as I mentioned before, the 3 roles that we have are subscriber, editor, and admin. The roles will be saved as an object in Firestore 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. They. 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 typed to this user interface. To authenticate a user and use that Firestore document, we'll first listen to the AngularFire auth state.

And once we get that, we can then retrieve the actual user document from Firestore. So if the user is defined, then we'll pull that document from Firestore and call value changes to get it as an observable. Otherwise, we'll just return an observable of 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 Fire they store. 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 typed to our user with a default role of subscriber. Then we can update this data without actually overriding it by calling the 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 will set up rules that prevent the user from updating or deleting this post. The test post just has some content and a title they'll 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 role based authorization is to create a set of abilities and then assign those abilities various roles that are authorized to perform them.

They 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.

They. 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 abilities that we want to provide to these roles.

So I'll go ahead and create another one here for Can Edit, they. 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. They 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.

They what we wanna see is this Firestore error whenever we try to read or write any unauthorized data. Currently, I don't have any security on the test post in my Firestore rules in the Firebase console, But I'm going to use the git method to retrieve a document from the Firestore database based on this request auth user ID. So that authenticated user's document is going to have the roles object. So we can read the data, then look at the roles. And for the subscriber, we wanna 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, Firestore 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 getRole function that takes they 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, we'll call the argument from this function.

Now Now we can just delete the previous rule and we can rewrite it using this function. So we'll say if get role with the subscriber role and that should equal true. They 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 git 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 Firestore on the user document and update their editor role to true. Then I'm going to log in to 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 Firestore and doesn't trigger any kind of error in the console. That's how you keep your data 100% secure in Firestore, but it's better if we never have to trigger these rules in the first place.

Now I'm jumping over to a component called subscribe page, and this is where we're pulling the actual test post from Firestore. They so I have a reference and a post observable, and then I have a variable for the user itself. If you wanna lock down a specific method in the component, you can subscribe to the user during NG on a net and then use it to lock down certain code based on whatever users have that ability. I'm 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 they 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 we can just hide these elements from the DOM using n g f and our auth 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 with 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, they, 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 RX operators here. First one is take 1 to prevent a running subscription. They. Then inside of 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 the 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 Ral on a certain ability. Because if you remember, multiple roles can be assigned bind to a specific ability. All of the code's 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 auth service, and that will authorize any roles that can read a document. They to put these guards to use, we'll go into the router, and then we'll import the guards and add them to the can activate array in each of the route objects. They 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 Firestore 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 the admin page is still locked down. They so on a final note, just to sum up the bottom line, you should be using back end Firestore 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 Firestore. If If this video helped you, please like and subscribe.

And if you wanna learn more, consider becoming a pro member at angularfirebase.com. You'll get access to exclusive content, a free copy of my book, they and 1 on 1 project support. Thanks for watching, and I'll see you soon. They
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 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's saved in Firestore.
customer support
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 add angularfirebase.com. I'm starting from a brand new Angular app with AngularFire 2 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 is called the auth service. Now before we get into the actual auth code, I wanna warn you that I'm going to go through it very quickly because I covered it in episode 55, they have, which was Firestore OAuth. If you get lost, refer back to that, but let's quickly review the sign in sign up process. First, I'm going to import our AngularFire libraries, and then I'm going to define a custom user interface. But before we define the user, let's actually define our roles. They as I mentioned before, the 3 roles that we have are subscriber, editor, and admin. The roles will be saved as an object in Firestore 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. They. 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 typed to this user interface. To authenticate a user and use that Firestore document, we'll first listen to the AngularFire auth state.
And once we get that, we can then retrieve the actual user document from Firestore. So if the user is defined, then we'll pull that document from Firestore and call value changes to get it as an observable. Otherwise, we'll just return an observable of 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 Fire they store.
google
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 typed to our user with a default role of subscriber. Then we can update this data without actually overriding it by calling the 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 will set up rules that prevent the user from updating or deleting this post. The test post just has some content and a title they'll 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 role based authorization is to create a set of abilities and then assign those abilities various roles that are authorized to perform them.
cryptocurrency
They 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.
They. 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.
reading
Now we can repeat this pattern for whatever abilities that we want to provide to these roles. So I'll go ahead and create another one here for Can Edit, they. 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. They 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. They what we wanna see is this Firestore error whenever we try to read or write any unauthorized data. Currently, I don't have any security on the test post in my Firestore rules in the Firebase console, But I'm going to use the git method to retrieve a document from the Firestore database based on this request auth user ID.
So that authenticated user's document is going to have the roles object. So we can read the data, then look at the roles. And for the subscriber, we wanna 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.
customer support
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, Firestore 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 getRole function that takes they 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, we'll call the argument from this function. Now Now we can just delete the previous rule and we can rewrite it using this function. So we'll say if get role with the subscriber role and that should equal true. They 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 git 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 Firestore on the user document and update their editor role to true. Then I'm going to log in to 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 Firestore and doesn't trigger any kind of error in the console. That's how you keep your data 100% secure in Firestore, but it's better if we never have to trigger these rules in the first place. Now I'm jumping over to a component called subscribe page, and this is where we're pulling the actual test post from Firestore. They so I have a reference and a post observable, and then I have a variable for the user itself. If you wanna lock down a specific method in the component, you can subscribe to the user during NG on a net and then use it to lock down certain code based on whatever users have that ability. I'm 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 they 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 we can just hide these elements from the DOM using n g f and our auth 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 with 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, they, 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 RX operators here.
operations
security
programming
military
law enforcement
systems
First one is take 1 to prevent a running subscription. They. Then inside of 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 the 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 Ral on a certain ability. Because if you remember, multiple roles can be assigned bind to a specific ability. All of the code's 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 auth service, and that will authorize any roles that can read a document.
They to put these guards to use, we'll go into the router, and then we'll import the guards and add them to the can activate array in each of the route objects. They 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 Firestore to see if a rule evaluates to true or false.
security
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 the admin page is still locked down. They so on a final note, just to sum up the bottom line, you should be using back end Firestore 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 Firestore. If If this video helped you, please like and subscribe. And if you wanna learn more, consider becoming a pro member at angularfirebase.com. You'll get access to exclusive content, a free copy of my book, they and 1 on 1 project support. Thanks for watching, and I'll see you soon. They
youtube
security
seasons
customer support
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 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's saved in Firestore. 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 add angularfirebase.com. I'm starting from a brand new Angular app with AngularFire 2 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 is called the auth service. Now before we get into the actual auth code, I wanna warn you that I'm going to go through it very quickly because I covered it in episode 55, they have, which was Firestore OAuth. If you get lost, refer back to that, but let's quickly review the sign in sign up process. First, I'm going to import our AngularFire libraries, and then I'm going to define a custom user interface. But before we define the user, let's actually define our roles. They as I mentioned before, the 3 roles that we have are subscriber, editor, and admin. The roles will be saved as an object in Firestore 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. They. 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 typed to this user interface. To authenticate a user and use that Firestore document, we'll first listen to the AngularFire auth state. And once we get that, we can then retrieve the actual user document from Firestore. So if the user is defined, then we'll pull that document from Firestore and call value changes to get it as an observable. Otherwise, we'll just return an observable of 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 Fire they store. 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 typed to our user with a default role of subscriber. Then we can update this data without actually overriding it by calling the 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 will set up rules that prevent the user from updating or deleting this post. The test post just has some content and a title they'll 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 role based authorization is to create a set of abilities and then assign those abilities various roles that are authorized to perform them. They 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. They. 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 abilities that we want to provide to these roles. So I'll go ahead and create another one here for Can Edit, they. 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. They 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. They what we wanna see is this Firestore error whenever we try to read or write any unauthorized data. Currently, I don't have any security on the test post in my Firestore rules in the Firebase console, But I'm going to use the git method to retrieve a document from the Firestore database based on this request auth user ID. So that authenticated user's document is going to have the roles object. So we can read the data, then look at the roles. And for the subscriber, we wanna 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, Firestore 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 getRole function that takes they 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, we'll call the argument from this function. Now Now we can just delete the previous rule and we can rewrite it using this function. So we'll say if get role with the subscriber role and that should equal true. They 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 git 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 Firestore on the user document and update their editor role to true. Then I'm going to log in to 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 Firestore and doesn't trigger any kind of error in the console. That's how you keep your data 100% secure in Firestore, but it's better if we never have to trigger these rules in the first place. Now I'm jumping over to a component called subscribe page, and this is where we're pulling the actual test post from Firestore. They so I have a reference and a post observable, and then I have a variable for the user itself. If you wanna lock down a specific method in the component, you can subscribe to the user during NG on a net and then use it to lock down certain code based on whatever users have that ability. I'm 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 they 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 we can just hide these elements from the DOM using n g f and our auth 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 with 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, they, 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 RX operators here. First one is take 1 to prevent a running subscription. They. Then inside of 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 the 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 Ral on a certain ability. Because if you remember, multiple roles can be assigned bind to a specific ability. All of the code's 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 auth service, and that will authorize any roles that can read a document. They to put these guards to use, we'll go into the router, and then we'll import the guards and add them to the can activate array in each of the route objects. They 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 Firestore 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 the admin page is still locked down. They so on a final note, just to sum up the bottom line, you should be using back end Firestore 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 Firestore. If If this video helped you, please like and subscribe. And if you wanna learn more, consider becoming a pro member at angularfirebase.com. You'll get access to exclusive content, a free copy of my book, they and 1 on 1 project support. Thanks for watching, and I'll see you soon. They
Rule
rule
based
based
authorization
authorization
is
is
the
the
cornerstone
cornerstone
of
of
any
any
user
user
moderated
moderated
site.
site
A
a
good
good
example
example
is
is
Stack
stack
Overflow,
overflow
which
which
provides
provides
users
users
various
various
privileges
privileges
based
based
on
on
the
the
amount
amount
of
of
reputation
reputation
that
that
they
they
gained
gained
on
on
the
the
site.
site
In
in
this
this
episode,
episode
I'm
i'm
going
going
to
to
show
show
you
you
how
how
to
to
use
use
Angular
angular
5
5
and
and
Firestore
firestore
to
to
build
build
your
your
own
own
role
role
based
based
authorization
authorization
feature.
feature
My
my
goal
goal
is
is
to
to
give
give
you
you
a
a
solution
solution
that
that
is
is
both
both
flexible
flexible
and
and
secure
secure
on
on
the
the
front
front
end
end
and
and
the
the
back
back
end.
end
It
it
works
works
by
by
assigning
assigning
a
a
role
role
to
to
a
a
user
user
that's
that's
saved
saved
in
in
Firestore.
firestore
Then
then
that
that
role
role
is
is
used
used
to
to
walk
walk
down
down
certain
certain
actions
actions
in
in
Angular.
angular
The
the
default
default
role
role
is
is
a
a
subscriber,
subscriber
which
which
only
only
has
has
the
the
ability
ability
to
to
read
read
data.
data
Then
then
we
we
have
have
an
an
editor
editor
that
that
can
can
read
read
and
and
update
update
data.
data
And
and
of
of
course
course
an
an
admin
admin
user
user
that
that
can
can
read,
read
update,
update
create,
create
and
and
delete.
delete
If
if
you're
you're
brand
brand
new
new
to
to
the
the
channel
channel
make
make
sure
sure
to
to
subscribe
subscribe
and
and
you
you
can
can
follow
follow
along
along
with
with
the
the
source
source
code
code
add
add
angularfirebase.com.
angularfirebase.com
I'm
i'm
starting
starting
from
from
a
a
brand
brand
new
new
Angular
angular
app
app
with
with
AngularFire
angularfire
2
2
installed.
installed
You
you
can
can
find
find
the
the
install
install
instructions
instructions
on
on
the
the
official
official
repo.
repo
The
the
first
first
thing
thing
I'm
i'm
going
going
to
to
do
do
is
is
generate
generate
a
a
core
core
module
module
to
to
isolate
isolate
all
all
the
the
logic
logic
for
for
authentication
authentication
and
and
authorization.
authorization
Then
then
inside
inside
that
that
module,
module
I'm
i'm
going
going
to
to
generate
generate
a
a
service
service
is
is
called
called
the
the
auth
auth
service.
service
Now
now
before
before
we
we
get
get
into
into
the
the
actual
actual
auth
auth
code,
code
I
i
wanna
wanna
warn
warn
you
you
that
that
I'm
i'm
going
going
to
to
go
go
through
through
it
it
very
very
quickly
quickly
because
because
I
i
covered
covered
it
it
in
in
episode
episode
55,
55
they
they
have,
have
which
which
was
was
Firestore
firestore
OAuth.
oauth
If
if
you
you
get
get
lost,
lost
refer
refer
back
back
to
to
that,
that
but
but
let's
let's
quickly
quickly
review
review
the
the
sign
sign
in
in
sign
sign
up
up
process.
process
First,
first
I'm
i'm
going
going
to
to
import
import
our
our
AngularFire
angularfire
libraries,
libraries
and
and
then
then
I'm
i'm
going
going
to
to
define
define
a
a
custom
custom
user
user
interface.
interface
But
but
before
before
we
we
define
define
the
the
user,
user
let's
let's
actually
actually
define
define
our
our
roles.
roles
They
they
as
as
I
i
mentioned
mentioned
before,
before
the
the
3
3
roles
roles
that
that
we
we
have
have
are
are
subscriber,
subscriber
editor,
editor
and
and
admin.
admin
The
the
roles
roles
will
will
be
be
saved
saved
as
as
an
an
object
object
in
in
Firestore
firestore
with
with
each
each
possible
possible
role
role
being
being
set
set
to
to
a
a
boolean
boolean
value.
value
This
this
allows
allows
you
you
to
to
assign
assign
multiple
multiple
roles
roles
to
to
a
a
single
single
user
user
if
if
that's
that's
what
what
you
you
desire.
desire
They.
they
Then
then
on
on
the
the
user
user
itself,
itself
we'll
we'll
just
just
have
have
a
a
user
user
ID
id
and
and
email,
email
and
and
then
then
the
the
roles
roles
property
property
will
will
be
be
set
set
to
to
this
this
roles
roles
object
object
interface.
interface
Let's
let's
go
go
ahead
ahead
and
and
switch
switch
back
back
to
to
the
the
service,
service
and
and
then
then
we
we
can
can
set
set
up
up
an
an
observable
observable
that
that
is
is
typed
typed
to
to
this
this
user
user
interface.
interface
To
to
authenticate
authenticate
a
a
user
user
and
and
use
use
that
that
Firestore
firestore
document,
document
we'll
we'll
first
first
listen
listen
to
to
the
the
AngularFire
angularfire
auth
auth
state.
state
And
and
once
once
we
we
get
get
that,
that
we
we
can
can
then
then
retrieve
retrieve
the
the
actual
actual
user
user
document
document
from
from
Firestore.
firestore
So
so
if
if
the
the
user
user
is
is
defined,
defined
then
then
we'll
we'll
pull
pull
that
that
document
document
from
from
Firestore
firestore
and
and
call
call
value
value
changes
changes
to
to
get
get
it
it
as
as
an
an
observable.
observable
Otherwise,
otherwise
we'll
we'll
just
just
return
return
an
an
observable
observable
of
of
null.
null
From
from
there,
there
we
we
need
need
a
a
way
way
for
for
the
the
user
user
to
to
authenticate,
authenticate
so
so
we're
we're
just
just
going
going
to
to
use
use
Google
google
OAuth
oauth
for
for
this
this
example.
example
And
and
when
when
the
the
user
user
successfully
successfully
authenticates,
authenticates
we're
we're
going
going
to
to
run
run
this
this
update
update
user
user
data
data
method.
method
The
the
purpose
purpose
of
of
that
that
method
method
is
is
to
to
create
create
or
or
update
update
the
the
user
user
document
document
in
in
Fire
fire
they
they
store.
store
The
the
way
way
we
we
do
do
that
that
is
is
we
we
create
create
a
a
reference
reference
to
to
the
the
actual
actual
user
user
document
document
with
with
the
the
authentication
authentication
user
user
ID,
id
and
and
then
then
we
we
can
can
set
set
up
up
an
an
object
object
here
here
typed
typed
to
to
our
our
user
user
with
with
a
a
default
default
role
role
of
of
subscriber.
subscriber
Then
then
we
we
can
can
update
update
this
this
data
data
without
without
actually
actually
overriding
overriding
it
it
by
by
calling
calling
the
the
reference
reference
set
set
and
and
then
then
pass
pass
it
it
the
the
merge
merge
true
true
option.
option
This
this
will
will
create
create
or
or
update
update
the
the
existing
existing
document
document
in
in
a
a
non
non
destructive
destructive
way.
way
So
so
at
at
this
this
point,
point
we
we
have
have
a
a
basic
basic
authentication
authentication
system,
system
but
but
we
we
still
still
don't
don't
have
have
any
any
role
role
based
based
rules
rules
in
in
our
our
app.
app
What
what
I've
i've
done
done
is
is
created
created
a
a
couple
couple
extra
extra
components
components
here.
here
This
this
one
one
happens
happens
to
to
pull
pull
some
some
data
data
from
from
Firestore
firestore
and
and
will
will
set
set
up
up
rules
rules
that
that
prevent
prevent
the
the
user
user
from
from
updating
updating
or
or
deleting
deleting
this
this
post.
post
The
the
test
test
post
post
just
just
has
has
some
some
content
content
and
and
a
a
title
title
they'll
they'll
property.
property
Then
then
I
i
set
set
up
up
another
another
component
component
called
called
the
the
super
super
secret
secret
component
component
that's
that's
only
only
for
for
admin
admin
users,
users
and
and
that's
that's
where
where
we
we
keep
keep
all
all
of
of
our
our
Bitcoins.
bitcoins
The
the
way
way
I
i
like
like
to
to
implement
implement
role
role
based
based
authorization
authorization
is
is
to
to
create
create
a
a
set
set
of
of
abilities
abilities
and
and
then
then
assign
assign
those
those
abilities
abilities
various
various
roles
roles
that
that
are
are
authorized
authorized
to
to
perform
perform
them.
them
They
they
what
what
I'm
i'm
doing
doing
first
first
is
is
creating
creating
a
a
helper
helper
method
method
that
that
takes
takes
the
the
user
user
and
and
the
the
allowed
allowed
roles
roles
from
from
one
one
of
of
these
these
abilities,
abilities
and
and
we'll
we'll
check
check
and
and
see
see
if
if
the
the
user
user
has
has
the
the
appropriate
appropriate
role
role
to
to
perform
perform
it.
it
So
so
if
if
the
the
user
user
is
is
not
not
defined,
defined
then
then
we'll
we'll
just
just
return
return
false.
false
Otherwise,
otherwise
we're
we're
going
going
to
to
loop
loop
over
over
the
the
allowed
allowed
roles,
roles
and
and
if
if
the
the
user
user
has
has
that
that
role,
role
then
then
we
we
can
can
return
return
true.
true
Otherwise,
otherwise
we'll
we'll
just
just
return
return
false.
false
Now
now
I'm
i'm
going
going
to
to
define
define
the
the
abilities
abilities
right
right
here
here
in
in
the
the
service,
service
but
but
you
you
could
could
extract
extract
this
this
out
out
into
into
its
its
own
own
class
class
if
if
you
you
have
have
a
a
really
really
complex
complex
set
set
of
of
roles
roles
and
and
abilities.
abilities
They.
they
So
so
the
the
first
first
one
one
is
is
can
can
read,
read
and
and
this
this
will
will
determine
determine
if
if
a
a
user
user
can
can
read
read
a
a
document
document
or
or
not.
not
So
so
right
right
now,
now
the
the
admin
admin
editor
editor
and
and
subscriber
subscriber
all
all
have
have
access
access
to
to
read.
read
As
as
you'll
you'll
see
see
later,
later
we
we
can
can
use
use
this
this
method
method
to
to
easily
easily
hide
hide
or
or
block
block
content
content
in
in
our
our
components
components
or
or
in
in
a
a
router
router
guard
guard
to
to
easily
easily
control
control
access
access
throughout
throughout
the
the
application.
application
Now
now
we
we
can
can
repeat
repeat
this
this
pattern
pattern
for
for
whatever
whatever
abilities
abilities
that
that
we
we
want
want
to
to
provide
provide
to
to
these
these
roles.
roles
So
so
I'll
i'll
go
go
ahead
ahead
and
and
create
create
another
another
one
one
here
here
for
for
Can
can
Edit,
edit
they.
they
And
and
then
then
I'll
i'll
create
create
one
one
more
more
for
for
can
can
delete,
delete
which
which
is
is
only
only
allowed
allowed
to
to
be
be
performed
performed
by
by
the
the
admin
admin
user.
user
Keep
keep
in
in
mind
mind
that
that
anything
anything
you
you
do
do
in
in
Angular
angular
only
only
provides
provides
front
front
end
end
security.
security
They
they
it
it
would
would
be
be
trivial
trivial
to
to
reverse
reverse
engineer
engineer
and
and
circumvent
circumvent
these
these
rules.
rules
So
so
before
before
we
we
do
do
anything
anything
on
on
the
the
front
front
end,
end
we're
we're
going
going
to
to
write
write
some
some
back
back
end
end
rules
rules
in
in
Firestore
firestore
that
that
will
will
guarantee
guarantee
data
data
security.
security
They
they
what
what
we
we
wanna
wanna
see
see
is
is
this
this
Firestore
firestore
error
error
whenever
whenever
we
we
try
try
to
to
read
read
or
or
write
write
any
any
unauthorized
unauthorized
data.
data
Currently,
currently
I
i
don't
don't
have
have
any
any
security
security
on
on
the
the
test
test
post
post
in
in
my
my
Firestore
firestore
rules
rules
in
in
the
the
Firebase
firebase
console,
console
But
but
I'm
i'm
going
going
to
to
use
use
the
the
git
git
method
method
to
to
retrieve
retrieve
a
a
document
document
from
from
the
the
Firestore
firestore
database
database
based
based
on
on
this
this
request
request
auth
auth
user
user
ID.
id
So
so
that
that
authenticated
authenticated
user's
user's
document
document
is
is
going
going
to
to
have
have
the
the
roles
roles
object.
object
So
so
we
we
can
can
read
read
the
the
data,
data
then
then
look
look
at
at
the
the
roles.
roles
And
and
for
for
the
the
subscriber,
subscriber
we
we
wanna
wanna
make
make
sure
sure
that
that
that
that
value
value
is
is
true.
true
In
in
other
other
words,
words
if
if
the
the
current
current
user
user
tries
tries
to
to
read
read
this
this
document
document
but
but
they
they
don't
don't
have
have
the
the
subscriber
subscriber
role,
role
then
then
it's
it's
going
going
to
to
block
block
access
access
to
to
that
that
document.
document
The
the
one
one
glaring
glaring
problem
problem
here
here
is
is
that
that
this
this
line
line
is
is
super
super
long
long
and
and
it's
it's
going
going
to
to
get
get
completely
completely
out
out
of
of
control
control
if
if
we
we
try
try
to
to
repeat
repeat
it
it
for
for
multiple
multiple
roles.
roles
Luckily,
luckily
Firestore
firestore
rules
rules
allow
allow
you
you
to
to
write
write
functions,
functions
so
so
we
we
can
can
write
write
this
this
in
in
a
a
much
much
more
more
readable
readable
and
and
expressive
expressive
way.
way
I'm
i'm
going
going
to
to
create
create
this
this
getRole
getrole
function
function
that
that
takes
takes
they
they
the
the
role
role
as
as
a
a
string
string
as
as
an
an
argument.
argument
Then
then
I'm
i'm
going
going
to
to
repeat
repeat
that
that
whole
whole
get
get
line
line
that
that
I
i
wrote
wrote
just
just
a
a
second
second
ago,
ago
but
but
instead
instead
of
of
calling
calling
subscriber,
subscriber
we'll
we'll
call
call
the
the
argument
argument
from
from
this
this
function.
function
Now
now
Now
now
we
we
can
can
just
just
delete
delete
the
the
previous
previous
rule
rule
and
and
we
we
can
can
rewrite
rewrite
it
it
using
using
this
this
function.
function
So
so
we'll
we'll
say
say
if
if
get
get
role
role
with
with
the
the
subscriber
subscriber
role
role
and
and
that
that
should
should
equal
equal
true.
true
They
they
then
then
we
we
can
can
do
do
the
the
same
same
thing
thing
for
for
the
the
editor
editor
and
and
admin
admin
roles
roles
as
as
well.
well
And
and
we
we
can
can
lock
lock
these
these
down
down
based
based
on
on
the
the
type
type
of
of
operation.
operation
So
so
we
we
can
can
say
say
allow
allow
update
update
if
if
git
git
role
role
editor
editor
equals
equals
true.
true
And
and
then
then
for
for
admin,
admin
we'll
we'll
say
say
allow
allow
create
create
or
or
delete
delete
if
if
the
the
admin
admin
role
role
equals
equals
true.
true
Now
now
I'm
i'm
going
going
to
to
go
go
into
into
Firestore
firestore
on
on
the
the
user
user
document
document
and
and
update
update
their
their
editor
editor
role
role
to
to
true.
true
Then
then
I'm
i'm
going
going
to
to
log
log
in
in
to
to
the
the
app,
app
and
and
if
if
I
i
try
try
to
to
delete
delete
a
a
page,
page
I
i
should
should
still
still
get
get
an
an
error
error
because
because
they
they
don't
don't
have
have
the
the
admin
admin
privilege.
privilege
However,
however
if
if
this
this
user
user
performs
performs
an
an
update,
update
then
then
it
it
goes
goes
through
through
in
in
Firestore
firestore
and
and
doesn't
doesn't
trigger
trigger
any
any
kind
kind
of
of
error
error
in
in
the
the
console.
console
That's
that's
how
how
you
you
keep
keep
your
your
data
data
100%
100%
secure
secure
in
in
Firestore,
firestore
but
but
it's
it's
better
better
if
if
we
we
never
never
have
have
to
to
trigger
trigger
these
these
rules
rules
in
in
the
the
first
first
place.
place
Now
now
I'm
i'm
jumping
jumping
over
over
to
to
a
a
component
component
called
called
subscribe
subscribe
page,
page
and
and
this
this
is
is
where
where
we're
we're
pulling
pulling
the
the
actual
actual
test
test
post
post
from
from
Firestore.
firestore
They
they
so
so
I
i
have
have
a
a
reference
reference
and
and
a
a
post
post
observable,
observable
and
and
then
then
I
i
have
have
a
a
variable
variable
for
for
the
the
user
user
itself.
itself
If
if
you
you
wanna
wanna
lock
lock
down
down
a
a
specific
specific
method
method
in
in
the
the
component,
component
you
you
can
can
subscribe
subscribe
to
to
the
the
user
user
during
during
NG
ng
on
on
a
a
net
net
and
and
then
then
use
use
it
it
to
to
lock
lock
down
down
certain
certain
code
code
based
based
on
on
whatever
whatever
users
users
have
have
that
that
ability.
ability
I'm
i'm
I'm
i'm
going
going
to
to
create
create
a
a
couple
couple
methods
methods
here,
here
one
one
to
to
edit
edit
a
a
post
post
and
and
another
another
to
to
delete
delete
a
a
post.
post
Then
then
if
if
you
you
want
want
to
to
lock
lock
down
down
a
a
method
method
based
based
on
on
a
a
certain
certain
ability,
ability
you
you
can
can
just
just
do
do
if
if
they
they
can
can
edit
edit
and
and
pass
pass
it
it
the
the
user
user
object.
object
Then
then
just
just
wrap
wrap
the
the
important
important
part
part
inside
inside
of
of
this
this
if
if
statement.
statement
That's
that's
one
one
way
way
to
to
use
use
the
the
auth
auth
service,
service
but
but
it's
it's
actually
actually
a
a
lot
lot
Easier
easier
if
if
you
you
perform
perform
this
this
logic
logic
inside
inside
of
of
the
the
template
template
itself.
itself
In
in
most
most
cases,
cases
you
you
have
have
buttons
buttons
or
or
other
other
elements
elements
that
that
the
the
user
user
will
will
interact
interact
with
with
to
to
trigger
trigger
some
some
kind
kind
of
of
method.
method
We
we
we
we
can
can
just
just
hide
hide
these
these
elements
elements
from
from
the
the
DOM
dom
using
using
n
n
g
g
f
f
and
and
our
our
auth
auth
service,
service
and
and
that's
that's
usually
usually
a
a
much
much
more
more
maintainable
maintainable
way
way
to
to
go.
go
But
but
there's
there's
actually
actually
still
still
an
an
even
even
better
better
mechanism
mechanism
that
that
we
we
can
can
use
use
in
in
Angular
angular
to
to
prevent
prevent
unauthorized
unauthorized
access
access
to
to
content.
content
And
and
that's
that's
with
with
the
the
router
router
guard
guard
to
to
lock
lock
down
down
certain
certain
routes
routes
based
based
on
on
the
the
user's
user's
role.
role
So
so
first,
first
I'm
i'm
going
going
to
to
create
create
a
a
guard
guard
called
called
the
the
admin
admin
guard
guard
and
and
add
add
that
that
to
to
the
the
core
core
module.
module
Inside
inside
the
the
guard,
guard
I'm
i'm
going
going
to
to
import
import
our
our
auth
auth
service
service
as
as
well
well
as
as
tap,
tap
map,
map
and
and
take
take
from
from
RxJS.
rxjs
Then
then
the
the
auth
auth
service
service
is
is
injected
injected
in
in
the
the
constructor,
constructor
they,
they
and
and
we're
we're
going
going
to
to
return
return
an
an
observable
observable
from
from
this
this
guard
guard
inside
inside
of
of
can
can
activate.
activate
And
and
it's
it's
important
important
that
that
you
you
map
map
this
this
observable
observable
to
to
a
a
Boolean.
boolean
So
so
first,
first
we'll
we'll
call
call
pipe,
pipe
and
and
then
then
we'll
we'll
pipe
pipe
in
in
a
a
few
few
RX
rx
operators
operators
here.
here
First
first
one
one
is
is
take
take
1
1
to
to
prevent
prevent
a
a
running
running
subscription.
subscription
They.
they
Then
then
inside
inside
of
of
map,
map
we'll
we'll
see
see
if
if
the
the
user
user
object
object
exists.
exists
And
and
if
if
so,
so
we'll
we'll
also
also
check
check
for
for
the
the
user
user
roles
roles
object
object
for
for
the
the
admin
admin
property.
property
Then
then
we'll
we'll
do
do
a
a
ternary
ternary
if
if
statement
statement
here
here
and
and
return
return
true
true
if
if
true,
true
otherwise
otherwise
false.
false
Then
then
we
we
can
can
use
use
the
the
tap
tap
operator
operator
which
which
recently
recently
replaced
replaced
the
the
do
do
operator
operator
to
to
console
console
log
log
an
an
error
error
if
if
the
the
user
user
is
is
trying
trying
to
to
access
access
an
an
unauthorized
unauthorized
area,
area
but
but
you
you
could
could
also
also
use
use
the
the
router
router
here
here
to
to
redirect
redirect
the
the
user
user
to
to
a
a
different
different
route.
route
So
so
this
this
is
is
going
going
to
to
lock
lock
down
down
a
a
route
route
based
based
on
on
a
a
certain
certain
role,
role
but
but
we
we
can
can
also
also
lock
lock
down
down
Ral
ral
on
on
a
a
certain
certain
ability.
ability
Because
because
if
if
you
you
remember,
remember
multiple
multiple
roles
roles
can
can
be
be
assigned
assigned
bind
bind
to
to
a
a
specific
specific
ability.
ability
All
all
of
of
the
the
code's
code's
exactly
exactly
the
the
same.
same
We
we
just
just
have
have
to
to
change
change
one
one
thing
thing
down
down
here
here
in
in
the
the
map
map
operator.
operator
So
so
instead
instead
of
of
looking
looking
at
at
the
the
actual
actual
role
role
itself,
itself
will
will
pass
pass
the
the
user
user
to
to
the
the
can
can
read
read
method
method
from
from
our
our
auth
auth
service,
service
and
and
that
that
will
will
authorize
authorize
any
any
roles
roles
that
that
can
can
read
read
a
a
document.
document
They
they
to
to
put
put
these
these
guards
guards
to
to
use,
use
we'll
we'll
go
go
into
into
the
the
router,
router
and
and
then
then
we'll
we'll
import
import
the
the
guards
guards
and
and
add
add
them
them
to
to
the
the
can
can
activate
activate
array
array
in
in
each
each
of
of
the
the
route
route
objects.
objects
They
they
if
if
you
you
can
can
lock
lock
things
things
down
down
at
at
the
the
route
route
level,
level
it's
it's
going
going
to
to
simplify
simplify
your
your
code
code
base
base
overall,
overall
and
and
it
it
also
also
has
has
the
the
added
added
benefit
benefit
of
of
not
not
requiring
requiring
a
a
read
read
in
in
Firestore
firestore
to
to
see
see
if
if
a
a
rule
rule
evaluates
evaluates
to
to
true
true
or
or
false.
false
As
as
you
you
can
can
see
see
here,
here
we
we
have
have
an
an
unauthenticated
unauthenticated
user,
user
and
and
they
they
are
are
denied
denied
access
access
to
to
any
any
of
of
these
these
pages
pages
just
just
based
based
on
on
the
the
router
router
guard.
guard
Then
then
if
if
we
we
log
log
in
in
with
with
the
the
editor
editor
role,
role
we
we
can
can
go
go
to
to
the
the
main
main
content
content
page,
page
but
but
the
the
admin
admin
page
page
is
is
still
still
locked
locked
down.
down
They
they
so
so
on
on
a
a
final
final
note,
note
just
just
to
to
sum
sum
up
up
the
the
bottom
bottom
line,
line
you
you
should
should
be
be
using
using
back
back
end
end
Firestore
firestore
rules
rules
for
for
your
your
actual
actual
data
data
security.
security
And
and
then
then
on
on
the
the
front
front
end,
end
use
use
router
router
guards
guards
and
and
additional
additional
logic
logic
to
to
enhance
enhance
the
the
user
user
experience.
experience
That's
that's
it
it
for
for
role
role
based
based
access
access
control
control
with
with
Firestore.
firestore
If
if
If
if
this
this
video
video
helped
helped
you,
you
please
please
like
like
and
and
subscribe.
subscribe
And
and
if
if
you
you
wanna
wanna
learn
learn
more,
more
consider
consider
becoming
becoming
a
a
pro
pro
member
member
at
at
angularfirebase.com.
angularfirebase.com
You'll
you'll
get
get
access
access
to
to
exclusive
exclusive
content,
content
a
a
free
free
copy
copy
of
of
my
my
book,
book
they
they
and
and
1
1
on
on
1
1
project
project
support.
support
Thanks
thanks
for
for
watching,
watching
and
and
I'll
i'll
see
see
you
you
soon.
soon
They
they
success
The concept of rule-based authorization in Angular 5 and Fir merchant allow users to assign multiple roles to a single user. A helper method for updating a user is discussed, along with the use of Fir permission rules for data security. The speakers emphasize the importance of using Firiber rules for data security and offer advice on becoming a pro member at angular t e s.
Role-Based Authorization with Firestore
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 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's saved in Firestore. 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 add angularfirebase.com. I'm starting from a brand new Angular app with AngularFire 2 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 is called the auth service. Now before we get into the actual auth code, I wanna warn you that I'm going to go through it very quickly because I covered it in episode 55, they have, which was Firestore OAuth. If you get lost, refer back to that, but let's quickly review the sign in sign up process. First, I'm going to import our AngularFire libraries, and then I'm going to define a custom user interface. But before we define the user, let's actually define our roles. They as I mentioned before, the 3 roles that we have are subscriber, editor, and admin. The roles will be saved as an object in Firestore 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. They. 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 typed to this user interface. To authenticate a user and use that Firestore document, we'll first listen to the AngularFire auth state. And once we get that, we can then retrieve the actual user document from Firestore. So if the user is defined, then we'll pull that document from Firestore and call value changes to get it as an observable. Otherwise, we'll just return an observable of 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 Fire they store. 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 typed to our user with a default role of subscriber. Then we can update this data without actually overriding it by calling the 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 will set up rules that prevent the user from updating or deleting this post. The test post just has some content and a title they'll 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 role based authorization is to create a set of abilities and then assign those abilities various roles that are authorized to perform them. They 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. They. 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 abilities that we want to provide to these roles. So I'll go ahead and create another one here for Can Edit, they. 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. They 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. They what we wanna see is this Firestore error whenever we try to read or write any unauthorized data. Currently, I don't have any security on the test post in my Firestore rules in the Firebase console, But I'm going to use the git method to retrieve a document from the Firestore database based on this request auth user ID. So that authenticated user's document is going to have the roles object. So we can read the data, then look at the roles. And for the subscriber, we wanna 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, Firestore 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 getRole function that takes they 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, we'll call the argument from this function. Now Now we can just delete the previous rule and we can rewrite it using this function. So we'll say if get role with the subscriber role and that should equal true. They 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 git 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 Firestore on the user document and update their editor role to true. Then I'm going to log in to 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 Firestore and doesn't trigger any kind of error in the console. That's how you keep your data 100% secure in Firestore, but it's better if we never have to trigger these rules in the first place. Now I'm jumping over to a component called subscribe page, and this is where we're pulling the actual test post from Firestore. They so I have a reference and a post observable, and then I have a variable for the user itself. If you wanna lock down a specific method in the component, you can subscribe to the user during NG on a net and then use it to lock down certain code based on whatever users have that ability. I'm 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 they 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 we can just hide these elements from the DOM using n g f and our auth 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 with 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, they, 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 RX operators here. First one is take 1 to prevent a running subscription. They. Then inside of 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 the 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 Ral on a certain ability. Because if you remember, multiple roles can be assigned bind to a specific ability. All of the code's 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 auth service, and that will authorize any roles that can read a document. They to put these guards to use, we'll go into the router, and then we'll import the guards and add them to the can activate array in each of the route objects. They 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 Firestore 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 the admin page is still locked down. They so on a final note, just to sum up the bottom line, you should be using back end Firestore 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 Firestore. If If this video helped you, please like and subscribe. And if you wanna learn more, consider becoming a pro member at angularfirebase.com. You'll get access to exclusive content, a free copy of my book, they and 1 on 1 project support. Thanks for watching, and I'll see you soon. They
The concept of rule-based authorization in Angular 5 and Fir merchant allow users to assign multiple roles to a single user. A helper method for updating a user is discussed, along with the use of Fir permission rules for data security. The speakers emphasize the importance of using Firiber rules for data security and offer advice on becoming a pro member at angular t e s.