Skip to content

Firestore security rules for server-side clients

Read how to leverage Firestore Security Rules to protect the data of applications written in Go / Python.

Introduction

This article presents how to leverage Firestore Security Rules to protect the data of applications written in Go / Python.

Firestore Security Rules are used to enforce the protection of the data accommodated in a Firestore Database. This way, you can decouple the responsibility of protecting your data from application logic. However, application logic must be implemented with respect to these rules to enable data accessibility to end users.

Here you can find out more about the Firestore Security Rules. This article refers to the applications and databases hosted in Google Cloud.

How can you protect the Firestore data for Go / Python clients?

Firestore security rules can play a powerful role to protect your application data. But does it work with Python or Go? With some guidance, yes. 

Google currently provides client libraries for Python & Go that directly interact with the Google Cloud Firestore API through gRPC transport. Clients built over these libraries are therefore supposed to be set up for privileged (server) environments. These libraries are so-called Admin SDKs which bypass the security rules.

In simple terms, when you make a Firestore query using Python library, which looks like the one shown below:

from google.cloud import firestore
client = firestore.Client()
query = client.Query()# the query goes on…

This will ‘directly’ connect to the Firestore through the admin SDK (most probably using an application-default-identity of an IAM member) which will bypass the security rules while accessing the collections & documents.

Despite this limitation, using Google’s REST API to read/write data from Firestore is an option where the security rules will be respected.

REST API

Interaction of the application with the Firestore Database can be done using Google’s REST API. These interactions should be done via end-user credentials (i.e. Google OAuth2.0), which also enables access to request.auth.user objects in Firestore security rules. ’auth’ objects are essential when the data needs to be protected by a user, role or group.

Google provides a well-documented REST API schema that can be explored & tested using the API explorer.

In order to adopt this method, you need to follow the REST API conventions while accessing the target database and its collections.

When the app is hosted on GCP

Applications deployed on App Engine / Cloud Run (or any other GCP host environment) by default use service accounts to interact with other GCP services. In case of using the REST API for interacting with the Firestore database, a web client (i.e. http) should carry the authenticated user credentials instead of using its service account. 

Sample Architecture vs Security Rules

Below are some examples of an application architecture where the security rules can & can’t be applied:

As shown in example 1, the posture of the application seems like a backend/microservice where the client is expected to have certain IAM permissions to access the application hosted in Google Cloud. Supposably, the app is written in a language that creates a Firestore Client through the Admin SDK. Therefore the Firestore security rules won’t be respected.

In Example 2, the application can be invoked by the end-user through an additional layer of authentication. But this time the app interacts with the database through the REST API. In this case, the credentials of the end-user can be passed to the REST API for authentication instead of an IAM member with certain permissions. Read more in this related article.

Need more help?

For the ones looking for a ‘quickstart’ reference application in Go/Python on Google Cloud where the Firestore security rules are respected, here is an example step-by-step guide. 

Step-by-step example

Introduction

In this example, we have a backend application written in golang that receives an article with its content & title from end-users and stores them in a Firestore collection. And it provides an endpoint that renders a list of contents and titles. The purpose is to restrict the people that can access this list using Firestore Security Rules.

While receiving the content and title, it also receives end-user authentication with a bearer-token passed from the front-end. This token is also used to access the Firestore Database through the REST API.

Sample codes are stored in the following git repository:

Please note that this demo doesn’t guarantee a final (deployable) application. The purpose is to demonstrate the steps that should be taken in order to achieve the demo objectives. Therefore, the repository exists to support the described steps with example code pieces.

In order to provide a bit of concrete guideline, a firebase authentication example has been stored in the ‘frontend’ folder of the repository. However, the backend and frontend connection isn’t provided. (No user form is rendered for content and title, which could be easily implemented into the demo architecture)

So the example application architecture is the following:

Requirements

A Google Cloud project is required to follow this example.

Setup a Git repository & GCP project

After setting up a git repository & obtaining a Google Cloud project where the billing is enabled, a cloud shell (or a configured remote CLI) should be launched for creating the associated application resources on App Engine.

gcloud app create

Front-End

Development (Python, JS, html)

This example includes Firebase authentication for the end-user to sign in to the application.

Before implementing the Firebase authentication, you need an application domain in order to register to the Firebase authentication backend. Therefore a helloworld program will be sufficient at this step. An example repo is provided in the helloworld folder.

Deployment to App Engine

After cloning the repository into Cloud Shell, go to the helloworld folder and deploy the app.

gcloud app deploy

Setup Firebase Authentication

This demo contains Firebase authentication, but Google Identity Aware Proxy -IAP-, could also be used for authenticating the users to the application. To embed a firebase authentication layer into our App Engine, the following steps should be taken:

  • Add your GCP project to the Firebase console
  • Go to the authentication page in the console, and register your application, which then will generate a modular script that can directly be implemented to your HTML template.

More detailed instructions can be found in the official documentation.

An auto-generated snippet should look like the <head> element of the index. html file.

The script.js file in this demo validates authentication and fetches the access token when the user is signed in.

  • After these steps, update your App Engine to contain the Firebase Authentication layer.

Once authentication is set up and deployed, a login page will appear in the application as shown below:

Back-End

Development

The backend example is provided in golang. It consists of the following elements:

  • An API framework (gin):

Receives an article with its content and title through a POST request. 

API layer also receives a token along with the article from the trusted client (frontend).

  • REST calls: 

Posts the article data into the Firestore database using the REST API.

Adopting REST API Schemas

Golang structs can be used to model the data following the REST API conventions. An example can be found in the RESTAPI Module of the demo repository.

This demo doesn’t guarantee the latest REST API schemas, Google Cloud REST API conventions can easily be explored through the official website. (v1 Firestore REST API references)

Making requests to the Firestore REST API

The Bearer token received from the front-end can be passed to the REST API in order to write/receive data from Firestore Collections as demonstrated in the request.go file in the Data module.

No additional authentication is required at this stage to access Firestore resources, the end-user token will be enough to make this API request for this example.

Deployment to Cloud Run

A sample Dockerfile can be found in the demo repository as well as a cloud run configuration file to deploy the backend to Cloud Run. (the go version in this demo isn’t compatible with App Engine at the moment)

Firestore Security Rules

Syntax

In the following example of Firestore security rules:

  • Allows any authenticated user to write into the Article Collection
  • Only allows reading if the user exists in a different collection & its document.
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /articles/{article} {
    	allow read: if request.auth.token.email in get(/databases/$(database)/documents/roles/0Z4CaGhuGIw2ksFPPHBp).data.reviewers;
    }
    match /gossips/{gossip} {
    	allow write: if request.auth.token != null;
    }
  }
}

As it can be seen in the example, an external document:

databases/(default)/documents/roles/0Z4CaGhuGIw2ksFPPHBp

contains an array field called ‘reviewers’, therefore if the authenticated user is not part of this reviewers record, Firestore will forbid the readout of articles for that user.

Deploy

This security rule then can be applied to the database through Firebase Console or CLI

On the console, it can be found on Database → Rules tab as shown:

These rules can also be systematically deployed and managed through IaC such as Terraform

Notes

This example only demonstrates that once the application grows and business logic in the application code gets more complicated, Firestore Security Rules can enforce the protection of the data on a very low level.

Please refer to the Firestore documentation to follow the best practices and see what else is possible using security rules syntax.

Next Steps

As the next step, the frontend can be customized to fetch/pass the data that is actually needed in the backend.

The backend can be adjusted to actually revalidate the token received from the frontend to enhance the application security. Then, the data models can be designed to read/write the data that is actually needed in the Firestore Database & front-end.

Security rules should be customized to actually serve the business demands.

Summary

This tutorial has covered the following elements of an application architecture:

  • Firebase authentication at the frontend
  • Backend that receives data from front-end (body and token)
  • Backend that passes the data to Firestore through the REST (including authenticated user object)
  • Security rules that enforce the data read & write by user & authentication