In this tutorial, we’ll be building a simple react application with authorization using Firebase with private routes. Before we dig into the details, I would like to mention some ready-to-use best react dashboard templates and web application templates, which come with Firebase Authentication. Let’s start setting up firebase authentication in react app.

You can check them out and use them directly in your project to save lots of time and money, also they are built by experienced developers, so you get a chance to learn a lot as well.

Let’s now move to our tutorial.

Contents to Set up Firebase Authentication in React App

  • Introduction
  • Prerequisites
  • Setting up React
  • Setting up Firebase in React
  • React Router
  • Authentication Context using Firebase and React
  • Private Routes in React
  • Create View Components in React
    • Signup in Firebase
    • Sign in Firebase
    • SignOut in Firebase
  • Conclusion

Introduction

We use authentication to recognize a user’s identity. At the end of this article, we’ll have built a simple React application that allows users to authenticate using Firebase and we’ll make sure that only authenticated users can access the application.

Prerequisites

  • Have basic knowledge of Javascript
  • Make sure you have Node>=8.10 and npm>=5.6 on your machine for a better experience
  • Have a basic understanding of React, Context, Hooks, and Routes.
  • Comfortable with command line
  • Text editor
  • A Firebase account

Setting up React

To create a project in React run the command below:

npx create-react-app react_firebase_auth

npx is a package runner tool that comes with npm 5.2+. We just created a project named react_firebase_auth. Go to the project and start it.

cd react_firebase_auth
npm start

We will need some dependencies for our application such as:

  • react-dom that contains the DOM bindings for React Router, I mean, the router components for websites.
npm install --save react-router-dom
  • material-ui to implement Google’s Material Design.
npm i @material-ui/core --save

Our react application was set up successfully. Now it’s time to set up the firebase.

Setting up Firebase Authentication in React

Let’s begin by creating a new firebase application. To this follow the steps below. Go to the Firebase console.

  1. Press Add Project
  2. Enter your app name
  3. Accept the terms
  4. Press Create Project
  5. Wait until the app will be created
  6. And press Continue
  7. Go to Authentication tap
  8. Click Set up sign-in methods
  9. Choose your email/password and enable it.
  10. After it is done go to Project Settings and scroll down to the list of platforms. Select Web.
  11. Copy your app configuration
Javascript Config

On the sidebar menu click Authentication, go to the Sign-in method and enable email/password.

Now, let’s set up a firebase in our react application. Create a firebase.js file in your src folder. Install firebase dependencies using the command below:

npm install --save firebase

Open firebase.js and paste the javascript script that you copied in the Firebase Console.

firebase.js

As you can see, we can initiate our firebase app using firebase.initializeApp and export it as an app. By doing so, we have total access to our database.

React Router

Now, go to your App.js and add some routing.

import React from 'react';
import "./App.css";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Home from './Home'
import Login from './Login'
import SignUp from './Signup'
function App() {
  return (
    <Router>
        <div>
          <Route exact path="/" component={Home} />
          <Route exact path="/login" component={Login} />
          <Route exact path="/signup" component={SignUp} />
        </div>
      </Router>
  );
}
export default App;

We wrapped our layout into BrowserRouter which provides Browser Context to all our applications. Basically, it allows us to use Routes, Links, Redirects, and other routers’ features.

Now, to work with authentication we will need to have to store our authentication states if you’re logged in or not and update our component tree. To do this we’ll use React Context API.

Authentication Context using Firebase and React

Create Authentication.js in your scr folder and past this:

     import React, { useEffect, useState } from "react";
    import app from "./firebase.js";
    export const AuthContext = React.createContext();
    export const AuthProvider = ({ children }) => {
      const [currentUser, setCurrentUser] = useState(null);
      const [pending, setPending] = useState(true);
      useEffect(() => {
        app.auth().onAuthStateChanged((user) => {
          setCurrentUser(user)
          setPending(false)
        });
      }, []);
      if(pending){
        return <>Please wait...</>
      }
      return (
        <AuthContext.Provider
          value={{
            currentUser
          }}
        >
          {children}
        </AuthContext.Provider>
      );
    };

In this file, we had to import the app from firebase.js where we have our firebase API and we created a context. The context in react is everything that allows you to propagate some data to the whole react component tree.

And we created a provider component that lets you store our authentication state. It holders a user and we will update every time our authentication state will change in firebase. For this, we use hook, useEffect, to signup for changes to our firebase object and we pass an empty array to our useEffect as a second argument so that it will run once when component AuthProvider will be mounted in our tree.

Then in our AuthProvider layout, we used AuthProvider.Provider and we passed our current user that we get from firebase on every auth state change, we passed it as a value to our AuthProvider.Provider and then we render children passed to this component.

Now, go back to App.js and wrap our layout into AuthProvider.

    <AuthProvider>
      <Router>
        <div>
          <Route exact path="/" component={Home} />
          <Route exact path="/login" component={Login} />
          <Route exact path="/signup" component={SignUp} />
        </div>
      </Router>
    </AuthProvider>

So, everything below it in our component tree will have access to the current user through the context API. In our case, if you’re logged in you’ll have a user object having all the user descriptions and if you´re logged out you’ll have a null or undefined state user object.

Private Routes in React

We can create our private routes to allow only the authenticated users can go to the home page.

Create PrivateRoute.js in your scr folder.

import React, { useContext } from "react";
import { Route, Redirect } from "react-router-dom";
import { AuthContext } from "./Authentication";
const PrivateRoute = ({ component: RouteComponent, ...rest }) => {
  const {currentUser} = useContext(AuthContext);
  return (
    <Route
      {...rest}
      render={routeProps =>
        !!currentUser ? (
          <RouteComponent {...routeProps} />
        ) : (
          <Redirect to={"/login"} />
        )
      }
    />
  );
};

export default PrivateRoute

Here we need to know what component should be rendered if a user is authenticated. So we take the component and the rest of the props { component: RouteComponent, …rest }. PrivateRouter will be basically a wrapper on regular routes. So we pass the rest of the props {…rest} and then in our route render function, depending on if we have used it or not, we will render our route component or redirect to the login page.

Go back to your App.js and make these changes:

 <AuthProvider>
      <Router>
         <div>
           <PrivateRoute exact path="/" component={Home} />
           <Route exact path="/signin" component={Signin} />
           <Route exact path="/signup" component={SignUp} />
        </div>
      </Router>
  </AuthProvider>

Now let’s create our view components. We’ll use material UI for this. To get deeper about Material-UI you can click here to get it done, using the official documentation.

Create View Components in React

We’ll use material-ui for our interfaces. Make sure you have installed the material-ui dependencies.

NOTE: In this article, we’re covering Firebase Authentication in React. So, for more details about material-ui go to the official documentation.

Create SignIn.js in your src folder and paste the code below.

import React from 'react';
import Button from '@material-ui/core/Button';
import CssBaseline from '@material-ui/core/CssBaseline';
import TextField from '@material-ui/core/TextField';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '@material-ui/core/Checkbox';
import Grid from '@material-ui/core/Grid';
import Box from '@material-ui/core/Box';
import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
import Container from '@material-ui/core/Container';

import { Link } from 'react-router-dom'
function Copyright() {
  return (
    <Typography variant="body2" color="textSecondary" align="center">
      {'Copyright © '}
      <Link color="inherit" href="https://pacoconsulting.co.mz/">
        PACO IT Consulting
      </Link>{' '}
      {new Date().getFullYear()}
      {'.'}
    </Typography>
  );
}
const useStyles = makeStyles((theme) => ({
  paper: {
    marginTop: theme.spacing(8),
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
  },
  avatar: {
    margin: theme.spacing(1),
    backgroundColor: theme.palette.secondary.main,
  },
  form: {
    width: '100%', // Fix IE 11 issue.
    marginTop: theme.spacing(1),
  },
  submit: {
    margin: theme.spacing(3, 0, 2),
  },
}));
export default function SignIn() {
  const classes = useStyles();
  return (
    <Container component="main" maxWidth="xs">
      <CssBaseline />
      <div className={classes.paper}>
        <Typography component="h1" variant="h5">
          Sign in
        </Typography>
        <form onSubmit={handleLogin}  className={classes.form} noValidate>
          <TextField
            variant="outlined"
            margin="normal"
            required
            fullWidth
            id="email"
            label="Email Address"
            name="email"
            autoComplete="email"
            autoFocus
          />
          <TextField
            variant="outlined"
            margin="normal"
            required
            fullWidth
            name="password"
            label="Password"
            type="password"
            id="password"
            autoComplete="current-password"
          />
          <FormControlLabel
            control={<Checkbox value="remember" color="primary" />}
            label="Remember me"
          />
          <Button
            type="submit"
            fullWidth
            variant="contained"
            color="primary"
            className={classes.submit}
          >
            Sign In
          </Button>
          <Grid container>
            <Grid item xs>
              <Link href="#" variant="body2">
                Forgot password?
              </Link>
            </Grid>
            <Grid item>
              <Link href="#" variant="body2">
                {"Don't have an account? Sign Up"}
              </Link>
            </Grid>
          </Grid>
        </form>
      </div>
      <Box mt={8}>
        <Copyright />
      </Box>
    </Container>
  );
}

Create SignUp.js in your scr folder.

   import React from 'react';
import Button from '@material-ui/core/Button';
import CssBaseline from '@material-ui/core/CssBaseline';
import TextField from '@material-ui/core/TextField';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '@material-ui/core/Checkbox';
import Grid from '@material-ui/core/Grid';
import Box from '@material-ui/core/Box';
import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
import Container from '@material-ui/core/Container';
import { Link } from 'react-router-dom'
function Copyright() {
  return (
    <Typography variant="body2" color="textSecondary" align="center">
      {'Copyright © '}
      <Link color="inherit" href="https://material-ui.com/">
        Your Website
      </Link>{' '}
      {new Date().getFullYear()}
      {'.'}
    </Typography>
  );
}
const useStyles = makeStyles((theme) => ({
  paper: {
    marginTop: theme.spacing(8),
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
  },
  avatar: {
    margin: theme.spacing(1),
    backgroundColor: theme.palette.secondary.main,
  },
  form: {
    width: '100%', // Fix IE 11 issue.
    marginTop: theme.spacing(3),
  },
  submit: {
    margin: theme.spacing(3, 0, 2),
  },
}));
export default function SignUp() {
  const classes = useStyles();

  return (
    <Container component="main" maxWidth="xs">
      <CssBaseline />
      <div className={classes.paper}>
        <Typography component="h1" variant="h5">
          Sign up
        </Typography>
        <form onSubmit={handleSignUp} className={classes.form} noValidate>
          <Grid container spacing={2}>
            <Grid item xs={12} sm={6}>
              <TextField
                autoComplete="fname"
                name="firstName"
                variant="outlined"
                required
                fullWidth
                id="firstName"
                label="First Name"
                autoFocus
              />
            </Grid>
            <Grid item xs={12} sm={6}>
              <TextField
                variant="outlined"
                required
                fullWidth
                id="lastName"
                label="Last Name"
                name="lastName"
                autoComplete="lname"
              />
            </Grid>
            <Grid item xs={12}>
              <TextField
                variant="outlined"
                required
                fullWidth
                id="email"
                label="Email Address"
                name="email"
                autoComplete="email"
              />
            </Grid>
            <Grid item xs={12}>
              <TextField
                variant="outlined"
                required
                fullWidth
                name="password"
                label="Password"
                type="password"
                id="password"
                autoComplete="current-password"
              />
            </Grid>
            <Grid item xs={12}>
              <FormControlLabel
                control={<Checkbox value="allowExtraEmails" color="primary" />}
                label="I want to receive inspiration, marketing promotions and updates via email."
              />
            </Grid>
          </Grid>
          <Button
            type="submit"
            fullWidth
            variant="contained"
            color="primary"
            className={classes.submit}
          >
            Sign Up
          </Button>
          <Grid container justify="flex-end">
            <Grid item>
              <Link to="/signin" variant="body2">
                Already have an account? Sign in
              </Link>
            </Grid>
          </Grid>
        </form>
      </div>
      <Box mt={5}>
        <Copyright />
      </Box>
    </Container>
  );
}

Create Home.js in your scr folder.

import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import IconButton from '@material-ui/core/IconButton';
import MenuIcon from '@material-ui/icons/Menu';
const useStyles = makeStyles((theme) => ({
  root: {
    flexGrow: 1,
  },
  menuButton: {
    marginRight: theme.spacing(2),
  },
  title: {
    flexGrow: 1,
  },
}));
export default function ButtonAppBar() {
  const classes = useStyles();
  return (
    <div className={classes.root}>
      <AppBar position="static">
        <Toolbar>
          <IconButton edge="start" className={classes.menuButton} color="inherit" aria-label="menu">
            <MenuIcon />
          </IconButton>
          <Typography variant="h6" className={classes.title}>
            News
          </Typography>
          <Button color="inherit">Login</Button>
        </Toolbar>
      </AppBar>
    </div>
  );
}

Signup in Firebase

In SignUp.js file make these changes:

import React, { useCallback} from "react";
import { Link } from 'react-router-dom'
import app from "./firebase.js";

And

export default function SignUp({ history }) {
  const classes = useStyles();
  const handleSignUp = useCallback(async event => {
    event.preventDefault();
    const { email, password } = event.target.elements;
    try {
      await app
        .auth()
        .createUserWithEmailAndPassword(email.value, password.value);
      history.push("/");
    } catch (error) {
      alert(error);
    }
  }, [history]);
...
...
...

This component is getting history objects from our routing context. When we click on that button our handleSignUp will fire. Inside this function, we get our event and call preventDefault() because we want to reload the page when the user clicks on the signup button. Then we get out email and password inputs from target.elements and we call createUserWithEmailAndPassword() from firebase API and we pass our email and password values: createUserWithEmailAndPassword(email.value, password.value). And then we pass our handleSignUp function to onSubmit callback of our form.

SignIn in Firebase

In the SignIn.js file, SignIn.js make these imports:

import React, { useCallback, useContext } from 'react'; // add {useCallback, useContext}
import { withRouter, Redirect } from "react-router";
import app from "./firebase.js";
import { AuthContext } from "./Authentication.js";

In the SignIn() function make these changes:

  • Add history
  • Add handleLogin method.
export default function SignIn({history}) {
const classes = useStyles();
const handleLogin = useCallback(
  async event => {
    event.preventDefault();
    const { email, password } = event.target.elements;
    try {
      await app
        .auth()
        .signInWithEmailAndPassword(email.value, password.value);
      history.push("/");
    } catch (error) {
      alert(error);
    }
  },
  [history]
);
const { currentUser } = useContext(AuthContext);
if (currentUser) {
  return <Redirect to="/" />;
}

There are two differences with the SignUp page. Here we use signInWithEmailAndPassword() and we use our authentication context const { currentUser } = useContext(AuthContext);. As you might have remembered we are tracking firebase users and we update our context with currentUser field using our auth model. And then we check: if we have currentUser we render the Redirect component from react-router. This component when rendered will just redirect to the path that uses a set in the to attribute.

SignOut in Firebase

Basically, in Home.js we call signOut() from auth module in our logout button.

onClick={() => app.auth().signOut()} 

Congratulations!!! Test your app.

npm start

Conclusion

Output

Our application is ready. Now you can Firebase Authentication in React application.

Related Article: How to Create React App with TypeScript?