DEV Community

loading...
Cover image for Firebase: Firestore-Rules

Firebase: Firestore-Rules

crisarji profile image crisarji ・5 min read

What's Firebase Firestore Rules?

Hello developer pal!, glad to see you here.

Rules, rules and rules, we always hear about rules to follow for interacting with databases, endpoints, programming languages, and well, Firebase Firestore is not the exception to the...Rule(dammit once again!).

Anyway, when you work with Firebase you see the features related to store some kind of information have their own Rules tab, this is the way you can declare for allowing/denying the access to certain resources based on the user who is trying the request.

A bad practice is to keep the resources open for everybody throughout the web, if so, anyone could perform CRUD operations on your site/app, modify assets, or even remove collections(and I am pretty sure you don't want that, do you?), you can read more information right here.

Show Me The Code

Disclaimer: For this post, a shallow explanation will be given related to Firestore ans Security Rules version 2, released on May 2019

The 3 main pieces to be focus on are:

  1. Default versions for test and prod
  2. Writing rules straight in console vs versioned file
  3. Allow/Deny access according to auth states and functions

Default versions for test and prod

Whenever you start a new Firebase project, in the section Firestore/Rules, creating a new db project will present 2 options, you can opt any of those in, let's see the difference:

Mode Production

Under this mode, any access is explicitly denied, this forces the developer to add some logic for explicitly allowing users to access the resources.

The default schema for production mode looks like this:

  rules_version = '2';
  service cloud.firestore {
    match /databases/{database}/documents {
      match /{document=**} {
        allow read, write: if false;
      }
    }
  }
Enter fullscreen mode Exit fullscreen mode

Something to remark is the fact that the rules keep the track on a historical, this means that is possible to activate a previous rule schema, compare a former version against the most recent one, and even delete unused schemas; this also helps to easily find bugs when adding new docs or collections.

Mode Test

Under this mode, any access is explicitly allowed to any user for the next whole month(by default through a timestamp). This will allow the developer to start the work right away, though, the idea is set the schema as soon as possible for allowing only expected users to consume resources.

The default schema for test mode looks like this:

  rules_version = '2';
  service cloud.firestore {
    match /databases/{database}/documents {
      match /{document=**} {
        allow read, write: if
            request.time < timestamp.date(2021, 4, 20);
      }
    }
  }
Enter fullscreen mode Exit fullscreen mode

Just like in production mode, the rules keep the track on a historical, also, a few days before due date, the main email registered will start receiving notifications about the expiring access to the database collections and docs unless a new rule schema is applied.

Writing rules straight in console vs versioned file

Straight in Console

Writing straight to Firebase Console is an option, it is easy and fast.

One more feature with this approach, is the integration with an sort of built-in linter, it determines some syntax issues before publishing, in fact, it throws an error, and the changes wont be published till the issue is fixed.

Versioned File

A cleaner way to have the rules is through a versioned file, in your firebase.json file, you can add an entry for firestore/rules(and even indexes!).

  {
    "hosting": {
      ...
    },
    "firestore": {
      "rules": "firestore.rules",
      "indexes": "firestore.indexes.json"
    },
    "functions": {
      ...
    },
    "emulators": {
      ...
    }
  }

Enter fullscreen mode Exit fullscreen mode

Then you can add the firestore.rules file and keep the versions in git or any other version handler

The flow goes as shown below, if more info required, take a look at the documentation right here.

  // Set up Firestore in your project directory, creates a .rules file
  firebase init firestore

  // Edit the generated .rules file to your desired security rules
  // ...

  // Deploy your .rules file
  firebase deploy --only firestore:rules
Enter fullscreen mode Exit fullscreen mode

Allow/Deny access according to auth states and functions

Either way the writing of rules goes, the critical part is the access to docs and collections. It is possible to create js functions for avoid duplicating conditionals for every element, I wrote a post related to Adding roles to the authentication with Vue(x)+Firebase in case you want to check the use of claims and token additions.

So, for example, you could add a function for determining whether a request comes from an Admin or a Regular user profile, according to the resolution(handle by Firebase itself), the access to different resources is granted or not.

Take a look at the example below:

  rules_version = '2';

  service cloud.firestore {
    match /databases/{database}/documents {
      // true if the user is signed in and the claim is admin
      function isAdmin() {
        return request.auth.uid != null && request.auth.token.admin == true;
      }
      // true if the user is signed in and the claim is regular
      function isRegular() {
        return request.auth.uid != null && request.auth.token.regular == true;
      }

      // Shared collections
      match /settings/{doc} {
        allow read: if isAdmin() || isRegular();
        allow write: if isAdmin();
      }

      ...
      ...
      ...
    }
  }
Enter fullscreen mode Exit fullscreen mode

What happened in the code above?:

  • The functions created always ask whether the request incoming is related to a user authenticated, otherwise, the access is invalid and the request is denied
  • The function isAdmin(), when is invoked by an authenticated user, it looks for a particular token, in this case, the admin token, if presented, the request is validated
  • The function isRegular(),just like isAdmin() looks for a particular token, in this case, the regular token, if presented, the request is validated
  • There is a collection of settings, when a request for reading comes, the fetching is available only for authenticated users with a role of admin or regular
  • In the same collection of settings, when a request for writing comes, the upsert is available only for authenticated users with a role of admin

This is useful since even when the APIkey of your app/site is available for third-parties, the requests wont do any operations to your data without an authenticated-and-roled user.

Sometimes read and write could be to macro, you can granulate them a bit more:
_read rule can be broken into get and list
_write rule can be broken into create, update, and delete

More info about this topic can be found right here

Conclusion

As shown above, Firebase Firestore rules are quite powerful, allowing even write some functions in the declared schema for avoid repeating the code over and over again; maybe you could have a better way to do it, let's discuss in a thread below!

Thanks for reading!

Discussion (0)

pic
Editor guide