Controlling Access With Firebase Custom Claim

danielle-macinnes-Ehtk87Go0Wg-unsplash.jpg When building applications, you may need to control a user's access based on the user's role. For example If your building a school management software, you may want to grant a different level of access to the students and another level of access to the guardians, or you may want to have admin users with special privileges in your application. Firebase lets us build applications without having to worry much about our back end. This article will show how to control access with custom claim (admin and normal users) using a cloud function together with the Firebase Admin SDK (nodejs).

Note: This article does not show how to setup authentication with Firebase from scratch (assumes you already have authentication setup, but want to go a step further by adding custom claims). If you would like to see how to setup Authentication with Firebase, check out this awesome article by Victoria Lo.

To setup custom claim using firebase, here are the steps you need to take:

1. Install the Firebase CLI tools which we will use to set up the cloud function locally

To install the Firebase CLI tools run

npm install firebase-tools -g

To initialize you firebase project locally, run

firebase init

you will be guided to set up the firebase services you need for this project(select functions and any other service you will be needing in your project).

once the setup is complete, your project should look similar to this

Screenshot from 2020-08-30 17-35-15.png

once done, the next step is to write your cloud function in the index.js file inside the functions folder. we will write a cloud function that set's a custom claim on a user's authentication token. The code snippet will look like this

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp();

exports.addAdminClaim = functions.https.onCall((email) =>
    admin
        .auth()
        .getUserByEmail(email)
        .then((user) => admin.auth().setCustomUserClaims(user.uid, { admin: true }))
        .then(() => ({ message: `user with ${email} has been made an admin` }))
        .catch((err) => err)
);

we've just written a firebase callable cloud function that takes an email, then using the firebase admin SDK and the email passed the cloud function, we get a user's authentication token and set a custom claim of "admin: true" on it.

2. Deploy the cloud function

To Deploy the cloud function, run

firebase deploy --only functions

3. Call the function from your client App

In our client side app,

import firebase from 'firebase';
const functions = firebase.functions();
const makeAdmin = functions.httpsCallable('addAdminClaim");

//get the email of the user you want to make an admin
makeAdmin(email).then( 
//sets the admin claim on the user with the email address passed and returns the response from the addAdminClaim cloud function
).error( 
//handles error here 
)

An ideal workflow would most likely involve having an input that asks for the email address of the user you want to make an admin, once you get this users email and the form is submitted you call the makeAdmin function while passing the users email, then handle the promise returned from the makeAdmin function, as shown above.

4. Accessing the admin claim on the auth token in your client side app: You most likely would like to display a different interface to the admin user and would need to find out if an authenticated user is an admin or not. whenever you authenticate a user, the data returned always has a user property. To access the claims on that user property, you will need the code snippet shown below:

user.getIdTokenResult().then(({claims}) => claims.admin )
//you can store the response from the function execution in your application state, then use it to tell if a user is an admin

5. Accessing the admin claim on the auth token in firebase security rules:

You may also want to restrict access to your database depending on whether or not a user is an admin. You can do this by checking if the property you set on the user token ( in this example, the property is admin) exits. let's say we are building a flight management application and we have a flights collection in our database and we only want an admin to be able to create and update available flights, our firestore security rule will look like this:

 match /flights/{flightId} {
      allow write: if request.auth.token.admin == true;
    }

voila!, hope this article has been able to show you how to control access using Firebase custom claim.