A simple control panel for application admins to manage users and privileges using Firebase 
Tags: express, firebaseadminsdk, typescript, node
Published: 2021-02-17 06:48:27

---
title: A simple control panel for application admins to manage users and privileges using Firebase 
published: true
date: 2021-01-31 13:21:51 UTC
tags: expressjs,firebaseadminsdk,typescript,nodejs
canonical_url: 
---

### A simple control panel for application admins to manage users and privileges using Firebase.

![](https://cdn-images-1.medium.com/max/1024/1*vyLfTua4bZNPmTbE7b00GA.jpeg)

Firebase admin SDK is a server side SDK that allows us to interact with our Firebase project with admin privileges and perform certain actions to monitor and manager our project in our own way without using the Firebase console.

We are going to create a web application through which we will try to perform the actions that the Firebase admin SDK provides us with.

For that purpose we will need a front-end application which will act as the control panel and a back-end where we will integrate the admin SDK.

We will walk-through the front-end in another Part.

> This project is made using Ubuntu 20.04 OS.

### Pre-requisites

Create a firebase project and turn on Authentication — email & password authentication, and Realtime Database.

Visit [console.firebase.com](http://console.firebase.com) to create a project and configure as above.

### **Part 1 — Making the back-end**

We will be using **node-js**  **as the back-end** for making a rest API that our front-end application will consume. We will be using **Typescript** as it provides us a better and error free way to write **Javascript** code.

In the first step we are going to set up a node project to use Typescript. We will use express for making the rest API.

After creating a new folder and opening the terminal, let’s run the commands to create the project.

```
npm init -y
```

Okay, now we have a **_package.json_** file. Let’s install the required dependencies for our project.

```
npm install express cors dotenv firebase-admin
```

Also **typescript** , **tslint** and the type declarations for **cors** and **express** as **dev-dependencies**.

```
npm install typescript tslint @types/express @types/cors
```

Now let’s make a few changes to the **_package.json_** to really integrate typescript in our build process. We will add a “start” key to the scripts object as follows.

**_“start”: “tsc && node dist/index.js”_**

With this we are making sure that we are running the **Typescript compiler** **_(or tsc)_** to transpile all the  **.ts** files before running the application. We will modify the  **_.tsconfig_** file to mention the **_dist_** directory as the output directory for the typescript compiler later in the article.

Let’s mention the **“dist/index.js”** as the value of the **main** property as this file will be the entry point for our application.

With these changes, the **_package.json_** file should look similar to this.

{% gist https://gist.github.com/i-am-jyotirmaya/afef83ec5c27255322a8f363345c7914 %}

Now, let’s add a **_tsconfig.json_** file to the project root with the following values. This file is a configuration file for typescript specific to that project. Here we mention the **_“outDir” as “dist”_** which makes sure that **tsc** uses the dist directory as the output directory for the transpiled files.

{% gist https://gist.github.com/i-am-jyotirmaya/3c4b8780878c2e46c923f489231cbc2a %}

Now to configure Typescript linting for the project, in a terminal running in the root of the project, run the following command to generate **_tslint.json_**.

```
./node_modules/.bin/tslint --init
```

Open the generated **_tslint.json_** file and the **no-console** rule accordingly.

{% gist https://gist.github.com/i-am-jyotirmaya/14fc19c6beaccbccedb031c68963dce4 %}

Now lets start configuring firebase-admin sdk in our project. For initializing the firebase-admin sdk we need to configure a service account.

[Follow this guide](https://firebase.google.com/docs/admin/setup#set-up-project-and-service-account) to configure service account, download the key and rename it as **_service-key.json_**. Place this file in the root of the project directory.

This file should not be pushed to any remote location where it is subject to the risk of getting exposed. This file should be added to the  **.gitignore** file in case of git.

In this project we are using **dotenv** to simplify the task of setting and using environment variables in multiple environments. So, we will create a  **.env** file at the project root to where we can define the several values that we require as environment variables.

Create a  **.env** file and paste the following values:

```
GOOGLE_APPLICATION_CREDENTIALS=service-key.json

DB_URL=<Your DB URL>
```

Find your DB URL in the firebase console on top of your realtime database.

Now, we will create a directory structure as shown:

> src  
>  — models  
> —modules  
>  — — admin  
>  — — auth  
>  — — users

Create an **_index.ts_** file under the **src** directory.

In the **_index.ts_** file, let’s import the required modules.

```
import express from 'express';
import * as admin from 'firebase-admin';
import * as dotenv from 'dotenv';
import cors from 'cors';
```

Now, before we initialize the admin sdk, we need to configure **dotenv** , in order to inject the values mentioned in the  **.env** file as the environment variables.

```
const dotenvKey = dotenv.config();
```

Here you can remove the constant assignment as we are not going to use the **dotenvKey** constant in the project.

Now, to initialize the firebase-admin sdk.

```
admin.initializeApp({    
    credential: admin.credential.applicationDefault(),    
    databaseURL: process.env.DB_URL
});
```

Here, firebase-admin will use the environment variable mapped by us in the  **.env** file to access the **_service-key.json_** file. Also, we provide the **databaseURL** as our application will access the **realtime database**.

Now, let’s add the boilerplate code to create an **express**  app.

```
**const** port = process.env.PORT || 8080;
**const** app = _express_(); 
app._use_(cors({origin: true}));
app._use_(express._json_());
app._use_(express._urlencoded_({extended: false}));
```

We use _cors_ to avoid the **CORS** error while consuming endpoints from a browser.

A great thing about Typescript is that it allows the use of Interfaces, which we can use in this case to define a schema for our user model. So, under the models directory, we will create a file named **_UserModel.ts_** , with the content as:

```
export interface User {
    uid: string,
    email: string | undefined,
    displayName: string | undefined,
    lastSignInTime: string,
    creationTime: string,
    emailVerified: boolean,
    admin: boolean
}
```

Before proceeding with creating the routes, we need to secure the endpoints to prevent unauthorized access. For this purpose, we will create and use two middlewares, one for Authentication and another one for Authorization, i.e, to ensure whether the user requesting the operation has sufficient privileges.

Let’s create two files, **_authenticate.middleware.ts_** , **_authorize.middleware.ts_**.

{% gist https://gist.github.com/i-am-jyotirmaya/57c6ef4e5c8ba03c6af2a0f32170f7c3 %}

We will use **authenticate** to check if user has a valid idToken, and the **authorize** to check if the user has the required privileges.

> **Note:** The **authenticate** filter requires an **Authorization** header with the value in the following form:- **Bearer <idToken>**

> **Note:** The **idToken** can be retrieved by signing-in to firebase using the [REST API for firebase Authentication](https://firebase.google.com/docs/reference/rest/auth) for the moment.

Proceeding further, we need to create a **_UsersController.ts_** file under _modules →users_ that will contain the methods that our routes for **“/users”** path will utilize.

{% gist https://gist.github.com/i-am-jyotirmaya/b264c50166bac548ac50f4c8b5411996 %}

Here, **“//RA”** means the particular function requires admin privileges to perform its functionality.

This name of the methods define its functionality in this case.

> **getAllUsers()** _→Returns a list of all the users._  
> **getUser()** _→Accepts an uid and returns the particular user._  
> **updateUser()** _→Here we can perform many task, but in the current scope we will only set the displayName property of the user. You can play around with the function and find out all the fields that a user can have in firebase authentication._  
> **createUser()** _→Creates an user._  
> **createAdminUser()** _→Creates an user but with Admin privileges._  
> **deleteUser()** _→Deletes the user whose uid is provided._  
> **deleteUsers()** _→Deletes multiple users, uid array is fetched from body._  
> **forceSignOut()** _→Forcibly revoke a users refresh token, so that their login can’t continue._  
> **mapUserFromUserRecord()** _→An utility function, private to the file, which helps in extracting user from an_ **_UserRecord_** _object returned by the admin sdk, returns a_ **_User_** _ object._

Here, we use **customClaims** to store the admin role for an user. These **customClaims** can only be retreived through the admin SDK. _(I didn’t find any way to retrieve them from the client sdk, Kindly mention in the feedback if I am wrong.)_

We need a router that can map different routes to the functions. For that purpose we create a file **routes.ts** in the same directory. The file contains the following.

{% gist https://gist.github.com/i-am-jyotirmaya/5f9605def56808646474829f56621eef %}

Here, we define a configureRoutes function, which will take our express **app** and add the routes. We pass **true** to **authorize** where the route requires admin privilege to perform the task.

Similarly, we create two files, **adminController.ts** and **routes.ts** under _modules →admin_.

{% gist https://gist.github.com/i-am-jyotirmaya/4e99a23dd6abe791c13e5f9fbfa6d4d0 %}

Finally, we add the following import statements to the **_index.ts_** _ _file.

```
import { configureRoutes as configureRoutesForUsers } from './modules/users/routes';
import { configureRoutes as configureRoutesForAdmin } from './modules/admin/routes';
```

We listen for the endpoints by starting the server using the following piece of code:

```
app.listen( port, () => {
    console.log('Server listening at port '+ port);
});
```

Finally start the server on localhost using

```
npm start
```

We can use [Postman](https://www.postman.com/) to test the endpoints. _Ex: http://localhost:8080/users/<uid>_.

We can add users to the project by using the firebase console or by using the [REST API for firebase](https://firebase.google.com/docs/reference/rest/auth). And thereafter we can retrieve the uid from the firebase console.

> **Note:** For the first time, to add an admin, we can normally add an user and call the **/admin/:uid/make** endpoint by passing **false** to its **authorize** filter. After that we can revert the value to  **true**.

Kindly refer to the following Github repository for the complete project files.

[i-am-jyotirmaya/Firebase-admin-demo-api](https://github.com/i-am-jyotirmaya/Firebase-admin-demo-api)

#### Thank You