Firebase: securing Express API

Authentication is an intrinsic part of any application. Relying on well developed and tested tools is normally a good path to follow, giving the complexity of providing identity. Among those tools we can find Auth0, Cognito or Firebase.

This post will cover how to enable security to an Express API - the Node.js web framework of choice for Maratel - using Firebase.

Firebase admin

A little story about Maratel: my brother in law owns this hotel and the software he uses to manage the hotel is no longer maintained. The company behind it closed its doors a long time ago. This project is slowly progressing but aims to save him from a headache. Meanwhile, I will try and take some content from out of it.

Story aside, we need two things to start working with Firebase. First, we need a project. If one is not available, we can learn how to do it here. Second, we need to include Firebase’s library to our project. Let’s do it:

yarn add firebase-admin

Now, here is how we can begin working with our newly created project:

const firebase = require(“firebase-admin”);

// Initialize
const serviceAccount = require(process.env.FIREBASE_SERVICE_ACCOUNT);
const firebaseApp = firebase.initializeApp({
  credential: firebase.credential.cert(serviceAccount),
  databaseURL: process.env.FIREBASE_DATABASE_URL
});

module.exports = firebaseApp;

This file is initializing and exporting our Firebase application. From this application, we’ll have access to all the features such as authentication, database, and storage.

Two important things to notice here:

  • FIREBASE_SERVICE_ACCOUNT: it’s the path to our service account JSON.
  • FIREBASE_DATABASE_URL: it’s the URL to our project, represented in this way https://appid.firebaseio.com.

Once the application has been created, it’ll be exported to be used in other parts of our application.

Express middleware

Let’s not get in details on express middleware, but basically, it’s a function that has access to request and response objects, which can be changed by the middleware. It can also end the request cycle. More about middleware can be found here.

Let’s take a look at how this middleware looks like:

const firebase = require('../../loaders/firebase');
const logger = require('../../loaders/logger');

async function authenticated(request, response, next) {
  const token = request.headers?.authorization
    ?.toLowerCase()
    .replace('bearer ', '');

  if (token) {
    try {
      await firebase.auth().verifyIdToken(token);
      return next();
    } catch (e) {
      logger.debug('Invalid firebase token');
    }
  }

  return response.status(401).send();
}

module.exports = authenticated;

With this middleware, we’re leveraging the optional chaining released within Node.js 14. What’s happening in the piece of code above is that we expect the request to have the authorization header. With the content of this header, we’ll use the Firebase admin library to check if it’s a valid token. In the absence of the header or invalidity, 401 is the code to be returned.

Now, let’s take a look at how to use this middleware:

const authenticated = require(../middlewares/authenticated);

router.get(/costumers, authenticated, (request, response) => {
  return response.json([
    { name: Special Costumer 
  }]);
});

Alright! Now our endpoint /costumers is secured. Using Firebase for authentication is straightforward and counts on plenty of documentation available for us to thrive.

There is a lot more to cover, but we might continue in another post.

Thanks for reading!