Firebase limit access to certain domains

While I was developing Iridescent, due to the fact that it’s a internal project, I had to limit the access only to user with email of certain domains.

Firebase Auth has provided a parameter hd to limit accounts shown in Google OAuth UI:

var provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({
    hd: "example.com"
});

But in my case, employees and students belong to two different GSuite domains, and hd parameter only supports one domain.

Also, Firebase’s API Key is exposed to the client-side, validate domain only on front-end is obviously not an option, as the user can download and modify the script. There must be a validation also on the back-end. 

A small description: My project uses Firestore and Firebase Authentication. Login is required to visit the page. 
When a user logs in, a Firestore document named with user’s ID is created (or updated if it exists) under users collection to store information like name, email address or avatar URL.

So I wrote the following function for Firestore:

function validAccount(userEmail){
    return userEmail.split('@')[1] == 's.example.com' || userEmail.split('@')[1] == 'example.com';
}

Considering the case that I want to allow access only for people who has email s.example.com and example.com

This is a small example of Firestore rule:

/// Firestore Rules
service cloud.firestore {
    match /databases/{database}/documents {

        function validAccount(userEmail){
            return userEmail.split('@')[1] == 's.example.com' || userEmail.split('@')[1] == 'example.com';
        }

        match /{document=**} {
            allow read, write: if false;
        }

        match /users {
            match /{userId}{
                allow read: if request.auth != null && validAccount(request.auth.token.email);

                allow write: if request.auth.uid == userId  /// Avoid editing another person's profile
                                && validAccount(request.auth.token.email);
            }
        }
    }
}

Client side

Now that we have denied requests from non-white-listed domain, to improve the experience, an alert should appear when validation fails.

As I said, each time user logs in, a function is called to update personal information received from Google OAuth. Instead of making another request to validate domain, we can reuse this one to reduce number of API call.

I created a function called validAccountCheck to do

/// Auth.js

import * as firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';
/**
 * Check if the account used for login is valid
 * 
 * @returns {Promise}
 */
function validAccountCheck() {
    let user = firebase.auth().currentUser;

    return firestore.collection('users').doc(user.uid).set({
        'displayName': user.displayName,
        'email': user.email,
        'photoURL': user.photoURL
    }, {
        merge: true
    }).then(() => {
        return true;
    }).catch(err => { // Not a white-listed domain
        console.log(err);
        return false;
    });
}

export {
    validAccountCheck
};

If the attempt to set new values to user/${userID} fails, it means current user doesn’t have a email with white-listed domain.

validAccountCheck() returns a Promise with a Boolean, which indicates if domain is white-listed or not.

In my project, this function is called after login. If domain is not valid, user will be logged out and alerted.

let provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider).then(result => {

    validAccountCheck().then(validAccount => {  /// Check if user has a valid email account
        if (validAccount) {
            /// Redirect to homepage
        }
        else {
            /// Not valid account
            firebase.auth().signOut();
        };
    });
})

Leave a Reply

Your email address will not be published. Required fields are marked *