🔐 How to authenticate your users in Express using Firebase

If you have a web application or a native one and you use Firebase for authenticating users, all the job is done in the client side. Your server doesn't need to take care of that as Firebase will do it. However, sometimes you’ll need the server to do something and it’ll need to know if the request is coming from a legit user. Let’s see how it can be done:

Sending an identification token from the client

As mentioned, the server doesn’t know anything about authentication so some identification data needs to be sent from the client. This is where the identification token comes handy:

const token = await user.getIdToken();

The token variable will contain a JWT (JSON Web Token) that uniquely identifies the user for which it was generated. This token can be sent in the request as part of a JSON object in the body:

const posts = await fetch('/api/userPosts', {
  method: 'POST',
  body: JSON.stringify({ userToken: token, ...restOfData})
});

Parsing the body

Express provides the bodyParser utility to extract the body information from the request. Since we know that the client will be sending JSON data we can do the following:

const app = express();

app.use(bodyParser.json());

app.listen(port, () => console.log(`Express is listening at port ${port}`));

This utility will parse the JSON and put it in the body property of the request. Now we can access the token by creating a middleware function that reads the req variable:

const authenticateUser = async (req, res, next) => {
  const { userToken } = req.body;
}

userToken will contain the token generated in the previous step by the client.

Authenticating the userToken

Now that we have the userToken we can use the Firebase admin module to authenticate it and get the user that generated it.

First we need to create a firebase app:

import admin from "firebase-admin";

// This values are obtained from the Firebase
// console when registering your admin app
const serviceAccount = {
  projectId: process.env.FIREBASE_PROJECT_ID,
  clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
  privateKey: process.env.FIREBASE_PRIVATE_KEY,
};

const app = admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
});

More info on how to get the service account can be found in the official Firebase documentation. Note that I take a different approach here so that the values can be set via environment variables, but the goal is to have your app ready.

Next we can initialise the firebase authentication module:

import admin, { auth } from "firebase-admin";

// ...

const fbAuth = firebaseAuth(app);

Finally we can create an auxiliary function that will be used to check the userToken:

const getUserForToken = async (idToken) => {
  const token = await fbAuth.verifyIdToken(idToken);
  return await fbAuth.getUser(token.uid);
};

Using the auth.verifyIdToken method we decode the token sent from the client, then the getUser method returns the firebase user for a given uid which is present in the token.

Finally, we can update our authenticateUser middleware:

const authenticateUser = async (req, res, next) => {
  const { userToken } = req.body;
  try {
    const user = await getUserForToken(userToken);
    netx()
  } catch (e) {
    res.sendStatus(401);
  }
}

The middleware now tries to retrieve the user, if everything works fine the next function is called to invoke the next middleware function. On the other hand, if retrieving the user fails we send an Unathorized status code back to the client.