React authentication tutorial with Firebase V9 and Firestore
Tags: react, javascript, firebase
Published: 2022-05-29 11:13:48

In this tutorial we are going to understand how to use Firebase V9 to both setting up the authentication for your application and use the Firestore database to manage additional information about the users.

First things first, if you are reading this, you probably know what Firebase is. For those who does not, Firebase is a **Backend-as-a-service** platform that provides several tools to developers, like authentication, database, storage, hosting, test lab, notification, among others. It is maintained by Google and it is a very useful platform where you can develop scalable projects.

Now that we already know what Firebase is, let's start our React application. In order to do that we will use the create react app boilerplate. So, move to the folder you want and type the following in your terminal
```
npx create-react-app authentication
```
Once the creation is finished, go to the project folder an type
```
npm start
```
which, after running, is going to show you the famous React first page in your browser.

![React first page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c4w1ghtvf1udhft6ia64.png)

## Planning

Okay! Now, let's talk a little about what we are going to create. I always like to plan every project and I suggest every reader to do the same. I encourage you to do that because I think it makes you more focused on what you really have to do. We can always code some components out of the blue, but if you are not focused on what you are doing, it is easy to waste a lot of time. Well, since authentication is the main purpose of this small project, it is a good idea to think about 3 different views:

1. Login view. We can assume that this is the first page of our app, when people arrive after type the url in the browser. This is going to be the view where the user can type your credentials to possibly access the home page of the application. As credentials we can consider the e-mail and password. So, this view will have a form with both e-mail and password inputs. After filling up both inputs, if the user is registered in the application he will be authorized to go to the home page. Otherwise, he cannot go further.
2. Register view. Well, since we are going to admit only registered users to go to the home page, we need to create a view where someone can create his own credentials to access the application. Again, since we are considering e-mail and password as credentials, this view is going to have a form with the desired e-mail and password the user wants to register himself.
3. Finally, we have the home page. This is going to be a view where only authorized users can access after his credentials are accepted by our application. So, let's suppose the home page is going to have a custom welcome message with the user's e-mail and the date when he registered into the application for the very first time.

![Structure of the views](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n92gosjbjj0c6wem9ylg.png)

I think this is a good starting point. This is not a very fancy application, so we do not have much different components to deal with and, that's why I'm not going to create a big component tree to our application.

![App structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/57m8n0bskcgluekxz29r.png)

This image could be a good app structure if you want to create a Form component and a Message Component. I am not going to do it, because I want to keep things simple.

1. The component root of the project is going to be the App component. This component is going to manage the routes of the application. So, it will be responsible to throw the user to the Login page, Register page or Home page.
2. Also, I am not going to create a big style for the application, since this is not the focus of this project.

## Login Page

We start with the **Login Page**. As I said earlier, the **Login Page** will just contain a form with two inputs, one to the e-mail and another to the password. In order to do that, we create a new folder into the *src* that I will call *views* and inside of it create the folder *Login* with the files *index.jsx* and *Login.jsx* according to the following image

![Views and login folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qfhzo8zhqrnyzn8oqfow.png)

Inside *index.jsx* file we just export the default component from the *Login.jsx* file.

### index.jsx
```react
export { default } from './Login';
```
and inside *Login.jsx* we create the Login form.

### Login.jsx
```react
import React, { useState } from 'react';

const Login = () => {

  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleEmail = event => {
    setEmail(event.target.value);
  };

  const handlePassword = event => {
    setPassword(event.target.value);
  };

  return (
    <div style={{ textAlign: 'center' }}>
      <div>
        <h3>Login</h3>
      </div>
      <div>
        <input
          value={email}
          onChange={handleEmail}
          placeholder="Type your e-mail"
        />
      </div>
      <div>
        <input
          type="password"
          value={password}
          onChange={handlePassword}
          placeholder="Type your password"
        />
      </div>
      <button>
        Submit
      </button>
      <div style={{ fontSize: '12px' }}>
          Dont't have an account?
          {' '}
          Register <span style={{ color: '#293462', fontWeight: 'bold' }}>here</span>
      </div>
    </div>
  );
};

export default Login;
```
Basically we create a form with a title where we wrote 'Login' and two inputs to deal with the e-mail and password followed by a submit button which, in the future will carry the function to send the user information to be validated. In the end we put a simple text so, if the user is not registered, he will be able to go to the **Register Page**. We have used the React hooks to create the states `email` and `password` and inside the input we use the `onChange` event handler with both `handleEmail` and `handlePassword` function to the e-mail and password inputs respectively.

**Remark:** I have used inline css in order to create a very simple style to the component. I will repeat some of these in the future. As I mentioned earlier, the focus here is not the style of the application but the logic itself. I strongly recommend you **not** to use css inline as I am doing here but instead use *css modules* or *styled components*, for example.

## Register Page

After that, we create a new folder inside the *views* called *Register* with the files *index.jsx* and *Register.jsx*. These files will be almost exactly the same from those from **Login Page** as we can see below.

### index.jsx
```react
export { default } from './Register';
```

### Register.jsx
```react
import React, { useState } from 'react';

const Register = () => {

  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleEmail = event => {
    setEmail(event.target.value);
  };

  const handlePassword = event => {
    setPassword(event.target.value);
  };

  return (
    <div style={{ textAlign: 'center' }}>
      <div>
        <h3>Register</h3>
      </div>
      <div>
        <input
          value={email}
          onChange={handleEmail}
          placeholder="Type your e-mail"
        />
      </div>
      <div>
        <input
          type="password"
          value={password}
          onChange={handlePassword}
          placeholder="Type your password"
        />
      </div>
      <button>
        Submit
      </button>
      <div style={{ fontSize: '12px' }}>
          Already have an account?
          {' '}
          Please <span style={{ color: '#293462', fontWeight: 'bold' }}>sign in</span>
      </div>
    </div>
  );
};

export default Register;
```
The only difference, for now, between *Register.jsx* and *Login.jsx* is the title and the message in the end. In Register component, we put the message to the user sign in if he already has an account.

## The Home Page

The **Home Page** is the simplest among the three pages. We start doing de same by creating a new folder named *Home* inside *views* with the files *index.jsx* and *Home.jsx*.

![Adding home folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vdg6d9flfvvdaz4wtx24.png)

The *index.jsx* will be similar to previous ones.

### index.jsx
```react
export { default } from './Home';
```
The *Home.jsx* will be super easy. Initially we just create a welcome message to the user. After including the authentication, we can improve it.

### Home.jsx
```react
import React from 'react';

const Home = () => {
  return (
    <div style={{ textAlign: 'center' }}>
      <h1>Welcome user!</h1>
      <div>
        If you are here, you are allowed to it!
      </div>
    </div>
  );
};

export default Home;
```

## Creating the routes for the pages

Now, the **Login Page**, **Register Page** and **Home Page** are created, but if you move to your browser you will not see those pages. That's because the application is still rendering what is inside the **App** component and we do not change anything there. Well, let's change this. Since the **App** component will be responsible to manage which page to be rendered, we now need the **React Router** library to create the specific routes. First, we need to install the `react-router-dom` library. So, go to your terminal and type
```
npm i react-router-dom
```
After the installation is completed, move to the **App** and change the entire code of it by the following

### App.js
```react
import {
  BrowserRouter as Router,
  Routes,
  Route,
} from "react-router-dom";

import Home from './views/Home';
import Login from './views/Login';
import Register from './views/Register';

function App() {
  return (
    <Router>
      <Routes>
        <Route path='/' element={<Login />} />
        <Route path='/register' element={<Register />} />
        <Route path='/home' element={<Home />} />
      </Routes>
    </Router>
  );
}

export default App;
```

All right! What have we done? Well, actually it's not difficult. The `react-router-dom` library gives us, out of the blue, the hability to manage routes and, that way, the application know which component must render. In order to to that, we import `BrowserRouter` as `Router`, `Routes` and `Route` from the library.

We can understand the `Router` as a container that wraps the whole application and allows us to use routes, then we import all the views we created before and for each of it we create an specific `Route` inside `Routes` passing as `props` the path of the route and the element that should be rendered. In this case, we are passing the route '/' to the **Login page**, '/register' to the **Register page** and '/home' to the **Home page**.

Now, if you move to the browser, you will se the **Login page**, because the localhost url is the route '/', so the application is rendering the **Login page**.

![Login Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lc4znvry1p4pfz01ffpl.png)

Now, changing the url in the browser adding '/register' in the end will take us to the **Register page**

![Register Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8jp7ds9w3ivgsh8c1w2j.png)

and, changing it to '/home' will take us to the **Home page**

![Home Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xzqzo9apvf3xiutyqv25.png)

Now, almost everything is fine but the links to change from the **Login** page to the **Register** page are still not working. Well, how could we make it work? In this case, we will need to use the `useNavigate` hook provided by the `react-router-dom` library. Its use is quite similar with the previous hook `useHistory`, which is not available anymore in React Router v6. We just need to import the `useNavigate` hook from the `react-router-dom`
```react
import { useNavigate } from 'react-router-dom
```
call it inside the respective component
```react
const navigate = useNavigate();
```
and use it  in the span element with the `onClick` prop.
**Remark:** I also included the pointer cursor in the styles of the span tag so the mouse cursor is going to show a hand when it passes on the text, which shows the user that the text is clickable.

```react
<span 
  onClick={() => navigate('/')}
  style={{ color: '#293462', fontWeight: 'bold', cursor: 'pointer' }}
>
  sign in
</span>
```

Making these changes to the **Login** and **Register** pages, this is the new code of them.

### Login.jsx
```react
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom'

const Login = () => {

  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const navigate = useNavigate();

  const handleEmail = event => {
    setEmail(event.target.value);
  };

  const handlePassword = event => {
    setPassword(event.target.value);
  };

  return (
    <div style={{ textAlign: 'center' }}>
      <div>
        <h3>Login</h3>
      </div>
      <div>
        <input
          value={email}
          onChange={handleEmail}
          placeholder="Type your e-mail"
        />
      </div>
      <div>
        <input
          type="password"
          value={password}
          onChange={handlePassword}
          placeholder="Type your password"
        />
      </div>
      <button>
        Submit
      </button>
      <div style={{ fontSize: '12px' }}>
          Dont't have an account? Register {' '}
          <span 
            onClick={() => navigate('/register')}
            style={{ color: '#293462', fontWeight: 'bold', cursor: 'pointer' }}
          >
            here
          </span>
      </div>
    </div>
  );
};

export default Login;
```

### Register.jsx
```react
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';

const Register = () => {

  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const navigate = useNavigate();

  const handleEmail = event => {
    setEmail(event.target.value);
  };

  const handlePassword = event => {
    setPassword(event.target.value);
  };

  return (
    <div style={{ textAlign: 'center' }}>
      <div>
        <h3>Register</h3>
      </div>
      <div>
        <input
          value={email}
          onChange={handleEmail}
          placeholder="Type your e-mail"
        />
      </div>
      <div>
        <input
          type="password"
          value={password}
          onChange={handlePassword}
          placeholder="Type your password"
        />
      </div>
      <button>
        Submit
      </button>
      <div style={{ fontSize: '12px' }}>
          Already have an account? Please {' '}
          <span 
            onClick={() => navigate('/')}
            style={{ color: '#293462', fontWeight: 'bold', cursor: 'pointer' }}
          >
            sign in
          </span>
      </div>
    </div>
  );
};

export default Register;
```
and after all that we can now click on the span elements to be redirected to the specific pages.

![Preview of the application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2por3zjqrlottbubcs8h.gif)

Now, there is one thing missing. We can only access the Home Page by typing the corresponding route into the url. Of course, that is not what we want. In the end, after the user is signed in, we want the application to redirect him to the **Home page**. Someone clever could say that it would be enough to use the `useNavigate` hook in the login page again associated with the submit button. Something like this
```react
const handleSubmit = (event) => {
  navigate('/home');
};
.
.
.
<button onClick={handleSubmit}>
  Submit
</button>
```
Well, that will work, but that creates a bitter feeling that both e-mail and password are worthless, right? Our application is receiving these inputs from the user and doing absolutely nothing with it. Actually, with this actual approach, the user does not need to fill out his e-mail and password to access the **Home page**.

![Unauthenticated access](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uvmq70aeyp2oo53qs4eh.gif)

And this is not what we want. As we said before, the **Home page** should only be accessed by an authenticated user. In the end, the `handleSubmit` function of the **Login page** needs to check if the user is already registered, and, if so, allows the access to the **Home page**. And that's what we are going to do in the next section.

## Firebase Authentication and Firestore database

After we finally prepare our application, now we need to deal with the user authentication. As I said earlier, we will use the Google Firebase to do that. So, move to *https://firebase.google.com/* in your browser. That's the page you will see

![Firebase first page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yjxlqs5as2a4hzpye8ho.png)

Now, click the console button in the top right corner of the page (you are going to need a Google account) and Firebase will redirect you to a page where all your projects will be available to be chosen. In that page, we click to add a new project. Then we have three simple steps:
1. Name the project. I am naming it as Authentication
2. Choose if you want Google Analytics or not. I am going to say yes;
3. Choose the Firebase account to the Google Analytics. I'm choosing the default one;

![Firebase configuration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i5d2l9r6ddvf4hqphlyt.gif)

After that, your project will be created in Firebase. In the project console, we are going to choose both Authentication and Firestore.

![Firebase authentication and firestore cards](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wm16biiv19rpnfkck6wn.png)

First, we click in the Authentication card and after redirection, click in Start, then in e-mail and password authentication and then activate it with the respective toggle. After that, click Save.

![authentication configuration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ycpync2zz4iwjp4wao97.gif)

Then, select the Firestore card, click in Create Database, choose if the database will be run in production mode or test mode and select the local of your cloud Firestore and click Activate

![Firestore configuration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/efvq3s5c4o8isd23ltag.gif)

After that, we move to the home page of the project to register it.

![Firebase registration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2geg5w02kyzysqf9erxo.gif)

We are almost there. Now move to the settings of the project, the second icon in the left bar, scroll down and you will find some important keys that you will have to import in your React application. Click to copy all this code.

![Firebase configuration keys](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t3rbt2fg7vyilnl1ewz1.png)

Before coming back to the code, let's go to the terminal and install `firebase` as a dependency to our project
```
npm install firebase
```
Once it is finished, let's come back to our code. Inside the `src` folder, we create a folder called `configs` and inside of it, create a file called `firebase.js`

![Creating firebase file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cofi34fd8vsxc6xa8tn8.png)

We now are going to paste the Firebase configuration inside of this file and make some changes.

```react
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
  apiKey: 'your apiKey here',
  authDomain: 'your authDomain here',
  projectId: 'your projectId here',
  storageBucket: 'your storageBucket here',
  messagingSenderId: 'your messagingSenderId here',
  appId: 'your appId here',
  measurementId: 'your measurementId here',
};
  
export const firebaseApp = initializeApp(firebaseConfig); // initialize app
export const db = getFirestore(); // this gets the firestore database
```
As you can see, in the code above, inside each field of the object `firebaseConfig` you put all your firebase codes.

**Attention:** If you intend to use git as a version control to your code and make it public so everyone can access it in your github, for example, it is not a good idea to simply paste your firebase code inside this file, because everyone could access your firebase API's. So, if you want to keep your keys protected, it's a good idea to create a `.env` file in the root of your project, paste these important keys there, include the `.env` file in your `gitignore` file and call the keys as React environment variables inside `firebase.js` file.

![Creating .env file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jlfpyusdkx1hgywfv4hm.png)

The elements in the `.env` file should not need the `' '` and you don't need to put comma or semicolon in the end of each line

### .env structure example
```
REACT_APP_API_KEY=AIzaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
REACT_APP_AUTH_DOMAIN=authentication-XXXXX.aaaaaaaaaaaaa
```

![.env file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bxuwytreq15uzabrcscy.png)

**Remark:** Do not forget to include your `.env` file into your `gitignore` file.

Now that you have done that, move back to the `firebase.js` and change the firebase keys using the environment variables.

### firebase.js
```react
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
  apiKey: `${process.env.REACT_APP_API_KEY}`,
  authDomain: `${process.env.REACT_APP_AUTH_DOMAIN}`,
  projectId: `${process.env.REACT_APP_PROJECT_ID}`,
  storageBucket: `${process.env.REACT_APP_STORAGE_BUCKET}`,
  messagingSenderId: `${process.env.REACT_APP_MESSAGING_SENDER_ID}`,
  appId: `${process.env.REACT_APP_APP_ID}`,
  measurementId: `${process.env.REACT_APP_MEASUREMENT_ID}`,
};
  
export const firebaseApp = initializeApp(firebaseConfig); // initialize app
export const db = getFirestore(); // this gets the firestore database
```

Now, remember that we need to do two different things: register a new user and sign in a user. If we go to Firebase authentication [documentation](https://firebase.google.com/docs/auth/web/password-auth) we can find two different functions available from Firebase authentication:
1. `createUserWithEmailAndPassword` that receives the parameters `auth`, `email` and `password`
2. `signinWithEmailAndPassword` that receives the same three parameters

We will use the first one to register a new user and the second to sign the user in the application. So, let's change the `firebase.js` file including these functions.

### firebase.js
```react
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";

import {
  getAuth,
  createUserWithEmailAndPassword,
  signInWithEmailAndPassword,
} from 'firebase/auth';

const firebaseConfig = {
  apiKey: `${process.env.REACT_APP_API_KEY}`,
  authDomain: `${process.env.REACT_APP_AUTH_DOMAIN}`,
  projectId: `${process.env.REACT_APP_PROJECT_ID}`,
  storageBucket: `${process.env.REACT_APP_STORAGE_BUCKET}`,
  messagingSenderId: `${process.env.REACT_APP_MESSAGING_SENDER_ID}`,
  appId: `${process.env.REACT_APP_APP_ID}`,
  measurementId: `${process.env.REACT_APP_MEASUREMENT_ID}`,
};
  
export const firebaseApp = initializeApp(firebaseConfig); // initialize app
export const db = getFirestore(); // this gets the firestore database

//### REGISTER USER WITH FIREBASE AUTHENTICATION ###//
export const registerUser = (email, password) => {
  const auth = getAuth();
  return createUserWithEmailAndPassword(auth, email, password);
};

//### LOGIN USER WITH FIREBASE ###//
export const loginUser = (email, password) => {
  const auth = getAuth();
  return signInWithEmailAndPassword(auth, email, password);
};
```

We just import the functions `getAuth`, `createUserWithEmailAndPassword` and `signInWithEmailAndPassword` from `firebase/auth` and we create the functions `registerUser` and `loginUser` to be imported in the respective components.

First, we move to the **Register page** import the `registerUser` function 
```react
import { registerUser } from '../../configs/firebase';
```
from `firebase.js` and create the `handleRegister` function.
```react
const handleRegister = () => {
    registerUser(email, password)
      .then((userCredential) => {
        alert('User created successfully!')
      })
      .catch((error) => {
        alert('Something went wrong!')
        const errorCode = error.code;
        console.log(errorCode);
      });
  }
```

This function uses the `createUserWithEmailAndPassword` that was originally exported from `firebase.js`. It is important to notice that this function returns a promise, so if it resolves positively, we are just using the native alert to show the message that the user was created successfully and, otherwise, we send a message that something went wrong. I strongly recommend you to create a specific alert component to show the message to the user, but we are not doing it here. To finish, we have to call this `handleRegister` into the submit button by calling it on the `onClick` props.

### Register.jsx
```react
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { registerUser } from '../../configs/firebase';

const Register = () => {

  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const navigate = useNavigate();

  const handleEmail = event => {
    setEmail(event.target.value);
  };

  const handlePassword = event => {
    setPassword(event.target.value);
  };

  const handleRegister = () => {
    registerUser(email, password)
      .then((userCredential) => {
        alert('User created successfully!')
      })
      .catch((error) => {
        alert('Something went wrong!')
        const errorCode = error.code;
        console.log(errorCode);
      });
  }

  return (
    <div style={{ textAlign: 'center' }}>
      <div>
        <h3>Register</h3>
      </div>
      <div>
        <input
          value={email}
          onChange={handleEmail}
          placeholder="Type your e-mail"
        />
      </div>
      <div>
        <input
          type="password"
          value={password}
          onChange={handlePassword}
          placeholder="Type your password"
        />
      </div>
      <button onClick={handleRegister}>
        Submit
      </button>
      <div style={{ fontSize: '12px' }}>
          Already have an account? Please {' '}
          <span 
            onClick={() => navigate('/')}
            style={{ color: '#293462', fontWeight: 'bold', cursor: 'pointer' }}
          >
            sign in
          </span>
      </div>
    </div>
  );
};

export default Register;
```
Now, let's go to the **Register page** and type an email and password and see what happens

![Registering a new user](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9u0bzlbj9um79cyvpd5q.gif)

It seems it is working. But what happened? Well, when the user clicked the Submit button, the application called the `handleRegister` that called the `createUserWithEmailAndPassword` and checked if everything was fine and created the user. Now let's see the authentication console in Firebase. If you go there you will realize that this new user was added to the list (now with only one user) of users that have credentials to be signed in.

![authentication user](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/whqgr04el5xggid3xa2u.png)

Pretty good! Let's see what happened if we try to register with the same user again. I will keep the console open.

![Attempt to register an already registered user](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qjcbphd64t7icm8e3rxl.gif)

Ah-ha! So, as we can see, if an already registered user try to register again, the promise resolves negatively and since we create the `console.log(errorCode)` inside the `catch` function, it show exactly why. In this case, the firebase authentication shows us that the e-mail is already in use so it does not register the user again. I encourage you to submit an empty e-mail and password. It will, again, return an error saying that the e-mail is invalid.

**Remark:** In real word applications we can use this `errorCode` to show good messages to the user.

Now you already imagine what we are going to do, huh? Yes, you are right! We now are going to use the `loginUser` function created in `firebase.js` to sign in an existing user. In order to do this, we move to `Login.jsx` file, import the `loginUser`
```react
import { loginUser } from '../../configs/firebase';
```
and call it inside the previous created `handleSubmit` function.
```react
const handleSubmit = () => {
    loginUser(email, password)
      .then((userCredential) => {
        alert('User signed in');
        navigate('/home');
      })
      .catch((error) => {
        alert('Something went wrong!');
        const errorCode = error.code;
        console.log(errorCode);
      });
  };
```
The full `Login.jsx` becomes this way.
```react
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom'
import { loginUser } from '../../configs/firebase';

const Login = () => {

  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const navigate = useNavigate();

  const handleEmail = event => {
    setEmail(event.target.value);
  };

  const handlePassword = event => {
    setPassword(event.target.value);
  };

  const handleSubmit = () => {
    loginUser(email, password)
      .then((userCredential) => {
        alert('User signed in');
        navigate('/home');
      })
      .catch((error) => {
        alert('Something went wrong!');
        const errorCode = error.code;
        console.log(errorCode);
      });
  };

  return (
    <div style={{ textAlign: 'center' }}>
      <div>
        <h3>Login</h3>
      </div>
      <div>
        <input
          value={email}
          onChange={handleEmail}
          placeholder="Type your e-mail"
        />
      </div>
      <div>
        <input
          type="password"
          value={password}
          onChange={handlePassword}
          placeholder="Type your password"
        />
      </div>
      <button onClick={handleSubmit}>
        Submit
      </button>
      <div style={{ fontSize: '12px' }}>
          Dont't have an account? Register {' '}
          <span 
            onClick={() => navigate('/register')}
            style={{ color: '#293462', fontWeight: 'bold', cursor: 'pointer' }}
          >
            here
          </span>
      </div>
    </div>
  );
};

export default Login;
```
Now let's see how it works in the browser.

![Login with authorized user](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fiyteg8lxh4xm6ubuhu1.gif)

Perfect! So, if you try to sign in with a user that is in the authentication list, the access will be allowed and the user will be redirected to the **Home page**. That's exactly what we wanted. If the user is not registered, we expect the access will be forbidden.

![Login with unauthorized user](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rkzo07ayn6nuh3at5lgh.gif)

Yeah! In this case the access was not allowed and, in the console we see the message "user not found" which is exactly what is happening now.

## Authorization

We just talked about authentication. Now it's time to set the authorization of our pages. Remember that we said before. We want that the **Home Page** to be accessed only if the user is authenticated. Otherwise, the user is redirected to the **Login Page**. In order to do that, we first need to include a button in the **Home Page** so the user can logout. First, let's move to the `firebase.js` file and import the `signout` from `firebase/auth`
```react
import {
  getAuth,
  createUserWithEmailAndPassword,
  signInWithEmailAndPassword,
  signOut,
} from 'firebase/auth';
```
and create in the end the `logoutUser` function
```react
//### LOGOUT USER ###//
export const logoutUser = () => {
  const auth = getAuth();
  signOut(auth).then(() => {
    alert('User signed out!');
  }).catch((error) => {
    alert('Something went wrong!');
    const errorCode = error.code;
    console.log(errorCode);
  });
};
```

The changed `firebase.js` file becomes

```react
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";

import {
  getAuth,
  createUserWithEmailAndPassword,
  signInWithEmailAndPassword,
  signOut,
} from 'firebase/auth';

const firebaseConfig = {
  apiKey: `${process.env.REACT_APP_API_KEY}`,
  authDomain: `${process.env.REACT_APP_AUTH_DOMAIN}`,
  projectId: `${process.env.REACT_APP_PROJECT_ID}`,
  storageBucket: `${process.env.REACT_APP_STORAGE_BUCKET}`,
  messagingSenderId: `${process.env.REACT_APP_MESSAGING_SENDER_ID}`,
  appId: `${process.env.REACT_APP_APP_ID}`,
  measurementId: `${process.env.REACT_APP_MEASUREMENT_ID}`,
};
  
export const firebaseApp = initializeApp(firebaseConfig); // initialize app
export const db = getFirestore(); // this gets the firestore database

//### REGISTER USER WITH FIREBASE AUTHENTICATION ###//
export const registerUser = (email, password) => {
  const auth = getAuth();
  return createUserWithEmailAndPassword(auth, email, password);
};

//### LOGIN USER WITH FIREBASE ###//
export const loginUser = (email, password) => {
  const auth = getAuth();
  return signInWithEmailAndPassword(auth, email, password);
};

//### LOGOUT USER ###//
export const logoutUser = () => {
  const auth = getAuth();
  signOut(auth).then(() => {
    alert('User signed out!');
  }).catch((error) => {
    alert('Something went wrong!');
    const errorCode = error.code;
    console.log(errorCode);
  });
};
```
Now we just import the `logoutUser` function in the **Home Page** and call it in the created Logout button

### Home.jsx
```react
import React from 'react';

import { logoutUser } from '../../configs/firebase';

const Home = () => {
  return (
    <div style={{ textAlign: 'center' }}>
      <h1>Welcome user!</h1>
      <div>
        If you are here, you are allowed to it!
      </div>
      <button onClick={logoutUser}>
        Logout
      </button>
    </div>
  );
};

export default Home;
```

Nothing special so far. We still did not block the **Home Page** to unauthenticated users, but we are on our way to do it.

![Inserting logout function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/904c830nkr5jxqssm10j.gif)

Well let's create the strategy to authorized and unauthorized pages to our application: the routes '/' and '/register' will be available always and the route '/home' will be available only for authenticated users. Right, but how do we know if a user is authenticated or not?

The Firebase authentication helps us with this task. We just need to use the `onAuthStateChanged` function. For more information we recommend the Firebase [documentation](https://firebase.google.com/docs/auth/web/manage-users) that tells us to define an observer to identify if the user is authenticated or not. We are going to make use of the React Context API to create a global state related to that. I'm assuming that you know how to work with context, but if you don't, I suggest this [link](https://dev.to/vcnsiqueira/teste-48gm) where I explain how to use it.

Well, in the `src` folder, we create a folder called `context` and inside of it we create the folder `AuthContext` with the file `index.jsx`.

### src/context/AuthContext/index.jsx
```react
import React, { createContext, useState, useEffect } from 'react';
import { getAuth, onAuthStateChanged } from "firebase/auth";

export const AuthContext = createContext();

export const AuthProvider = ({ children }) => {

  const [currentUser, setCurrentUser] = useState(null);

  const auth = getAuth();
  
  useEffect(() => {
    onAuthStateChanged(auth, (user) => {
      if (user) {
        const uid = user.uid;
        setCurrentUser(uid);
      } else {
        setCurrentUser(null);
      };
    });
  }, []);

  return (
    <AuthContext.Provider value={{ currentUser }}>
      {children}
    </AuthContext.Provider>
  );

};
```

Well, basically this context is constantly listening if there's any changes with the authentication and storing it in the variable `currentUser`. So, every time a user is authenticated, the `currentUser` will be equal to the user id of the Firebase authentication and if no user is authenticated this variable is null.

After creating this context we wrap the `AuthProvider` around the **App** component

### App.js
```react
import {
  BrowserRouter as Router,
  Routes,
  Route,
} from "react-router-dom";

import { AuthProvider } from './context/AuthContext';

import Home from './views/Home';
import Login from './views/Login';
import Register from './views/Register';

function App() {
  return (
    <AuthProvider>
      <Router>
        <Routes>
          <Route path='/' element={<Login />} />
          <Route path='/register' element={<Register />} />
          <Route path='/home' element={<Home />} />
        </Routes>
      </Router>
    </AuthProvider>
  );
}

export default App;
```

After this, we can use the user id anywhere we want and that is the information we need to allow the user to access or not the **Home page**. We will create a new generic component called `PrivateRoute` which will be inside the newly created `components` folder inside the `src` folder

![PrivateRoute component folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwtw7de824bmhx0m8kce.png)

The PrivateRoute component will be used to wrap the **Home Page** route component so if the currentUser exists it will render the Home Page and, otherwise it will throw the user to the **Login Page**

### PrivateRoute.jsx
```react
import React, { useContext } from 'react';
import { Navigate} from 'react-router-dom';
import { AuthContext } from '../../context/AuthContext';

const PrivateRoute = ({ children }) => {
    
  const { currentUser } = useContext(AuthContext);

  if (!!currentUser) {
    return children
  }
  return <Navigate to='/' />

};

export default PrivateRoute;
```
and then, we import the PrivateRoute in the **App** component and wrap the **Home Page** route.

### App.js
```react
import {
  BrowserRouter as Router,
  Routes,
  Route,
} from "react-router-dom";

import { AuthProvider } from './context/AuthContext';

import Home from './views/Home';
import Login from './views/Login';
import Register from './views/Register';
import PrivateRoute from "./components/PrivateRoute";

function App() {
  return (
    <AuthProvider>
      <Router>
        <Routes>
          <Route path='/' element={<Login />} />
          <Route path='/register' element={<Register />} />
          <Route path='/home' element={
            <PrivateRoute>
              <Home />
            </PrivateRoute>}
          />
        </Routes>
      </Router>
    </AuthProvider>
  );
}

export default App;
```

Now, if we try to access the home page by the url route, the application will not allow us to do that and the **Home Page** is only accessed by authenticated users.

![Home page only accessed by authorized users](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ltk0bwlawsow9lxywleq.gif)

## How to use Firestore to store data

Everything is working fine, but what is Firestore doing exactly? So far, nothing. And that's because we didn't call it for anything, actually. Let's change this. You can skip this if you don't want to learn how to store data information with Firestore database. If you are still here, let's recall some initial ideas. We wanted that when users logged in, they would be redirected to the **Home Page** with a custom welcome message that shows his e-mail and the date when they registered. But, for now, we just have the id of the user who access the **Home Page** through the AuthContext.

But, think about it. If we could store both the e-mail and the register date when the user register himself in the app with his own id and if we could recover this information inside the **Home Page** our problems would be solved. And a database is precisely the tool used to do that.

Moving back to the Firebase documentation we can find [here](https://firebase.google.com/docs/firestore/manage-data/add-data) how we can add data to Firestore. So we move back to the **Register page** and import the database `db` from `firebase.js` and we import the functions `doc`, `setDoc` and `Timestamp` from `firebase/firestore` and make a small change in the `handleRegister` so it can write inside the `users` collection of Firebase Firestore.

### Register.jsx
```react
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { registerUser, db } from '../../configs/firebase';
import { doc, setDoc, Timestamp } from 'firebase/firestore';

const Register = () => {

  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const navigate = useNavigate();

  const handleEmail = event => {
    setEmail(event.target.value);
  };

  const handlePassword = event => {
    setPassword(event.target.value);
  };

  const handleRegister = () => {
    registerUser(email, password)
      .then((userCredential) => {
        const user = userCredential.user
        setDoc(doc(db, 'users', user.uid), {
          email: email,
          registeredAt: Timestamp.fromDate(new Date()),
        });
        alert('User created successfully!')
      })
      .catch((error) => {
        alert('Something went wrong!');
        const errorCode = error.code;
        console.log(errorCode);
      });
  }

  return (
    <div style={{ textAlign: 'center' }}>
      <div>
        <h3>Register</h3>
      </div>
      <div>
        <input
          value={email}
          onChange={handleEmail}
          placeholder="Type your e-mail"
        />
      </div>
      <div>
        <input
          type="password"
          value={password}
          onChange={handlePassword}
          placeholder="Type your password"
        />
      </div>
      <button onClick={handleRegister}>
        Submit
      </button>
      <div style={{ fontSize: '12px' }}>
          Already have an account? Please {' '}
          <span 
            onClick={() => navigate('/')}
            style={{ color: '#293462', fontWeight: 'bold', cursor: 'pointer' }}
          >
            sign in
          </span>
      </div>
    </div>
  );
};

export default Register;
```
Before try it, move to the Firestore console, access the tab **Rules** and change the code inside of it to the following (specially if you select the production mode during configuration)

![Firestore rules](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sbjtrq9l3k0h1pi5mehg.png)

Now, let's try the application. We move to the Register page and create a new registration.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/879qat5h9e9lbao7b7t1.gif)

So, as you can see, now every time a new user register in the application, the e-mail and date of register is stored in Firestore in the collection users inside a document with the user id, under the fields `email` and `registeredAt` respectively. Now, we just need to get the data from Firestore inside the **Home Page**. 

Reading the Firestore [documentation](https://firebase.google.com/docs/firestore/query-data/get-data#:~:text=There%20are%20two%20ways%20to,to%20receive%20data%2Dchange%20events.) we just import `db` from `configs/firebase.js` and `doc` and  `getDoc` from `firebase/firestore` and use the `useEffect` hook to get this information from firestore every time any change happens in the component. We also import the `AuthContext` hook to get the user id to access the corresponding document in firestore. So we change the **Home Page** component this way

### Home.jsx
```react
import React, { useContext, useEffect, useState } from 'react';

import { logoutUser, db } from '../../configs/firebase';
import { doc, getDoc } from 'firebase/firestore';
import { AuthContext } from '../../context/AuthContext';

const Home = () => {

  const { currentUser } = useContext(AuthContext);

  const [email, setEmail] = useState(null);
  const [registered, setRegistered] = useState(null);
  
  useEffect(() => {
    const getUserInformation = async () => {
      const docRef = doc(db, "users", currentUser);
      const docSnap = await getDoc(docRef);
  
      if (docSnap.exists()) {
        const userData = docSnap.data();
        setEmail(userData.email);
        setRegistered(userData.registeredAt.toDate().toISOString().substring(0,10));
      } else {
        console.log("This document does not exists");
      }
    };

    getUserInformation();
  }, []);

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>Welcome {email}!</h1>
      <div>
        If you are here, you are allowed to it.
      </div>
      <div>
        Date of register: {registered}
      </div>
      <button onClick={logoutUser}>
        Logout
      </button>
    </div>
  );
};

export default Home;
```
And now, every time a user access the application, the **Home Page** will display his e-mail and date of registration.

![Home page showing the email and date of registration of the user](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2wlqc2lted0yjprnemxm.gif)

## Conclusion

It's not too difficult to set up a project with Firebase and use its features (Firestore and Firebase authentication) to handle user authentication and authorization with React!

I hope you enjoy and if you have any questions, just let me know! Thank you all!