19 August 2016

Today, I have a look at Firebase security rules for the purpose of implementing user roles. I share a quick example that includes both a database and corresponding rules to help you get up to speed with the format.

There’s no convenient interface in Firebase where roles can be readily added. The semantics are instead defined both through the JSON database along with the logic in the security rules.

Let’s say I have a secret_data path that I want to only be accessed only if a user is a paid_user.

My database looks like this.

 1 {
 2   "roles" : {
 3     "admin_user" : true,
 4     "free_user" : true,
 5     "paid_user" : {
 6       "335Fq6U6gZPWNEyDJRZd6ZYGkvn1" : true
 7     }
 8   },
 9   "secret_data" : {
10     "access_code" : 390399333,
11     "users" : {
12       "335Fq6U6gZPWNEyDJRZd6ZYGkvn1" : true
13     }
14   },
15   "users" : {
16     "335Fq6U6gZPWNEyDJRZd6ZYGkvn1" : {
17       "username" : "daniel"
18     }
19   }
20 }

The choice of where I place the roles is entirely up to me and how I want to work with my data. The only structure that is imposed on me is the JSON hierarchy.

It helps to restrict all access by default. That is easily accomplished by making the top level read and write in the rules be both false (lines 2–4 below).

Getting access to the secret_data, a path in my JSON database, involves setting up a boolean evaluation of the existence of my uid within the paid_user path.

I look at the tree, starting at roles and descend into paid_user and check for the existence of my uid within that path (line 8 below).

I’ve also added an additional check to verify that my uid also appears within the users that are authorized to access the secret data (line 9 below).

In other words, the implementation of my security model can be as complex as I want it to be.

There is an additional check of matching my uid to the logged-in user’s uid to be able to access any user information there (line 12 below).

In this case, that additional check is completely redundant because the preceding logic will override anything below.

That is why the top level read, write rules must be set to false.

Here are the security rules as I’ve described them.

 1 {
 2   "rules" : {
 3     ".read"  : false,
 4     ".write" : false,
 6     // Secret data is only available to paid users.
 7     "secret_data" : {
 8       ".read": "(root.child('roles').child('paid_user').hasChild(auth.uid)) 
 9                 && (data.child('users').hasChild(auth.uid))",
10       "users" : {
11         "$uid" : {
12           ".read" : "($uid === auth.uid)"
13         }
14       }
15     }
16   }
17 }

In conclusion, Firebase allows creating just about any security model you want. The key to creating the rules is to look at your data and match the structure within your rules and then add the appropriate logic. Having a couple consoles open side-by-side facilitates this process.

That’s it for my quick and easy introduction of how to approach making user roles in Firebase.

blog comments powered by Disqus