How to Build an Authentication system in React using Firebase

Moses Ogbopina
BitHubPH
Published in
15 min readFeb 23, 2021

What is firebase?

Firebase is a Backend as a Service provider, maintained by Google for creating mobile and web applications.
It is a platform that automates backend side development, by handling:
- Scalable databases
- Cloud code functions
- User authentication
- Social media Integration
- File storage
and so much more.

You can read more about firebase here.

The main focus of this article is setting up authentication with Firebase in a react project. In order to follow with the article, you'll need a sign in, sign up, profile and password reset page already set up in your react project.

Not to worry, if you don’t have a project already setup you can clone this repo here, with all the above pages already set up.

If you already have a project setup you can skip the next step and jump directly to the “setting up firebase” section.

Initializing the Project

Go to this repo, and click on the green code button, copy the URL, and head over to your terminal, and enter the following command.

git clone https://github.com/pena56/instagram-clone/tree/ft-user-authentication

This will clone the repo to your current working directory.

Next, we need to install the project dependencies, make sure you have node installed on your system. If don’t, you can download node here. Download the latest version on your preferred OS and follow the installation instructions to install it on your system.

Now that we have node setup, we can go ahead and install the project dependencies, first open your terminal on the project root directory and run the command.

npm install

This will install the project dependencies, and we can go ahead and run our development server with the command:

npm start

This will start the server on localhost:3000, and you should see the home page below.

home page

This is an instagram-clone project using react and firebase. And this article will be the first of many in explaining the process of building this clone.

There are four Pages already set up, a sign-in page, a sign-out page, a profile page, and a password reset page.

Now let’s jump into setting up firebase.

Setting up Firebase

In order to get started with firebase, you need to have a google account, if you don’t have one you can sign up here.

So head over to https://firebase.google.com, and click on the “Go to console” link on the top right. You will be taken to your console home page, where you will see a button to “Add project” and a list of all your projects if you have any.

We want to start a new project, so we will click on the “Add project” button to start one.

You will be asked to provide a name for your project, I'll call mine instagram-clone, since that’s what I’m working on, but feel free to call yours whatever you want, type in your name and click on the “continue” button.

You will be asked to set up Analytics for your firebase project. I’m going to leave this on but it has nothing to do with what we will be working on. Click on the “continue” button.

The next step is only available if you enabled analytics, we need to select an account for configuring analytics. Click on the dropdown and select “default account for firebase”, and finally click on the “Create project” button.

This will create your firebase project.

If you’ve made it this far, Congratulations. You have successfully created a Firebase project.

Click on the “continue” button to head over to your project dashboard.

We need to add an app to our project to get started. We want to add a web app to our project since that’s what we are building. Click on the “HTML tags” icon to add a web app.

You will be taken to a page to register your app.

First, provide a nickname for your app. I'll use my project name (instagram-clone) as the app nickname here. Next, you'll see a check box to set up hosting for the app, I'll leave this unchecked.

If you want to setup hosting on your application you can go ahead and check this, but this article will not cover the process of hosting on firebase.

Click on the “Register app” button

The next step is to add the Firebase SDK, but we will come back to this later on.

Click on the “Continue to console” button, to finalize the creation of the app.

Now we need to enable authentication in our app. To do this, click on the “Authentication” tab on the left, and you’ll be taken to the authentication page. Click on the “Get started” button.

You’ll be taken to a list of the sign-in methods available.

We want our users to be able to sign in with email and password. So we’ll click on the “Email/Password” option, and check the “enable” checkbox, and then click on the “save” button.

Now we have everything set up on firebase, time to head over to our react project.

To work with firebase in our project. we first need to install the Firebase SDK client library in our project. To do this go to your terminal and run the command.

npm install firebase

After firebase has been successfully installed, we can start writing code to implement our authentication system.

But first, we need to add the configuration of our firebase app to our react project, to do this we need our project configuration details on firebase.

So head over to your project dashboard and click on the settings icon on the top left, and then click on the “project settings” option.

Scroll down until you get to the “Firebase SDK snippet” section and click on the config option to reveal a firebaseConfig object, copy this object as it contains your project details.

We will store the information in a .env file as it contains information that should not be made public.

So in your project root directory create a new file called “.env.local” and the content of the file will be,

REACT_APP_FIREBASE_API_KEY = <your api key>
REACT_APP_FIREBASE_AUTH_DOMAIN = <your auth domain>
REACT_APP_FIREBASE_PROJECT_ID = <your project id>
REACT_APP_FIREBASE_STORAGE_BUCKET = <your storage bucket>
REACT_APP_FIREBASE_MESSAGING_SENDER_ID = <your messaging sender id>
REACT_APP_FIREBASE_APP_ID = <your app id>
REACT_APP_FIREBASE_MEASUREMENT_ID = <your measurement id>

Replace <your …. > with your corresponding project configuration you copied.

We will create a file in our adapters folder called “firebase.js”, and the content of this file will be the following,

import firebase from 'firebase';const firebaseConfig = {
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_FIREBASE_APP_ID,
measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID,
};
const myApp = firebase.initializeApp(firebaseConfig);export const auth = myApp.auth();

Let’s breakdown what’s going on in the above code,

First, we imported the firebase package we installed,

Next, we created an object that contains all of our firebase configuration details, this object is similar to what we copied from our firebase dashboard, but the difference here is that we are using our environment variables.

Next, we have a myApp variable, which initializes our application using our configuration settings, we will use this to create instances of the various functionalities of firebase we want to incorporate into our application. So since we want to use the firebase authentication feature, we will need to create an instance of firebase auth method.

Finally, we create and export our auth instance as a variable called auth.

We will be using React’s Context API, so we can have access to our user object across our entire application, if you don’t know how to use react context API, here’s a blog post that explains how to use react context API.

Create a file called “AuthContext.js” in your context folder, the content of the file will be as follows,

import { useContext, useState, useEffect, createContext } from 'react';import { auth } from '../adapters/firebase';const AuthContext = createContext();export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
const signup = (email, password, fullName) => {
let promise = new Promise(function (resolve, reject) {
auth
.createUserWithEmailAndPassword(email, password)
.then((ref) => {
ref.user.updateProfile({
displayName: fullName,
});
resolve(ref);
})
.catch((error) => reject(error));
});
return promise;
};
const signin = (email, password) => {
let promise = new Promise(function (resolve, reject) {
auth
.signInWithEmailAndPassword(email, password)
.then((ref) => {
resolve(ref);
})
.catch((error) => {
reject(error);
});
});
return promise;
};
const signout = () => {
return auth.signOut();
};
const passwordReset = (email) => {
let promise = new Promise(function (resolve, reject) {
auth
.sendPasswordResetEmail(email)
.then(() => {
resolve(`Password Reset Email sent to ${email}`);
})
.catch((error) => {
reject(error);
});
});
return promise;
};
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, [currentUser]);
const value = {
currentUser,
signup,
signin,
signout,
passwordReset
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
}

Let’s breakdown the code above.

First, we import the hooks we’ll need from react in order to create our context.

Next, we import our auth instance from the firebase file we created.

Then we create our context using the “createContext” hook and call it “AuthContext”.

Next, we create a function called “useAuth” that returns “useContext” with our “AuthContext” passed in as an argument, this is the function we will use to access the values of this context.

Then we create a component called “AuthProvider”, and it takes “children” as props, these children will be the various components in our application that will make use of this context.

Next, we create a “currentUser” state that will be used to store our authenticated user. we also have a loading state which we will use later on to display a loader while getting the authenticated user.

Now to our function responsible for signing up new users, this function takes an “email”, a “password”, and a “fullName” as arguments since these are the values we want to sign up new users with.

Next, we create a new Promise object

The reason I opted for a Promise is so we can easily get access to whatever is returned to us wherever we call this function.

Inside of the promise object, we call our auth instance from our firebase file which has a “createUserWithEmailAndPassword” method, this takes an email and password as arguments to create a new user object in firebase. This, if successful returns a promise with a reference to the user instance. We take this reference in our “.then” block and call the user object of this reference which has a “updateProfile” method, this takes an object as an argument. Now the “updateProfile” is only used to update the “displayName” and “photoURL” properties of the user object, but since we are only updating the “displayName”, that’s what we have to pass in as an argument. Now our Promise object will return the reference of the user if the user was created successfully or will return an error if the operation was unsuccessful.

The next function is to sign in users, and it follows the same pattern as our signup function, this function takes an “email ”and a “password”, and we create a new promise object which calls our auth instance and the “signInWithEmailAndPasword” method, this method takes an email and a password as arguments and returns the user reference when successful or returns an error if the operation fails.

Next, we have a “signout ”function that calls the “signOut” method from our auth instance.

The next method is for “passwordReset”, which takes an “email ”as an argument and uses the “sendPasswordResetEmail” method from our auth instance. This method sends an email to the email address provided.

Next, is the “useEffect” hook, where we call the “onAuthStateChanged” method, which sets an observer on the auth instance to get the current user. This user is then assigned to our “currentUser ”state.

Next, we have a value object to expose all the values we want accessible through this context, here we are exposing all our functions and the currentUser.

Next, we return AuthContext Provider, which will only render the children components when loading is false.

Now, we need to wrap our application with this context, so let’s head over to our “App” component and first import “AuthProvider”

import { AuthProvider } from './contexts/AuthContext'

then we wrap the “Switch ” component with AuthProvider as follows:

<AuthProvider>
<Switch>
<Route path="/" exact component={Profile} />
<Route path="/accounts/emailsignup" component={SignUp} />
<Route path="/accounts/password/reset" component={ResetPassword} />
<Route path="/accounts/signin" component={SignIn} />
</Switch>
</AuthProvider>

Now we have access to every value from our auth context available throughout our application.

Head over to the “SignupForm ”component, so we can add the sign-up functionality.

First, we need to import the following,

import { useRef, useState } from 'react';
import { useHistory } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';

We first import “useRef ”and “useState ”hooks from react, because we need to create a reference to our input fields and also so we can use state here. Next, we import the “useHistory ”hook from react-router-dom, so we can redirect our user after successfully signing up, and finally “useAuth ”so we can use the values from our AuthContext.

Inside of our component, before the return statement, we have the following,

const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const emailRef = useRef();
const fullNameRef = useRef();
const passwordRef = useRef();
const { signup } = useAuth();const history = useHistory();

In the first line, we have a loading state with an initial value of false, this state will be used to show a loader when our form is been processed. Our next state is an error state which has an empty string set as its initial value, this state will be used to store any error message we might encounter.

Next, we have three references that correspond to our input fields in the form, as we will use this to get whatever value that’s stored in those input fields.

In the next line, we call our “useAuth” method and then use object destructuring to get the “signup ”method we created in our AuthContext.

And finally, we have the “useHistory” hook assigned to a history variable.

Now let’s create a method to handle sign up, directly underneath the history variable, we’ll have the following function

const handleSignup = (e) => {
e.preventDefault();
setError('');
setLoading(true);
const email = emailRef.current.value;
const password = passwordRef.current.value;
const fullName = fullNameRef.current.value;
signup(email, password, fullName)
.then((ref) => {
setLoading(false);
history.push('/');
})
.catch((err) => {
setError(err.message);
setLoading(false);
});
};

We have a function called “handleSignup”, and this function takes an event “e” as an argument. In the body of the function, we have an “e.preventDefault” method, this prevent’s the default behavior of the form as we do not want to refresh the page upon submission of the form. Then we set the value of our error state to an empty string, the reason we do this is so we will clear all existing errors if there are any. Next, we set the value of the loading state to true, as we are in the process of processing the form. Next, we have three variables each corresponding to the value of the inputs reference. Then we call our signup function, which takes the “email”, “password” and “fullName” as arguments. If the operation was successful, then we set the value of our loading state to “false”, and then use our history variable to redirect the user back to the home page. If the operation was unsuccessful, we get an error object and set the value of the error state to the error message, and then set the value of the loading state to false.

Now it’s time to add this function to the “onSubmit ”event handler of our form, add the following as props to the form component,

onSubmit={(e) => handleSignup(e)}

The onSubmit handler takes an e argument and passes that to the “handleSignup” function.

We also need to add our references to their corresponding input fields, add the reference values as props to their corresponding fields,

{/* For Email input field */}
inputRef={emailRef}
{/* For Fullname input field */}
inputRef={fullNameRef}
{/* For Password input field */}
inputRef={passwordRef}

If you are following along with your own Form, use the “ref” prop of the input field instead.

On our Button component, we need to pass the “disabled” prop with its value set to loading, and the type of the button set to “submit” as follows,

disabled={loading}
type="submit"

Finally, we need to display our error message if there’s any, so directly underneath our button component, paste the following code,

{error && <AuthError>{error}</AuthError>}

We are using the logical “and” (&&) operator, hence the Error message will only be displayed if there’s a value in the error state.

And that is all for our signup form, it’s time to try it out. Make sure your server is running and navigate to the signup page on your browser.

Sign up page

With our sign-up page working, it’s time to work on our sign-in page, The sign-in page follows the same pattern as the sign-up page, so let’s jump right in.

Head over to the “SigninForm” component and import “useState”, “useRef” from react, and also “useHistory” from react-router-dom, and also “useAuth” from our AuthContext.

Next, we’ll need a “loading” state and an “error” state. We also will have a reference for “email ” and “password”, and a “history ” variable using the useHistory hook. And finally, we get the “signin” method from AuthContext.

Next, we have a function to “handleSignin”, and the content of the function is as follows,

const handleSignin = (e) => {
e.preventDefault();
setError('');
setLoading(true);
const email = emailRef.current.value;
const password = passwordRef.current.value;
signin(email, password)
.then((ref) => {
setLoading(false);
history.push('/');
})
.catch((error) => {
setError(error.message);
setLoading(false);
});
};

This function works the same as the “handlesignup” function above. The sign-in function takes an email, and a password as arguments, if the sign-in process was successful, the user is redirected to the profile page.

We now need to add the “onSubmit” event handler to our form as follows,

onSubmit={(e) => handleSignin(e)}

Also, don’t forget to add the input references and the type and disabled props for the button.

And finally, we have our error message underneath.

Now let’s go to our browser and try the sign-in page out.

Sign in page

Now that we have our sign-up and sign-in pages working, it time to work on the password reset page. The password reset page follows the same pattern as the sign-up and sign-in page.

First off import “useState”, “useRef”, and “useAuth”.

Inside of the function, we are going to be working with three states, we’ll have an error state, a loading state, and a message state to show a success message when the reset email has been sent successfully. We will also need to get passwordReset from “useAuth” and also create a reference with “useRef” for the email input field.

Next, we have a function to handle password reset as follows,

const handlePasswordReset = (e) => {
e.preventDefault();
setLoading(true);
setError('');
setMessage('');
const email = emailRef.current.value;
passwordReset(email)
.then((msg) => {
setMessage(msg);
setLoading(false);
})
.catch((error) => {
setError(error.message);
setLoading(false);
});
};

This function is very similar to the sign-up or sign-in function, the only difference is that we set the success message as the value of the message when the operation is successful, else we set the message to the error state.

We now need to add the above function to the onSubmit handler of the form as follows,

onSubmit={(e) => handlePasswordReset(e)}

Don’t also forget to add the email input reference to the input field. And also the disabled prop of the Button set to “loading”.

Finally, underneath the button, make sure to include the error message and the message as follows.

{error && <AuthError>{error}</AuthError>}
{message && <AuthSuccess>{message}</AuthSuccess>}

Now we can go ahead and try the form out.

Password reset page

We are almost done, just one more feature left to implement. The signout feature,

The sign-out feature is a lot less complicated than the others, all we need here is the signout function from the AuthContext.

So first go to the Header Component (or wherever your signout/logout link is located) and import useAuth as follows

import { useAuth } from '../contexts/AuthContext';

We also need the “useHistory” hook from react-router-dom so we can redirect the user upon signing out.

Don’t forget to create a history variable assigned to the “useHistory” hook, and also destructure the signout function from useAuth. Next, we need a function to handle signing out and the function is as follows,

const handleSignout = () => {
signout();
setShowDrawer((prev) => !prev);
history.push('/accounts/signin');
};

This function calls the signout method, and this takes care of signing out the user. The next line toggles the dropdown menu and the last line redirects the user back to the sign-in page.

And that is it, let’s try it out. If you don’t have a user already logged in, log in a user and click on the profile icon on the header and click on “logout”. This will log out the signed-in user and also redirect to the sign-in page.

As a final touch, let’s had some permission to the profile page, so only an authenticated user will be able to view it.

To do that we first head over to the Profile page, then we import the Redirect component in react-router-dom as follows

import { Redirect } from 'react-router-dom';

We also need to import “useAuth”, as we will need the value of the currentUser.

import { useAuth } from '../contexts/AuthContext';

Inside the Profile function, we destructure “currentUser” from “useAuth” and in the return value we’ll have this.

<>
{currentUser ? (
<ProfileContainer>
<Header />
<ProfileBanner />
<ProfilePost />
</ProfileContainer>
) : (
<Redirect to="/accounts/signin" />
)}
</>

We are using a ternary operator here, where if the value of the currentUser is not null, the contents of this page will be rendered, else if the currentUser is null, then you’ll be redirected to the sign-in page.

Now we have a functional user authentication system in react using firebase,

Complete code can be found here.

--

--

Moses Ogbopina
BitHubPH

Python Developer | JavaScript Enthusiast | Certified Otaku