In this article, I’ll help you write rules for role-based firestore access.
I’ll try to explain this with an example so that you can easily understand when to use this method of authentication while you build your application.
Role-Based User Authorization: Where users can have many roles and those rules provide different privileges that enable different operations to the firestore database.
To better understand it, start by looking at our data model.
Here in our data model, we have a user collection and the user document has a field “roles” which is an array of strings, where the string represents what role that user has and allowing them to perform actions according to the privilege.
Well, this has an issue, because we cannot let the user modify this “roles” array on their own. This would be ineffective if users can assign roles to themselves.
To get over this we create a posts collection :
Which has 4 important fields, the content (also the image), “published” which determines whether or not the post is made public, a “timestamp” to give information about when it was published, and “userId” to associate the author of the post to that document.
Let’s understand the logic on how this works with rules :
Look at the match block which references the users’ collection on line 4. It has custom functions set to it and you’ll learn to implement it later after we understand the logic.
So basically, on the user’s collection, we want to allow read if a user is logged in to our app. But, to update or delete a user, requires to be an admin user. To check if it’s a request from an admin account we use “hasAnyRole(list)” which takes in a list of strings that we’ll match to the roles of that user and if a user has ANY of those roles it will allow the operation.
Now, look at the posts collection, to read a post we check if a user is logged in and the post has been published. However, the admin can read any post, no matter it is published or not. So we added a “or” admin check, which checks if the requesting user is admin or not and allows admin to read unpublished posts.
Coming to creating a post we’ll first check if it has all the valid fields and format and the user is the author of the post. This will make sure that the user can post content only with his id as the author id.
For update, it’s almost a similar process but we only allow certain fields of a document to be updated once after it’s posted. So we’ll use another custom function to validate an update post request. And also allow additional roles to make an update to a post.
Finally, deletion of the post can only be done by admin, so we check if the requesting user is an admin or not. You could optionally allow the author of the post to be able to delete a post but that depends on your use case.
Custom Functions :
Let’s get to the unimplemented functions from the above logic :
This function is pretty straightforward, it just checks if the requesting user’s auth attribute is not null.
The next function is
This function checks if the user is logged in and then gets the roles array that we created earlier in the user’s data model and checks if it contains any of the roles.
Checking the validity of the post is a bit complex than these as we need to check multiple attributes to approve the request.
The “post” is the variable that holds the data from the request.
“isOwner” variable holds the boolean value of if a user is the owner of the post.
“isNotFromPastOrFuture” will check if the user the timestamp on the incoming data is not from the past or future. This will help you to server-side validate if the user is trying to post something back or forward in time.
“hasMandatoryFields” variable will check if all the mandatory keys exist in the incoming data. This will help you filter incomplete data sent through unauthorized clients.
And finally returns the boolean of all these variables.
Now, validation of updating post is a bit different from this :
Similar to the newPost function “post” variable holds the incoming data.
Unlike the new post, we only need to check if there are only keys that can be modified after publishing the post.
“isValid” variable checks if the content type is the string (depends on what data type you want it to be) and checks if the content length doesn’t exceed 2000 characters.
Finally, returns the values of these.
This was a basic example of role-based authorization. You can customize these rules accordingly to match your purpose.
I Hope, this article helped you understand writing firestore rules. If you find anything difficult to understand feel free to comment down.
Don’t forget to leave a clap 👏🏻. It makes me happy 🥰.