DEV Community

Cover image for Firebase Learn Cloud Firestore Security Rules
Raja Tamil
Raja Tamil

Posted on

Firebase Learn Cloud Firestore Security Rules

I have been asked by a lot of developers this one question…

Is Firebase safe as the config code is exposed to the browser? 🔥

My answer is Yes and No.

The config code that Firebase provides is meant to be public.

But…

The real security lays in the Security Rules. 👮‍♂️

Having the right security rule in place, you can build a secure web app with the Cloud Firestore without needing your own server at all.

However, if you have your own server or are using cloud functions using Firebase Admin SDK, all security rules will be bypassed.

In that case, you will have to handle security manually in your server-side environment.

Let’s take a look at the SIX common scenarios that you may run into when writing Firestore Security Rules.

Recommended
Learn Firebase Storage Quickly - Guide

01. Read Permission For Anyone

Let’s say you have a posts collection and you want to show all documents from it to anyone who visits your site. You can do something like this:

service cloud.firestore {    
  match /databases/{database}/documents {      
    match /posts/{docId} {        
      allow read;     
    }    
  }  
} 
Enter fullscreen mode Exit fullscreen mode

Pretty straight forward!

The allow:read gives permission to read all documents in a given path, in this case, /posts/{docId}.

Other operations such as create, update and delete can be a different statement or merged with the read statement depending upon what type of rule you’re trying to write.

But, NEVER use allow:write without additional rules in place.

02. For Authenticated Users Only

Instead of showing the posts to anyone, how about showing them to only authenticated users.

service cloud.firestore {   
  match /databases/{database}/documents {     
    match /posts/{docId} {      
      allow read: if request.auth.uid != null     
    }   
  }
}
Enter fullscreen mode Exit fullscreen mode

You can easily do that by checking to see if the signed-in user’s uid exists or not. The request.auth object has information about the signed-in user. So, you can access uid using request.auth.uid property.

Nice!

The third security rule will enforce that only logged-in users can read or write their own documents, not others and vice-versa.

03. Secure Logged-In User Data

If you’re from an SQL background, this is a one-to-one relationship. One document per user in a users collection.

service cloud.firestore {    
  match /databases/{database}/documents {      
    match /users/{uid} {        
      allow read, write: if request.auth.uid == uid      
    }    
  }  
} 
Enter fullscreen mode Exit fullscreen mode

In the inner match path, you can see the users collection slash the wildcard variable {uid} that basically represents any document that’s inside that collection.

Using this security rule, users can only update their own document if their logged-in user ID is equal to the uid that already exists in the database.

On top of that, you could add an email verification check as a second condition using request.auth.token.email_verified flag.

service cloud.firestore {    
  match /databases/{database}/documents {      
    match /users/{uid}/ {        
      allow read, write: 
        if request.auth.uid == uid &&
           request.auth.token.email_verified == true     
    }    
  }  
} 
Enter fullscreen mode Exit fullscreen mode

04. Never Trust The Client And Validate Data

Let’s say you have a post collection and each document inside it has a title, content, author, author_id, etc. The author_id would be an actual user ID.

Alt text of image

I often find that it’s easier to understand when showing the data structure before showing the security rule.

Sometimes, you will want to validate the incoming request data before saving into the database using the request object.

Let’s add a security rule to validate data before allowing users to add new posts to the Cloud Firestore.

service cloud.firestore {   
  match /databases/{database}/documents {     
    match /posts/{docId} {      
      allow create: 
        if request.resource.data.title is string && 
           request.data.title.size() > 5 &&
           request.data.title.size() < 500 &&
           request.resource.data.author_id == request.auth.uid     
     }   
  } 
}
Enter fullscreen mode Exit fullscreen mode

It’s checking to see if the title is a string and also validating if the length of the title characters is between 5 and 500 long.

As you can see, you can add validation pretty much to all of the fields, in fact, it’s recommended because it gives more schema to your data model.

Finally, check to see if the request.resource.data.author_id is equal to the currently logged-in user’s uid.

This is very similar to server-side validation without managing your own server. 🙂

The Firestore query for adding a new post should be something like this:

firebase
  .firestore()     
  .collection("posts")     
  .add({         
      title: "JavaScript MVC",
      content: "In this JavaScript MVC...",     
      author: firebase.auth().currentUser.displayName, 
      author_id: firebase.auth().currentUser.uid     
   });
Enter fullscreen mode Exit fullscreen mode

Check out my other article Learn Firestore CRUD Queries Quickly if you want to know more about queries.

05. Get Multiple Documents Owned By A Single User

Getting multiple documents by a single user often referred to a one-to-many relationship.

Before creating a rule, make sure that each document has a file called author_id which is basically the userId of whoever created that post.

Continue Reading...

Discussion (0)