John Stewart

Hi! I'm John. Software engineer, blogger, tech head, typically making or talking about software. JavaScript mainly. Android enthusiast.

Firebase Auth + React + MobX

Keep it simple

I've always struggled with authentication mainly because Firebase Auth was not around a few years ago. But now it's here, in fact it's been here for a bit and it makes adding auth to your app fairly easy. There are a couple different strategies for doing this sort of thing but I personally use this one as it seems to make the most sense to me. So here it is for you or for my future self.

High Level

What I do when the page loads is get the current auth status of the user. I then set the app store (MobX) with that user status and then finally render the React app.

Within the React app I have React Router that is used to create the pages display. For any routes that I think need auth, I created a hoc (Higher Order Component) to check the auth state from the app store.

If the user is logged in then I render the route component otherwise I redirect to the login route which takes you through the login process that is run by Firebase Auth.

The login is a simple redirect on success that redirects to the homepage which starts the entire process outlined above.

Hopefully, that makes sense. Let's take a look at some code to illustrate this a bit better.

Deps

Here is a list of the dependencies used to make this happen:

I used create-react-app to get up and running.

Setup Firebase

There is a tiny amount of setup needed for Firebase so let's take a look at that code real quick.

fb.js

import firebase from 'firebase/app';
import 'firebase/auth';

const config = {
  apiKey: "abcdefgh_123456789",
  authDomain: "your-app.firebaseapp.com",
  databaseURL: "https://your-app.firebaseio.com",
  projectId: "your-app",
  storageBucket: "your-app.appspot.com",
  messagingSenderId: "1234567890"
};

firebase.initializeApp(config);

export default firebase;

The above config can be found in your Firebase Project console.

Here I bring in firebase and initialize it and then export it for use within my React app.

If you want to learn more about how to get things going here are the Firebase Docs.

App Store

The store I'm using in this project is MobX but the principles hold true for Redux or React Context or any state management solution.

AppState.js

import { observable, action } from 'mobx';

class AppState {
  @observable loggedIn = false;
  @observable user = null;
}

const appState = new AppState();
export default appState;

Here is an AppState class that has two observable properties that are managed by mobx. At the end we export a single instance of the AppState to be shared across the React app.

Router

Next up is setting up the routes within React Router.

App.jsx

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';

import protectedRoute from './protectedRoute'; // HOC (Higher Order Component)

// "Pages"
import Home from './Home';
import SignIn from './SignIn';

const App = () => (
  <Router>
    <div>
      <ul>
        <li>
          <Link to="/">Home</Link>
        </li>
        <li>
          <Link to="/connect">Connect</Link>
        </li>
      </ul>

      <hr />

      <Route exact path="/" component={protectedRoute(Home)} />
      <Route path="/connect" component={SignIn} />
    </div>
  </Router>
);

export default App;

As mentioned before, we have a hoc component called protectedRoute that will simply check if you are logged in or not. The /connect route is not protected because it leads us to the SignIn page. It will be up to you what routes you think need to be protected but any that do can be wrapped in the protectedRoute component.

Protected Route

Let's take a look at the implementation of the protectedRoute component.

protectedRoute.jsx

import React from 'react';

import appState from './AppState';
import { Redirect } from 'react-router-dom';

const protectedRoute = (RouteComponent) => {
  if (appState.loggedIn) {
    return <RouteComponent />;
  }
  return <Redirect to="/connect" />;
};

export default protectedRoute;

So here this high order component is really more of a higher order function but still serves the same purpose. Here we have a function that takes in a RouteComponent which we can also refer to as the page we want to render. Before we render the RouteComponent we check appState.loggedIn. If the user is logged in then we show the component otherwise we redirect to the /connect route to login.

SignIn

Let's say the user is not logged in so we get to the SignIn page.

SignIn.jsx

import React, { Component } from 'react';
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth';

import fb from './fb';

export default class SignIn extends Component {
  uiConfig = {
    signInFlow: 'popup',
    signInSuccessUrl: '/',
    signInOptions: [
      fb.auth.GoogleAuthProvider.PROVIDER_ID
    ]
  };
  
  render() {
    return (
      <div>
        <StyledFirebaseAuth uiConfig={this.uiConfig} firebaseAuth={fb.auth()}/>
      </div>
    )
  }
}

Here I do a couple things. I bring in StyledFirebaseAuth which is a nice React Component built by the Firebase team that gives us a styled Sign In UI.

This component takes a config that tells the component a few different things. I'm telling it that I want the 'popup' sign in flow, that I want it to redirect to the root of the site on success, and finally that I want it to use Google auth. Finally, I pass in the Firebase Auth instance by importing the fb.js file we created in the beginning and passing it that instance via fb.auth().

App Start

On success of the sign in flow the user will be redirected to the root / URL where we will run the following code.

index.jsx

import React from 'react';
import { render } from 'react-dom';

import App from './App';

import fb from './fb';

import appState from './AppState';

fb.auth().onAuthStateChanged((user) => {
  if (user) {
    appState.loggedIn = true;
    appState.user = {
      displayName: user.displayName,
      email: user.email,
      metadata: user.metadata,
      phoneNumber: user.phoneNumber,
      photoURL: user.photoURL
    };
  }

  render(<App />, document.getElementById('root'));
});

Here I bring in the App, the firebase instance (fb), and the app state (appState).

The fb.auth().onAuthStateChanged function is called and returns a user object or a falsey value. If the user object is truthy then we can assume the user is logged in.

From there we can set our app state properties to their respective values.

Finally, we render the App and the app starts up with the appState and will either render the route being requested or will take you to the SignIn page to being the process again.

Summary

In the end, I hope that approach made sense. As I was putting it together it did get a bit confusing or circular so maybe there is more room for improvement here. Either way this should get you up and running with Firebase Authentication within your React App.


If you liked this article and want to say hi, then you can find me on Twitter.