How to Set Up Firebase Authentication with React

Written by pictureinthenoise | Published 2023/05/29
Tech Story Tags: firebase | firebase-auth | reactjs | authentication | react-tutorial | reactjs-development | react | tutorial

TLDRThis guide describes the steps to setup Firebase's Email/Password authentication service with a basic Login/Signup/Profile/Logout workflow on the front-end. In an effort to keep things simple, the guide only implements Firebase Authentication's Email/Password authentication service.via the TL;DR App

I noticed quite a few popular tutorials on how to set up Firebase Authentication with React web applications are a bit outdated...and sometimes a bit hard to follow.

In this guide, I wanted to offer what I hope will be an easy-to-follow tutorial on how to get started using Firebase Authentication with a modern React application. In an effort to keep things simple, the guide only implements Firebase Authentication's Email/Password authentication service.

As you may know, Firebase offers many authentication providers beyond basic Email/Password authentication. However, to reiterate, my goal here is to offer an easy-to-follow guide for you, especially if you may be just getting started with Firebase Authentication. So, an implementation of Email/Password authentication is a good place to begin.

In particular, this guide describes the steps to set up Firebase's Email/Password authentication service with a basic Login/Signup/Profile/Logout workflow on the front end. After completing the guide, you will be in a position to enhance the workflow with additional functionality such as allowing the user to reset his/her password.

Prerequisites

To complete this guide, you will need to have:

  • An existing Firebase account. If you don't have an existing Firebase account, you need to create one.
  • Installed Node.js v13.x or higher.
  • Installed Node Package Manager, npm.
  • An understanding of how to create a new React project using React v16.8 or higher.
  • Basic knowledge of React Hooks.
  • Basic knowledge of React Router v6.x or higher.
  • Basic knowledge of Bootstrap styles.

Step 1 - Setting Up a New Firebase Project

A Firebase project acts as a container for the Firebase services that you will use with your React web application. So, in this step, you will create a new Firebase project. To start, login to your Firebase account and click on Go to Console. Next, click on Add Project. This will kick off a simple workflow to add the new project.

  1. In the first step of the workflow, enter a new name for your project. This guide uses the name my-auth-test.
  2. The second step of the workflow offers you the option to enable Google Analytics for the project. Since Google Analytics are not necessary to implement the authentication service, you can turn this option off.
  3. Click on Create Project to create the new project.

You will need to wait for a few seconds as Firebase creates and provisions resources for the new project. When the project is ready, click Continue.

Step 2 - Configuring the New Firebase Project

After clicking Continue in Step 1, you should have been redirected to the Project Overview page for the new project. You now need to configure the new project to use Firebase and Firebase's Email/Password authentication service.

Step 2a - Adding Firebase

  1. Enter a nickname for your application in the Register app field. This guide uses the nickname my-react-app-with-auth.

  2. Click on Register App.

  3. Firebase will generate a set of configuration keys for your Firebase project under the header Add Firebase SDK. You will also see 2 options to display instructions on how to add the Firebase SDK using npm or using a <script> tag. Since you are building a React application via this guide, keep the default option Use npm selected.

  4. In the second text box under the Add Firebase SDK header, you will see the configuration code that you will need when you configure Firebase services in your React web application. It will be similar to:

// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: "AIzaSyDiUlY68W9Li_0EIkmdGdzD7nvqCT9kHnY",
  authDomain: "my-auth-test-fbd48.firebaseapp.com",
  projectId: "my-auth-test-fbd48",
  storageBucket: "my-auth-test-fbd48.appspot.com",
  messagingSenderId: "1078604952662",
  appId: "1:1078604952662:web:5d0b908439cfb5684ab7f7"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);

  1. Copy the code block displaying your application configuration and save it since you will use it later when building the React application.

To reiterate, your Firebase Project configuration keys will be different from the keys shown in the code block above. Ensure that you copy your Firebase Project configuration keys.

Step 2b - Adding Authentication

After you have copied the configuration code block in Step 2a, close the Add Firebase to your web app screen and you will be routed back to the Project Overview page.

  1. At the top of the page, you should now see 1 app displayed under your project name indicating that you have registered a new application with the project. Below that, you will see the header Choose a product to add to your app. Click on Authentication.

  2. When the Authentication Overview page loads, click on Get Started. You will be routed to the actual Authentication setup page for your project where you can choose which authentication services you would like to add. The Sign-in method tab should already be selected with a table of available authentication services.

  3. Since this guide describes the steps to configure your React web application for Firebase's Email/Password authentication service, click on Email/Password as displayed on the Sign-in method tab panel.

  4. On the next screen, click the toggle switch to the right of Email/Password to enable the Email/Password authentication service. Do not enable the Email link (passwordless sign-in) option. This authentication option requires a different implementation in React than the Email/Password authentication service and its implementation is not covered in this guide.

  5. Click Save. You will be routed back to the main Sign-in method tab panel. As you have now enabled an authentication service, the table of available authentication services will be replaced by a new table with two headers: Provider and Status. You should see Email/Provider listed under Provider and Enabled under Status.

You've successfully configured your new Firebase project with the Firebase Email/Password authentication service. Next, you will build the React application.

Step 3 - Creating a New React Project and Installing Packages

Step 3a - Creating a New React Project

Create a new React project using your desired application name. This guide uses my-react-app-with-auth.

npx create-react-app my-react-app-with-auth

Step 3b - Installing Packages

This guide requires the installation of 3 Node.js packages:

Install each of the 3 packages above via npm:

npm install firebase

npm install react-router-dom

npm install bootstrap

Step 4 - Creating an Instance of the Firebase Authentication Service

You will use the Firebase Authentication configuration keys that you copied in Step 2a to initialize an instance of the Firebase Authentication service in the React application.

  1. Create an empty firebase.js file in the src directory of the React application.

  2. Insert the following code using your configuration key values:

import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";

const firebaseConfig = {
    apiKey: "AIzaSyDiUlY68W9Li_0EIkmdGdzD7nvqCT9kHnY",
    authDomain: "my-auth-test-fbd48.firebaseapp.com",
    projectId: "my-auth-test-fbd48",
    storageBucket: "my-auth-test-fbd48.appspot.com",
    messagingSenderId: "1078604952662",
    appId: "1:1078604952662:web:5d0b908439cfb5684ab7f7"
}

const app = initializeApp(firebaseConfig);
const auth = getAuth(app);

export { auth }

  1. Save firebase.js.

This file initializes the Firebase Authentication service using your application configuration and provides an auth object that will be used when calling Firebase Authentication services from the React application.

As this guide is meant as more of a "just getting started" tutorial, and I am trying to keep things simple, I have elected to hardcode configuration key values in the firebase.js file above. However, this is not ideal - particularly for a production application. A better solution would be to create an environment variables file and put the actual configuration key values there.

Step 5 - Building the React Application

Step 5a - Overview of React Application Components

The React application will consist of components that offer a simple Login/Signup/Profile/Logout workflow as mentioned in the Introduction:

Login

  • The user's entry point into the application is the login form - i.e. it is the home route of the application.

  • The user can login using the form and get routed to his/her profile.

  • Alternatively, the user can click a signup link below the login form and get routed to the signup form.

Signup

  • The signup form is used to create a new account for the user.

  • Alternatively, the user can cancel the sign up and get routed back to the login form.

Profile and Logout

  • The user is routed to his/her profile when the login is successful.

  • The user can click a logout button on his/her profile to logout of his/her account.

Accordingly, you will need to build the appropriate components to support this workflow. The final src directory will contain the following files:

src
|__ index.js
|__ firebase.js // Created in Step 4.
|__ App.js
|__ Layout.jsx
|__ Login.jsx
|__ Profile.jsx
|__ Signup.jsx

You will build each of the required components in Step 5c through Step 5g. However, you first need to do a little "housecleaning" with the project template as described in the next step.

Step 5b - Cleaning Up the React Project Template

  1. To begin, you need to clean up the React project template. Delete the following files from your React project as this guide does not use them:

  • reportWebVitals.js

  • setupTests.js

  • logo.svg

  • index.css

  • App.css

  • App.test.js

  1. Clean up the template code in index.js so it reflects the following:

// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import "bootstrap/dist/css/bootstrap.min.css";

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

You will not need to make any further edits to index.js.

  1. Finally, you will be replacing most of the code in App.js. So, you can clean up the existing code to reflect an empty component skeleton that you will "fill in" shortly.

// App.js
const App = () => {
}

export default App

Step 5c - Building App.js

The App.js component will hold the JSX markup defining the overall structure of the application along with the application routes.

  1. Add the following code to the existing App.js file:

import Layout from "./Layout";
import Login from "./Login";
import Signup from "./Signup";
import Profile from "./Profile";
import { BrowserRouter, Routes, Route } from "react-router-dom";

const App = () => {
  return (
    <BrowserRouter>
      <Routes>
          <Route path = "/" element = { <Layout></Layout> }>
            <Route index element = { <Login></Login> }></Route>
            <Route path = "/signup" element = { <Signup></Signup> } ></Route>
            <Route path = "/profile" element = { <Profile></Profile> }></Route>
          </Route>
      </Routes>
    </BrowserRouter>
  )
}

export default App

  1. Save App.js.

Note that the Login component, which you will build in a moment, is specified as the home route of the application just as we want it to be per the workflow definition outlined in Step 5a.

Step 5d - Building Layout.jsx

The Layout.jsx component specifies the application markup that remains consistent across all routes. This guide uses a simple header message: React With Firebase Authentication. A "real" application might use branding and/or navigation bar markup in its Layout specification, for example. The component also includes an <Outlet /> tag for rendering the markup specified by the different routes.

  1. Create a new Layout.jsx file in the src directory.

  2. Add the following code:

import { Outlet } from "react-router-dom";

const Layout = () => {
    return(
        <div className = "container-fluid">
            <div className = "row justify-content-center mt-3">
                <div className = "col-md-4 text-center">
                    <p className = "lead">React With Firebase Authentication</p>
                </div>
                <Outlet />
            </div>
        </div>
    )
}

export default Layout

  1. Save the Layout.jsx file.

Step 5e - Building Login.jsx

To reiterate, the Login.jsx component is the home route for the application - i.e. this is the route that will display when navigating to localhost:3000. The component includes the form where the user can enter his/her e-mail and password to login to the application. If he/she doesn't have an existing account, he/she can click on a link below the form to get routed to the Signup component which will be built in the next step.

  1. Create a new Login.jsx file in the src directory.

  2. Add the following code:

import { useState } from "react";
import { auth } from "./firebase";
import { signInWithEmailAndPassword } from "firebase/auth";
import { Link, useNavigate } from "react-router-dom";

const Login = () => {
    const navigate = useNavigate();
    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");
    const [notice, setNotice] = useState("");

    const loginWithUsernameAndPassword = async (e) => {
        e.preventDefault();

        try {
            await signInWithEmailAndPassword(auth, email, password);
            navigate("./profile");
        } catch {
            setNotice("You entered a wrong username or password.");
        }
    }

    return(
        <div className = "container">
            <div className = "row justify-content-center">
                <form className = "col-md-4 mt-3 pt-3 pb-3">
                    { "" !== notice &&
                        <div className = "alert alert-warning" role = "alert">
                            { notice }    
                        </div>
                    }                  
                    <div className = "form-floating mb-3">
                        <input type = "email" className = "form-control" id = "exampleInputEmail1" aria-describedby = "emailHelp" placeholder = "[email protected]" value = { email } onChange = { (e) => setEmail(e.target.value) }></input>
                        <label htmlFor = "exampleInputEmail1" className = "form-label">Email address</label>
                    </div>
                    <div className = "form-floating mb-3">
                        <input type = "password" className = "form-control" id = "exampleInputPassword1" placeholder = "Password" value = { password } onChange = { (e) => setPassword(e.target.value) }></input>
                        <label htmlFor = "exampleInputPassword1" className = "form-label">Password</label>
                    </div>
                    <div className = "d-grid">
                        <button type = "submit" className = "btn btn-primary pt-3 pb-3" onClick = {(e) => loginWithUsernameAndPassword(e)}>Submit</button>
                    </div>
                    <div className = "mt-3 text-center">
                        <span>Need to sign up for an account? <Link to = "./signup">Click here.</Link></span>
                    </div>
                </form>
            </div>
        </div>
    )
}

export default Login

  1. Save Login.jsx.

Note that Login.jsx imports the auth Firebase authentication service object from firebase.js. The auth object is passed, along with the user's email and password input values, to the Firebase SDK's signInWithEmailAndPassword method to login the user. If the login fails, a notice is displayed to the user. React's useState hook is used to keep track of the user's e-mail and password input. Additionally, the react-router-dom useNavigate hook is used to programatically route the user to the Profile component if login is successful. Also, as seen in the last child <div> block within the <form> element, the user can click on a link to get routed to the Signup component to create an account if he/she does not have an existing account.

Step 5f - Building Signup.jsx

The Signup.jsx component presents the user with a form where he/she can sign up for a new application account.

  1. Create a new Signup.jsx file in the src directory.

  2. Add the following code:

import React, { useState } from "react";
import { auth } from "./firebase";
import { createUserWithEmailAndPassword } from "firebase/auth";
import { Link, useNavigate } from "react-router-dom";

const Signup = () => {
    const navigate = useNavigate();
    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");
    const [confirmPassword, setConfirmPassword] = useState("");
    const [notice, setNotice] = useState("");

    const signupWithUsernameAndPassword = async (e) => {
        e.preventDefault();

        if (password === confirmPassword) {
            try {
                await createUserWithEmailAndPassword(auth, email, password);
                navigate("/");
            } catch {
                setNotice("Sorry, something went wrong. Please try again.");
            }     
        } else {
            setNotice("Passwords don't match. Please try again.");
        }
    };

    return(
        <div className = "container">
            <div className = "row justify-content-center">
                <form className = "col-md-4 mt-3 pt-3 pb-3">
                    { "" !== notice &&
                        <div className = "alert alert-warning" role = "alert">
                            { notice }    
                        </div>
                    }
                    <div className = "form-floating mb-3">
                        <input id = "signupEmail" type = "email" className = "form-control" aria-describedby = "emailHelp" placeholder = "[email protected]" value = { email } onChange = { (e) => setEmail(e.target.value) }></input>
                        <label htmlFor = "signupEmail" className = "form-label">Enter an email address for your username</label>
                    </div>
                    <div className = "form-floating mb-3">
                        <input id = "signupPassword" type = "password" className = "form-control" placeholder = "Password" value = { password } onChange = { (e) => setPassword(e.target.value) }></input>
                        <label htmlFor = "signupPassword" className = "form-label">Password</label>
                    </div>
                    <div className = "form-floating mb-3">
                        <input id = "confirmPassword" type = "password" className = "form-control" placeholder = "Confirm Password" value = { confirmPassword } onChange = { (e) => setConfirmPassword(e.target.value) }></input>
                        <label htmlFor = "confirmPassword" className = "form-label">Confirm Password</label>
                    </div>                    
                    <div className = "d-grid">
                        <button type = "submit" className = "btn btn-primary pt-3 pb-3" onClick = {(e) => signupWithUsernameAndPassword(e)}>Signup</button>
                    </div>
                    <div className = "mt-3 text-center">
                        <span>Go back to login? <Link to = "/">Click here.</Link></span>
                    </div>                    
                </form>
            </div>
        </div>
    )
}

export default Signup

  1. Save Signup.jsx.

As with Login.jsx, Signup.jsx imports the auth Firebase authentication service object from firebase.js. The auth object is passed, along with the user's email and password input values, to the Firebase SDK's createUserWithEmailAndPassword method to create a new account for the user. If the sign up process fails, or if the user's password and confirmPassword values do not match, a notice is displayed to the user. Again, React's useState hook is used to keep track of user input, namely email, password, and confirmPassword input. And, the react-router-dom useNavigate hook is used again to programatically route the user to the Login component if the sign up process is successful. Also, as seen in the last child <div> block within the <form> element, the user can click on a link to get routed back to the Login component if he/she wants to cancel the sign up process.

Note that if the sign up process is successful, you can see the new user entry in your Firebase database by clicking on the Users tab under the Authentication section of your Firebase project.

Step 5g - Building Profile.jsx

The final component that you will build is Profile.jsx. Users are routed to this component upon successful login. The route welcomes the user with their login name (i.e. e-mail address) and provides a button to logout. Upon logout, the user is routed back to the Login component.

  1. Create a new Profile.jsx file in the src directory.

  2. Add the following code:

import { useNavigate } from "react-router-dom";
import { auth } from "./firebase";
import { signOut } from "firebase/auth";

const Profile = () => {
    const navigate = useNavigate();
    const user = auth.currentUser;

    const logoutUser = async (e) => {
        e.preventDefault();

        await signOut(auth);
        navigate("/");
    }

    return(
        <div className = "container">
            <div className = "row justify-content-center">
                <div className = "col-md-4 text-center">
                    <p>Welcome <em className = "text-decoration-underline">{ user.email }</em>. You are logged in!</p>
                    <div className = "d-grid gap-2">
                        <button type = "submit" className = "btn btn-primary pt-3 pb-3" onClick = {(e) => logoutUser(e)}>Logout</button>
                    </div>                
                </div>
            </div>
        </div>       
    )    
}

export default Profile

  1. Save Profile.jsx.

Note that Profile.jsx can access the user's details via the auth currentUser object. You can inspect the object to view the other user properties that are available, such as the user's UID. If the user elects to logout, the auth object is passed to the Firebase SDK's signOut method to sign out the user. At that point, the user is routed programatically back to the Login component using the react-router-dom useNavigate hook.

Step 6 - Testing the Application

  1. Start the React application:

npm start

  1. Navigate to locahost:3000 in your browser if your browser does not launch automatically. You should see the Login form.

  1. Click on the Click here link to navigate to the Signup route. You should see the Signup form.

  1. Create a new user and click the Signup button. If the sign up is successful, you will be routed back to the Login form.

  1. Enter the new user's credentials in the Login form and click the Submit button. If the login is successful, you will be routed to the user's Profile.

  1. Click the Logout button on the user's Profile to sign out. If the sign out is successful, you will be routed back to the Login form.

The steps above capture the workflow for the application.

Conclusion and Next Steps

I hope this tutorial has provided you with a good start on how you can add Firebase Authentication to your React applications. As mentioned earlier, you may wish to enhance the application workflow with additional functionality such as allowing the user to update his/her password. The Firebase SDK offers many methods to manage user accounts which you can use to extend the functionality described in this guide. You can learn more about Firebase and Firebase Authentication via the official documentation.


Written by pictureinthenoise | Computational linguistics. At the end of the beginning.
Published by HackerNoon on 2023/05/29