Firebase Google Sign in with React
Tags: firebase, react, context
Published: 2020-08-17 19:04:03

## **Introduction**
Firebase Authentication provides an easy way to log in users using their already existing social accounts. In this tutorial, we will be looking at how to sign in a user using their google account. 

### **What I'll show you**
* Set up a Firebase project.
* Initializing a Firebase app.
* Enabling user authentication in Firebase.
* Using social providers to log in users
* Using the Context API to provide the authentication status to child components.
* Protecting routes i.e allowing only authenticated users to access a route.
* Handling user logout

### Requirements
* At least [node 8.10 and npm >= 5.6](https://nodejs.org/en/download/)
* Basic understanding of react hooks

## Starter Code
Clone the starter code `git clone -b starter https://github.com/gathoni-k/Firebase-Google-Signin.git`

First off, head [here](https://firebase.google.com) to create a project

Click on get started
![Firebase Get Started](https://dev-to-uploads.s3.amazonaws.com/i/ld9s5d2fcc5igw4uhv4z.png)
You'll be taken to a projects page. Select Add Project and give your project any name you would like,you can accept  google analytics or not and choose a Firebase account, You can choose the default account.
After a while, you'll be in the Project Overview Page.

![Firebase add app](https://dev-to-uploads.s3.amazonaws.com/i/9ls5qewak4x767pu6ru5.png)

To add an app, just below the Get Started... text, click on the third icon, this will let you create a web app and give your web app a nickname..

![Web app nickname](https://dev-to-uploads.s3.amazonaws.com/i/7nrqf1599suqjhgjp6d5.png)

Click next and copy the firebaseConfig object, we will be using it later to initialize our app.

To enable authentication, Go back to your projects overview page and click on Authentication tab then set up sign-in method and enable Google
.
![Set up sign in method](https://dev-to-uploads.s3.amazonaws.com/i/0elq8o0jh4yqu223hey1.png)

**Okay cool now to the fun stuff...**💃🏾💃🏾

Lets start off by grabbing that Firebase config object we grabbed from earlier, since these is sensitive information, we will want to store in a .env file and add it to the .gitignore file so that we don't mistakenly push it to a version source control provider like GitHub.

Having said that, create a .env file in the root folder and add the following

_.env_
```JavaScript
REACT_APP_API_KEY=""
REACT_APP_AUTH_DOMAIN=""
REACT_APP_DATABASE_URL=""
REACT_APP_PROJECT_ID=""
REACT_APP_STORAGE_BUCKET=""
REACT_APP_MESSAGING_SENDER_ID=""
REACT_APP_APP_ID=""
REACT_APP_MEASUREMENT_ID=""
```
Now fill it in using the corresponding values from the Firebase config object we grabbed earlier.

> Note that the REACT_APP_ prefix is necessary. You can read more on setting custom environment variables in react [here](https://create-react-app.dev/docs/adding-custom-environment-variables/)

To access .env variables, we will have to install dotenv. This is an npm package that loads environment variables from a .env file into process.env.

```bash
yarn add dotenv
```

Install Firebase
```bash
yarn add firebase
```

Now lets put all that to use
Create a services folder and create firebase.js file in it and add the following code

_src/services/firebase.js_
```javascript
import dotenv from 'dotenv'
dotenv.config()
import * as firebase from "firebase/app";
import "firebase/auth";

firebase.initializeApp({
  apiKey: process.env.REACT_APP_API_KEY,
  authDomain: process.env.REACT_APP_AUTH_DOMAIN,
  databaseURL: process.env.REACT_APP_DATABASE_URL,
  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
});
```
In the above code:
 * we've imported dotenv and configured it.
 * Imported firebase 
 * Initialized firebase app
Next create a signin function that will let us sign in with google. We will use the `signInWithPopup()` method.

_src/services/firebase.js_
```javascript
export const auth = firebase.auth();
const googleProvider = new firebase.auth.GoogleAuthProvider()
export const signInWithGoogle = () => {
  auth.signInWithPopup(googleProvider).then((res) => {
    console.log(res.user)
  }).catch((error) => {
    console.log(error.message)
  })
}
```

To use this function, we will have to import it in the Login.js file and add an onClick handler to the sign in button.

_src/Login.js_
```javascript
import React from "react";
import "./Login.css"
import { signInWithGoogle } from "./services/firebase";
export default function Login() {
  return (
      <div className="login-buttons">
        <button className="login-provider-button" onClick={signInWithGoogle}>
        <img src="https://img.icons8.com/ios-filled/50/000000/google-logo.png" alt="google icon"/>
        <span> Continue with Google</span>
       </button>
      </div>
  );
}
```

And that's it, you can now sign in your user via their google account.
But how do you prevent unauthorized users from accessing protected routes? Well, they are several ways but I'll show you how by using the context API. The context API is a way of defining global variables that can be accessed through your components instead of passing props component to component(prop drilling).

Create a Providers folder and in it create a UserProvider.js file

_src/providers/UserProvider.js_
```javascript
import React, {useState, useEffect,  createContext} from "react";
import { auth } from "../services/firebase"
export const UserContext = createContext({user: null})
export default () => {
  const [user, setuser] = useState(null)
  useEffect(() => {
auth.onAuthStateChanged(async (user) => {
  const { displayName, email }  = user;
  setuser({
    displayName,
    email
  })
})
  },[])
  return (
    <UserContext.Provider value={user}>{props.children}</UserContext.Provider>
  )
}
```

To understand the above code, we first have to understand the context API.
We will store the user value as context, and so we create that using createContext() and pass in the initial value of our user, in this case null and assign in to a variable UserContext.
UserContext will give us the Provider component which provides values. In our case it will provide us with the user.
In the default function, we will have to keep track of the authentication status of our user. This is done using the onAuthStateChanged, a function provided by firebase.auth() which we exported in the firebase.js app as auth.
Once the user signs in the state is updated with their display name and email.
Finally, the function returns the UserContext Provider component with the user value.

To use these value we have to wrap the components we want to use the user value in with the the UserProvider component.

_src/App.js_
```javascript
import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import "./App.css"
import Navbar from "./Navbar"
import Login from "./Login";
import Dashboard from "./Dashboard";
import UserProvider from "./providers/UserProvider";
function App() {
  return (
    <UserProvider>
    <Router>
    <Navbar/>
    <div className="App">
        <Switch>
          <Route exact path="/">
            <Login />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
        </Switch>
    </div>
    </Router>
    </UserProvider>
  );
}

export default App;
```
The user value will now be available to our components via the useContext hook.

_src/Login.js_
```javascript
import React, { useContext } from 'react';
import './Login.css'
import { signInWithGoogle } from './services/firebase';
import { UserContext } from './providers/UserProvider';
export default function Login() {
  const user = useContext(UserContext)
  return (
      <div className="login-buttons">
        <button className="login-provider-button" onClick={signInWithGoogle}>
        <img src="https://img.icons8.com/ios-filled/50/000000/google-logo.png" alt="google icon"/>
        <span> Continue with Google</span>
       </button>
      </div>
  );
}
```
Now what?
Now we check the user value and redirect to the dashboard page if they are authenticated

_src/Login.js_
```javascript
import React, { useEffect, useContext, useState } from 'react';
import './Login.css'
import { signInWithGoogle } from './services/firebase';
import { UserContext } from './providers/UserProvider';
import { Redirect } from 'react-router-dom';
export default function Login() {
  const user = useContext(UserContext)
  const [redirect, setredirect] = useState(null)

  useEffect(() => {
    if (user) {
      setredirect('/dashboard')
    }
  }, [user])
  if (redirect) {
    <Redirect to={redirect}/>
  }
  return (
      <div className="login-buttons">
        <button className="login-provider-button" onClick={signInWithGoogle}>
        <img src="https://img.icons8.com/ios-filled/50/000000/google-logo.png" alt="google icon"/>
        <span> Continue with Google</span>
       </button>
      </div>
  );
}
```

So there are few new things here. First, the redirect state and the useEffect hook.
The useEffect hook will run after the component renders. It checks for the user value and if its there, that means the user is authenticated and can be redirected to the dashboard.
By setting redirect to the dashboard path, we redirect the user appropriately. Neat!

One problem though, if a user goes to /dashboard, they will still have access. We really don't want that.
To protect the dashboard route, we have to check for the user's authentication status, if authenticated then cool they can stay, if not then we kick them out by redirecting the to the login page

_src/DashBoard.js_
```javascript
import React from "react";
import "./Dashboard.css";
import React, { useEffect, useContext, useState } from "react";
import { UserContext } from "./providers/UserProvider";
import { Redirect } from "react-router-dom";
export default function Dashboard() {
  const user = useContext(UserContext);
  const [redirect, setredirect] = useState(null);

  useEffect(() => {
    if (!user) {
      setredirect("/");
    }
  }, [user]);
  if (redirect) {
    <Redirect to={redirect} />;
  }
  return (
    <div className="dashboard">
      <h1 className="dashboard-text">Welcome Home</h1>
      <button className="logout-button">
        <img
          src="https://img.icons8.com/ios-filled/50/000000/google-logo.png"
          alt="google icon"
        />
        <span> logout</span>
      </button>
    </div>
  );
}
```
Now to logout, this is so simple. We just have to call `auth.signOut()` in our firebase.js file, import the function and add an onClick handler to the logout button.

_src/services/firebase.js_
```
...
export const logOut = () => {
  auth.signOut().then(()=> {
    console.log('logged out')
  }).catch((error) => {
    console.log(error.message)
  })
}
```

_src/Dashboard.js_
```javascript
import React from "react";
import "./Dashboard.css";
import React, { useEffect, useContext, useState } from "react";
import { UserContext } from "./providers/UserProvider";
import { Redirect } from "react-router-dom";
import { logOut } from "./services/firebase";
export default function Dashboard() {
  const user = useContext(UserContext);
  const [redirect, setredirect] = useState(null);

  useEffect(() => {
    if (!user) {
      setredirect("/");
    }
  }, [user]);
  if (redirect) {
    <Redirect to={redirect} />;
  }
  return (
    <div className="dashboard">
      <h1 className="dashboard-text">Welcome Home</h1>
      <button className="logout-button" onClick={logOut}>
        <img
          src="https://img.icons8.com/ios-filled/50/000000/google-logo.png"
          alt="google icon"
        />
        <span> logout</span>
      </button>
    </div>
  );
}
```

This tutorial was a bit lengthy but that's it.

Thanks for reading 🥰