Andrey Lechev
Index Of /

Index Of /

Firestore Rules

Some times ago I got tired of Firebase's letters about the lack of security for my databases and decided to spent some time and to build some set of rules I could use for more than one project.

In this article I'll share what I found and what rules I'm using, but first the bad things...

You cannot filter requests to the Firestore using security rules

Secretly I was hoping that setting, for example, read rules like “admins can read all articles, and everybody only published”, will let me just collection('articles').get() and the filtering will happen automatically, depending on the user auth.

// Users can see only published articles 
match /articles/{articleId} { 
  allow read: if resource.data.isPublished; 
} 

// Admins can see, create and delete everything 
match /articles/{articleId} { 
  allow read, write: if userIsAdmin(); 
}

Unfortunately, Firebase is not that good. Security rules are just for security and you have to check everything manually once again before making the request to pass the rules, otherwise, you can get an error only for the possibility to get an unallowed document.

How I'm keeping user roles in the Firestore

For every user, I'm creating a document in the users collection with the user id as the name of the document. And I think it's a popular scheme.

Roles are kept in the user document itself. Take for example boolean property isAdmin. In this case (when the roles are fixed) it's useful to create functions in the Firestore rules returning boolean values regarding every role you have.

function userIsAdmin() { 
  return exists(/databases/$(database)/documents/users/$(request.auth.uid)) && 
    get(/databases/$(database)/documents/users/$(request.auth.uid)).data.isAdmin == true; 
}

And using a function like this can be found a little higher in this article.

General rules

In my projects, I find out that I have a few general rules. In all three projects I'm dealing with these days, I was able to secure the data much better than before ;-))

function userGroup() { 
  return exists(/databases/$(database)/documents/users/$(request.auth.uid)) && 
    get(/databases/$(database)/documents/users/$(request.auth.uid)).data.group; 
} 

// 1. super admins can do everything 
match /{document=**} { 
  allow read, write: if userIsSuperAdmin(); 
} 

// 2. admins can edit anything for their group 
match /{document=**} { 
  allow write: if resource.data.group == userGroup() && userIsAdmin(); 
} 

// 3. authorized users can read read some docs belonging to their group 
match /invoices/{document=**} { 
  allow read: if resource.data.group == userGroup(); 
} 

// 4. authorized users can read everything from data support collections 
match /tags/{document=**} { 
  allow read: if request.auth.uid != null; 
} 

// 5. authorized users (and admins) can create and edit themselves 
match /users/{userDoc} { 
  allow write: if userDoc == request.auth.uid || userIsAdmin(); 
} 

// 6. authorized users can create some documents, comments for example 
match /comments/{comment=**} { 
  allow write: if request.auth.uid != null; 
} 

// 7. All users can read some open documents 
match /articles/{document=**} { 
  allow read: if resource.data.isPublished; 
}

Hope, this can save you some work next time you receive a letter with the subject “[Firebase] Your Cloud Firestore has insecure rules”.

 
Share this