Authenticate Firebase Users to consume your GCP backend

Sahilkumar
CapTech Corner

--

Introduction

In this blog, I will show how you can use some of the great built-in functionality already offered by Firebase and utilize it to extend your application to consume GCP resources as well. This simple trick expands the types of applications that can be built using Firebase by allowing you to also utilize services that GCP offers as well. Here is the github repo with the sample application that I will be going over in this post.

What is Firebase?

Firebase is a platform developed by Google and is meant to speed up the development of web and mobile applications by offering built-in functionality for authentication, storage, real-time database, messaging, and compute functions. It also has the ability to easily integrate with tools such as Google Ads, Slack, Jira, and Pager Duty. Firebase is currently leveraged by large companies such as The New York Times, Lyft, Venmo, and Trivago.

What are Firebase’s Shortcomings?

While Firebase is an excellent platform with tremendous capabilities, it also has some areas where its built-in functionality will not be enough to get the job done. The area I am going to talk about is the backend of an application; specifically the API service and the database features. Firebase’s API service and database features are solely made up of their compute functions and database.

Firebase Compute Functions and Their Limitations

Firebase’s compute functions are its cloud functions which are server-less functions to store your application logic and retrieve data from the real-time database. While server-less is a great feature, because it allows you to write your logic while not worrying about managing infrastructure, it isn’t always the right choice for every situation. There are times when an application needs dedicated servers to handle its backend traffic, and teams need the flexibility to have that option. In addition, Firebase’s cloud functions can only be built in a Nodejs runtime, unless you go through the GCP console, which brings complications of its own. When working through the cloud console, you can utilize Python 3.7 and Go 1.11 runtimes. This is a major limitation if another language is needed.

Firebase Databases and Their Limitations

Firebase offers two database services for use, their real-time database and Cloud Firestore. Cloud Firestore builds off the success of the real-time database and adds a more intuitive data model, faster querying, and better scale. For this reason, I will only touch on Cloud Firestore. Cloud Firestore is NoSQL document database managed by Google. Cloud Firestore and Cloud Datastore(GCP) are the only two document databases offered by Firebase and GCP. Both Firestore and DataStore are fully capable of meeting any need you have from a document database. However, a document database isn’t always the correct choice for applications. Today’s modern applications can utilize many different types of databases to store their data. Teams need the flexibility to go beyond choosing just a document database; they need to choose the one that fits their requirements best.

How To Get Past These Limitations?

To give an example of how to extend your Firebase application, I will go through a full-stack sample application that I built to help me fill out an NCAA March Madness bracket. In the application, I use Firebase to authenticate our users to sign in and sign out of the application. Then, I set up an API Gateway in GCP where I authenticate only our Firebase users to consume our backend service. The backend service grabs all the teams that we have available to query, and the database stores advanced statistics on each team.

Backend Service and Database for this Project

I will use a backend service made with Flask and that will run on GCP’s Cloud Run. Cloud Run is another server-less service (like Cloud Functions) offered by GCP. The big difference between Cloud Functions and Cloud Run is that Cloud Run serves containerized backends, whereas Cloud Functions do not have that capability. Containers provide all sorts of benefits, but I will not go over them here. It should be noted that any backend service offered by GCP can be used instead of Cloud Run such as a Kubernetes, App Engine, or even a Compute Engine backend. For our database we used Cloud Firestore only because a document database best fit the use-case of this application. However, any database service offered by GCP or even hosted outside of it could be used as long as your backend service is able to connect with it.

Load Up The Database

I started the project by loading up the Cloud Firestore database. I loaded it with data that was collected by a web scraping script of a site that kept track of the advanced analytics of different NCAA College Basketball teams that were tournament bound. The data had an average taken from all teams that were scraped and outputted in a JSON file, called tourney_team.json, as an array of objects where each key in an object is representing a team by name and an advanced statistic associated with that team. I use the script from firestoreupload.py to loads the JSON data into Firestore. The only thing to note is that because I ran the script locally from my computer, I needed to download service account credentials in order to have the permissions needed to write to the Firestore database. These credentials are downloaded as a credentials.json file. Loading this file allows for access to the Cloud Firestore database locally. The Cloud Run service’s service account is configured differently since it is the cloud. I will go over that now.

Setting up the Backend

I wrote the backend for this application in Flask which is a python micro framework which allows for making simple to complex backends for all scenarios. In the backend, I created two routes. One route is for querying all the names of teams to choose from and the other is for querying the dataset and filtering by the team name. The flask application is then containerized with the Dockerfile and deployed to Google Container Registry (GCR), which as the name suggests is a container registry hosted in GCP. From there, it can be deployed onto Cloud Run. In order to containerize the backend, I ran the following command:

docker build . -t gcr.io/{project-id}/{name-of-backend-service}:{version-number} -f Dockerfile

The reason to tag the container like this is for easy local deployment to GCR. After tagging the container with the naming convention above and authenticating the GCP command line on your local computer, I pushed the container to GCR with the following command:

docker push gcr.io/{PROJECT-ID}/{NAME-OF-BACKEND-SERVICE}:{VERSION-NUMBER}

It should be noted that both the backend service and the local script access the Cloud Firestore database, but they authenticate in slightly different ways. While the local script uses a local credentials.json file to authenticate, our Cloud Run service is hosted in GCP and authenticates through GCP as well. A service account with the needed permissions can be added through the cloud console or the GCP command line. Here is an example of deploying an image from GCR to Cloud Run and adding a service account with the needed permissions through the GCP command line.

gcloud run deploy --image {IMAGE-URL} --service-account {SERVICE-ACCOUNT}

Setting up the API Gateway

Now a database and backend service are set up in GCP. The next step is making the backend service consumable by a client. Using the HTTPS endpoint Cloud Run provides you will only allow everyone to access your backend service. For security purposes, it is needed that only authenticated users can access the backend. Here, I set up an API Gateway that only Firebase users from my project are authenticated to use it. Once the user is authenticated, they will be able to use the Cloud Run backend service and querying data from the Cloud Firestore database. The API Gateway needs an api-spec yaml file which defines the routes of the api to configure the set up of the API Gateway.

Here is an example of how the api-spec yaml file is set-up. Where it says API_ID, you’ll put the id of the API Gateway you create through the cloud console or GCP command line. Where it says ADDRESS_OF_GCP_BACKEND, the https address of any backend service in GCP can be used so that authenticated users can consume that service. The routes of your api will be defined under the paths section of the yaml file. Under the security section, we have placed the firebase rule. The firebase rule is defined below.

This security definition protects each route in the api by only allowing authenticated users to consume that route. In the areas where it says PROJECT_ID, the project id of your Firebase application is placed. Now when you deploy the API Gateway, there is a way to authenticate the Firebase users to consume the backend service.

Access the API Gateway from the Frontend

Now the API Gateway is set up, the last step is creating a client to access the backend service. The client code needs to initialize Firebase in order sign users in and out as well as access any other service from Firebase. Here is a snippet of the service code from the frontend to access the backend service. In order to authenticate, a bearer token is needed and it is set in the Authorization header. The bearer token can be grabbed can calling the authentication service of the Firebase SDK, grabbing the current user (if one is signed in), and calling the getIdToken() method.

This is an example of hitting the backend service to query by the team name. It should also be noted that when hitting the API Gateway from a web application, you’ll need a proxy server in between your web client and API Gateway. For a Vue application, you can easily have a proxy server set up when you run locally by having the following code in your vue.config.js file.

For a production application, and sticking to the Firebase theme, this a great use-case for Cloud Functions as a way to host your proxy server. It should be noted that this isn’t necessary for mobile applications that will use the API Gateway. Now there is a way to consume the backend service from an authenticated client. This is a simple example of how authenticated Firebase users can consume a GCP backend created outside of Firebase’s built-in backend functionality.

Conclusion

Firebase offers a lot of great built-in functionality that can easily be integrated into any frontend application. However, some of its built-in API services and database functionality limits what types of applications can be built. This blog showed an easy way to extend your Firebase application by using the authentication functionality and GCP’s API Gateway to have users securely consume any kind of API service and use any type of database that an application may need. This simple set-up opens up Firebase to create almost any kind of application that is needed. Once again, here is the code for the sample application.

--

--

Sahilkumar
CapTech Corner

Full Stack developer @ CapTech. Speciality in Cloud technologies, mainly GCP