Securing your express/Node.js API with Firebase auth
Tags: javascript, firebase, express, auth
Published: 2019-09-23 00:36:19

---
title: Securing your express/Node.js API with Firebase auth
published: true
description: Using firebase to handle user authentication between your frontend and your backend
tags: javascript, firebase, express, auth
---

A lot of applications, be it a mobile app or a web app have some form of authentication. If you've worked on various apps, handling authentication can become quite a repetitive task and can get boring which is why I love to make use of external services such as auth0 or firebase to make authentication a breeze. These services can also take care of social auth and that can save us so many lines of code. All we will have to worry about is integration.

In this article, I am going to cover using firebase to secure our APIs so that only authorized users have access to our resources. A common way of securing APIs is with the use of JWT tokens which is generated after a user supplies valid auth credentials and this token is validated on every request. This is quite similar to what we are going to be doing with firebase. We will make use of firebase to handle the generation and validation of this token.

Note that this article is not intended to teach you how to create/start an express server. If you are not familiar with using Node.js or express, I will advise you to check that out before reading this article.

Time for us to dive into some code.

Visit your [firebase console](https://console.firebase.google.com) and create a new project if you haven't already done that.


## The server-side

For the server-side, we will be using the firebase admin SDK as it is more suited for what we are trying to accomplish. 

Use this command to install the admin SDK on your server:


```cli
npm i firebase-admin
```

To verify that you are calling APIs from a trusted environment, google recommends you to generate and download a service account key for your project and add it to a path in your environment. So head over to your console, generate a service account key, download it(JSON preferably) and add its location to a path(GOOGLE_APPLICATION_CREDENTIALS) in the environment where you will be running your server.

```cli
exports GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account-file.json

```
Look [here](https://firebase.google.com/docs/admin/setup#initialize_the_sdk) for more on this.

We can now create a service in our project where we will initialize our SDK with our credentials and export it.

```javascript
import * as admin from 'firebase-admin';

admin.initializeApp(
  credential: admin.credential.applicationDefault(),
  databaseURL: 'https://<DATABASE_NAME>.firebaseio.com'
);

export default admin

```

Next, we will write the logic that handles the creation of new users. We can place this logic in our auth controller or anywhere you think is suitable.

```javascript
import admin from './firebase-service';

export const createUser = async (req, res) => {
const {
      email,
      phoneNumber,
      password,
      firstName,
      lastName,
      photoUrl
    } = req.body;

    const user = await admin.auth().createUser({
      email,
      phoneNumber,
      password,
      displayName: `${firstName} ${lastName}`,
      photoURL: photoUrl
    });

    return res.send(user);
}

```

Now that our logic for creating users is in place. We will need to make sure that requests coming in are from authenticated users. We can achieve this by creating middlewares to protect routes that we want to keep private.

We will create an auth middleware to ensure there is a valid firebase token in the request header.

```javascript
import admin from './firebase-service';


const getAuthToken = (req, res, next) => {
  if (
    req.headers.authorization &&
    req.headers.authorization.split(' ')[0] === 'Bearer'
  ) {
    req.authToken = req.headers.authorization.split(' ')[1];
  } else {
    req.authToken = null;
  }
  next();
};


export const checkIfAuthenticated = (req, res, next) => {
 getAuthToken(req, res, async () => {
    try {
      const { authToken } = req;
      const userInfo = await admin
        .auth()
        .verifyIdToken(authToken);
      req.authId = userInfo.uid;
      return next();
    } catch (e) {
      return res
        .status(401)
        .send({ error: 'You are not authorized to make this request' });
    }
  });
};


```
With this middleware in place, the user gets an 'unauthorized' error every time they try to access a private resource without being authenticated.

Now that we have created our middleware, let's use it to protect our private route.


```javascript
import {Router} from 'express';
import {createUser} from './controllers/authcontroller';
import {checkIfAuthenticated} from './middlewares/auth-middleware';
import {articles} from from './data';

const router = Router();


router.post('/auth/signup', createUser);

router.get('/articles', checkIfAuthenticated, async (_, res) => {
  return res.send(articles);
});  

export default router;


```

In the code above, we have defined two routes. One for creating our user, the second for fetching articles only if the user is authenticated. Now let's head over to the client-side and see how this API can be consumed.


 


## The client-side

We could be consuming our API using any javascript client-side library or framework for web or mobile apps, so I will not specify any but will rather focus on the firebase javascript SDK. Although there might be some differences in the SDK specific to various javascript libraries/frameworks, the APIs are still quite similar to the official web SDK.


So, we install firebase on the client.

```cli
npm i firebase
```
Note: Your platform might require a different SDK and method of installation, examples are angular-fire and react-native-firebase.


To keep things clean, we can also create a service on our client for initializing firebase with our configurations.

```javascript
import * as firebase from 'firebase/app';
import 'firebase/auth';

const config = {
  apiKey: "api-key",
  authDomain: "project-id.firebaseapp.com",
  databaseURL: "https://project-id.firebaseio.com",
  projectId: "project-id",
  storageBucket: "project-id.appspot.com",
  messagingSenderId: "sender-id",
  appID: "app-id",
}

firebase.initializeApp(config);

export const auth = firebase.auth

export default firebase;

```

Your credentials are available on your [firebase console](https://console.firebase.google.com). If you're not using javascript on the web you should check out how to initialize firebase on your specific platform.

We will create an auth service for calling the signup endpoint and signing in of users.

```javascript
import axios from 'axios';
import {auth} from './firebase-service';


export const createUserAccount = (data) => {
  return axios.post('https://your-api-url/auth/signup', data)
    .then(res => res.data)
}


export const loginUser = (email, password) => {
  return auth().signInWithEmailAndPassword(email, password);
}

```

We have defined our logic for creating a user and logging them into our app. This is how we can check with firebase if a user is already logged in.

```javascript

firebase.auth().onAuthStateChanged(user => {
   if (user) {
     return user;
   }
});

```

Now that we have signup and login in place, let us go ahead and generate a token on our client-side for authentication on the server. This can easily be done with a single line of code. Yeah! you heard right, a single line.

``` javascript

const token = await firebase.auth.currentUser.getIdToken();

```
You can either use it as shown above in an async function or resolve the promise to get the token value. We will be making a request to our API with the token attached to the request header to access the articles resource.

```javascript
import {auth} from './firebase-service';

export const getArticles = async () => {
const token = await auth.currentUser.getIdToken();

return axios.get('https://your-api-url/articles', {headers:  
  { authorization: `Bearer ${token}` }})
  .then(res => res.data);
}

```

We've simply passed in our firebase token to the authorization header. it will be extracted on the server-side and used to authenticate our user. This will all be handled by the middleware we created earlier


## User roles

One very important part of user authentication is role management. What if we want to have different levels of authorization and restrict access to certain resources to users with certain roles. This is also very easy to implement with firebase authentication.

We will be managing the roles on our server and this is how we can go about it.

```javascript
import admin from './firebase-service';

export const makeUserAdmin = async (req, res) => {
  const {userId} = req.body; // userId is the firebase uid for the user

  await admin.auth().setCustomUserClaims(userId, {admin: true});

  return res.send({message: 'Success'})
}
```

Now that we can assign roles to our user, how do we check if a user has a certain role? Easy, when we verify a user's token in our middleware, we can easily access this information on the data that is returned. We will add a middleware that checks if our user has an admin role.

```javascript
import admin from './firebase-service';

const getAuthToken = (req, res, next) => {
  if (
    req.headers.authorization &&
    req.headers.authorization.split(' ')[0] === 'Bearer'
  ) {
    req.authToken = req.headers.authorization.split(' ')[1];
  } else {
    req.authToken = null;
  }
  next();
};

export const checkIfAdmin = (req, res, next) => {
 getAuthToken(req, res, async () => {
    try {
      const { authToken } = req;
      const userInfo = await admin
        .auth()
        .verifyIdToken(authToken);

      if (userInfo.admin === true) {
        req.authId = userInfo.uid;
        return next();
      }

      throw new Error('unauthorized')
    } catch (e) {
      return res
        .status(401)
        .send({ error: 'You are not authorized to make this request' });
    }
  });
};

```

We can now protect our admin resources with this middleware. Here are our updated routes


```javascript
import {Router} from 'express';
import {createUser} from './controllers/authcontroller';
import {checkIfAuthenticated, checkifAdmin} from './middlewares/auth-middleware';
import {articles} from from './data';
import {records} from './data/admin';

const router = Router();

router.post('/auth/signup', createUser);

router.get('/stories', checkIfAuthenticated, async (_, res) => {
  return res.send(articles);
});  

router.get('/admin/records', checkIfAdmin, async (_, res) => {
  return res.send(records);
});

export default router;


```

Any token without an admin role assigned to it will get an 'unauthorized' error if it tries to access our admin resource.


There is a lot more that can be covered but that is all we will be covering in this article. Hopefully, this is enough push to get you started with firebase auth on the server. You can check out more possibilities by exploring the [firebase docs](https://firebase.google.com/docs/auth).