Firebase Security Rules

First Published: 2 July 2018
Updated on: 24 July 2019

This is the seventh and last post of a 7 post series that will teach you how to build an app using Ionic Framework and Firebase from scratch.

We are going to start preparing our app to go public, so the first thing we will need to do is update our security rules on the server, we do not want people connecting to the app and having access to someone else’s data.

Firestore Security

With the Cloud Firestore Security Rules, we can focus on building a great user experience, without having to manage infrastructure or write server-side authentication and authorization code.

The idea is to authenticate users through Firebase Authentication and set up rules to determine who has access to data stored in Cloud Firestore.

You can find your security rules in the Rules tab in the Cloud Firestore section of the Firebase Console.

To start securing our database we need to understand how the security rules work, let’s take a look at the default ones that come when you create the app.

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false;
    }
  }
}

The security rules work matching documents in the database, they have two permissions, read and write which are both false by default, meaning, no one has access to the database.

To start working with them, we tell them to allow all read/write operations, since we’re going to be in development mode:

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write;
    }
  }
}

The =** symbol is a cascade operator, rules don’t cascade by default.

So if you set up a read/write rule for the document users/{userId} but don’t set up read/write rules for users/{userId}/tasks/{taskId} no one will have access to the taskId documents.

When you use the =** operator, you’re telling Firestore rules that if the user matches the condition to read that document, they should be able to read all the sub-collections and documents below that tree.

The brackets mean we’re using a wild-card, for example, if I have a collection called users that has the documents for each user’s profile, I’d only want the profile owner to be able to have read/write access.

service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read, write: if request.auth.uid == userId;
    }
  }
}

Notice how we’re getting the user’s uid from the request object. When working with Firestore rules, we have two available objects.

The request object has information about the request made, such as the authenticated user: request.auth, and the time the request was made: request.time.

The resource object is THE Firestore document we’re accessing. For example, let’s say we have public and private profiles, each profile has a flag called public and it’s either set to true or false.

The resource object gives us access to that flag:

service cloud.firestore {
  match /databases/{database}/documents {

    match /myCollection/myDocument {
      allow read: if resource.data.public == true;
    }
  }
}

In that case, people will only be able to read profiles marked as public.

Storage Security

You should also set up rules for Firebase Storage, that way you can protect your users’ files.

You will need to go to: console.firebase.google.com/project/YOURAPPGOESHERE/storage/rules

Identifying your user is only part of security. Once you know who they are, you need a way to control their access to files in Cloud Storage.

Cloud Storage lets you specify per file and per path authorization rules that live on our servers and determine access to the files in your app. For example, the default Storage Security Rules require Firebase Authentication to perform any read or write operations on all data:

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

Data Validation

Firebase Security Rules for Cloud Storage can also be used for data validation, including validating file name and path as well as file metadata properties such as contentType and size:

service firebase.storage {
  match /b/{bucket}/o {
    match /images/{imageId} {
      // Only allow uploads of any image file that's less than 5MB
      allow write: if request.resource.size < 5 * 1024 * 1024
                   && request.resource.contentType.matches('image/.*');
    }
  }
}

Next Steps

Congratulations on finishing the course, if you want to go into more in-depth use cases using Ionic and Firebase, like AngularFire2, Cloud Functions, and Push Notifications, make sure to check my PREMIUM course at Building Firebase Powered Ionic Apps.