The purpose of this article is to explain a simple way to set the Firebase Firestore security rules to allow users to only CRUD their data and not everything else.
Let’s say you have 2 collections and one of them is “users” with user data and the other one is called “todos” for documents owned by users. The typical document looks like this:
Users
user: { name:"john doe" }
Todos
todo: { title:"hello to do", body:"we are the world , we are the children", uid:"65sd424c237a23a387b54543r79" }
You want users to be able to CRUD their own data in both of these collections. That means authorized requests can create, read, update or delete a user document only if the document is assigned to them. Similar is valid for the todos collection; authorized requests can create, read, update or delete todo documents only if the document has their uid.
Here is a sample secure rule set for these requirements;
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /users/{id}/{u=**} { allow read, write: if (isSignedIn() && isUser(id)); } match /todos/{id}/{t=**} { allow read, write: if (isSignedIn() && isUserOwner()); } match /{document=**} { allow read, write: if false; } function isSignedIn() { return request.auth != null; } function isUser(uid) { return uid == request.auth.uid; } function isUserOwner() { return getResourceData().uid == request.auth.uid; } function getResourceData() { return resource == null ? request.resource.data : resource.data } } }
In this example, normally, all documents are publicly inaccessible. In users and todos collections, we have specific rules and accessibility will be decided based on the data already saved in DB and / or the data being sent by the user.
Documents under todos
can be read and written only if they have a saved uid
which is the same as the sent request’s uid
.
Documents under users
can be read and written only if their document id is the same as the sent request’s uid
.
isSignedIn()
function checks if request is authorised.
isUser(id)
function checks if id matches the authorised request’s uid.
isUserOwner()
function checks if document’s uid matches the authorised request’s uid.
getResourceData()
function returns resource data. We need this to simplify ti if conditions. The key point is resource
only exists when reading from DB and request.resource
only exists when writing to DB (reading from the user).
I hope this can provide a good starter for the ones developing secure firebase applications. This is a simplified solution but this scenarios can get quite complicated for bigger databases with more interconnected data structure.