Firebase Auth with React Hooks in Typescript
Tags: react, firebase, typescript, auth
Published: 2020-10-08 05:05:50

### Overview
Firebase is a great provider for hooking up easy and simple authentication to a new or existing project, and can easily be integrated with other features of Google Cloud Platform. Adding it to your application can be simple, but a lot of tutorials don't cater for larger scale applications, and how it could be implemented cleanly and with quality assurance in mind.

### Assumed Knowledge
 * Firebase
 * Typescript
 * React
 * React Hooks (specifically useContext)

### Setting Up
#### GCP and Firebase
To start, you'll need to setup a GCP account and login to the Firebase console at https://console.firebase.google.com/. You'll then need to setup a project for us to use in this run through. For this walkthrough, I've named my project "HooksAuth"

I'm then going to enable email/password and Google Authentication by navigating to the "Authentication" page from the left menu and enabling them there

#### React
To setup your react project from scratch, you can use create-react-app by running the following command in your cli: `npx create-react-app hooksAuth --template typescript`

The next (optional) step is to remove all the boilerpate content from your React App. e.g. react images, manifest, App.tsx content etc. This helps me to start from a cleaner slate and not have create-react-app content in my site

#### Setting up the hooks
We are going to abstract our firebase app away through useContext so it's state can be shared through the app, and any abstractions and access can easily be updated and refactored.

 * Create `FirebaseContext.ts`
 * Add your config to this file (this can be retrieved by registering a web app in the firebase console), and will look something like:
```
import firebase from 'firebase';

const firebaseConfig = {
    apiKey: "KEY",
    authDomain: "somedomain.firebaseapp.com",
    databaseURL: "https://somedomain.firebaseio.com",
    projectId: "hooksauth-ID_HERE",
    storageBucket: "hooksauth-ID_HERE.appspot.com",
    messagingSenderId: "SENDER_ID",
    appId: "APP_ID"
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
```
 * Now we want to secure these values using .env (before we accidentally commit anything). To do this, create a file called .env.local in the root of the project, and add this file to .gitignore. In that file, you'll want to define your ID's in something similar to this format:
```
REACT_APP_API_KEY=KEY
REACT_APP_AUTH_DOMAIN=somedomain.firebaseapp.com
REACT_APP_DB_URL=https://somedomain.firebaseio.com
REACT_APP_PROJECT_ID=hooksauth-ID_HERE
REACT_APP_STORAGE_BUCKET=hooksauth-ID_HERE.appspot.com
REACT_APP_MESSAGING_SENDER_ID=SENDER_ID
REACT_APP_APP_ID=APP_ID
```
and update your config to be:
```
const firebaseConfig = {
    apiKey: process.env.REACT_APP_API_KEY,
    authDomain: process.env.REACT_APP_AUTH_DOMAIN,
    databaseURL: process.env.REACT_APP_DB_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,
};
```
* Move the firebase initialisation. To stop the firebase initialisation happening multiple times, we want to wrap it in an if statement to make it look like:
```
// Initialize Firebase
if (!firebase.apps.length) {
    firebase.initializeApp(firebaseConfig);
    firebase.analytics();
}
```
* Now we want to actually do some React stuff. We want to create a context for us to use in the rest of our app. So using react hooks, we will create a context that contains the auth providers we will use, and a way to access our firebase object:
```
export interface IFirebaseContext {
    firebase: firebase.app.App,
    authProviders: string[]
}

export const FirebaseContext = React.createContext({} as IFirebaseContext)
```
And then our provider to wrap the app:
```
export const FirebaseProvider = ({ children }: any) => {
    return (
        <div>
            <FirebaseContext.Provider value={{ 'firebase': firebase.app(), authProviders } as IFirebaseContext}>
                {children}
            </FirebaseContext.Provider>
        </div>
    );
}
```
 * Now to wrap the rest of our app. So in App.tsx, we want to wrap the root div in `<FirebaseProvider>`. And now in the rest of our App, if we want to access our auth object, we would simply call `const firebaseContext = useContext(FirebaseContext);` and access it there. This can be combined with the firebase-ui package to get your authentication up and running in no time

For an example of usage with the react-firebaseui package and saving user data see my github repo: https://github.com/bmpickford/firebase-auth-example

Open to any improvements so feel free to submit a PR or an issue