Index Of /

Firestore Rules

June 1, 2019

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();
}

Unfortunatelly, 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 error only for possibility to get 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 a name of the document. And I think it's popular scheme.

Roles are kept into 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 function like this can be found 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 subject “[Firebase] Your Cloud Firestore has insecure rules”.

Andrey “Leechy” Lechev

Some frontender thoughts
Twitter, GitHub, LinkedIn