useAuth Hook

Nivetha Krishnan
3 min readSep 5, 2022

An Authentication Hook

Photo by Micah Williams on Unsplash

A very common scenario for any application is authentication and we have a bunch of components to render differently depending on whether the user is logged in or not. Inside any component, we want to be able to access the current user and allow them to sign in or out. To accomplish this, let's create a custom useAuth hook.

The API useAuth is simple to start:

  • user – The current user, if any.
  • signout – A function to sign the user out and clear the session.
  • signinWithGitHub – A function to initiate signing into GitHub.

To make these values available throughout our entire application, we’ll use React Context. Context provides a way to pass data through the component tree without having to pass props down manually at every level.

Context is designed to share data that can be considered “global” for a tree of React components, like the current user. Let’s create a new file lib/auth.js and create a Context provider.

lib/auth.js

import React, { useState, useEffect, useContext, createContext } from 'react'import firebase from './firebase'const authContext = createContext()export function ProvideAuth({ children }) {const auth = ProvideAuth()return <authContext.Provider value={auth}>{children}</authContext.Provider>}export const useAuth = () => {return useContext(authContext)}function useProvideAuth() {const [user, setUser] = useState(null)const [loading, setLoading] = useState(true)const handleUser = (rawUser) => {if (rawUser) {const user = formatUser(rawUser)setLoading(false)setUser(user)return user} else {setLoading(false)setUser(false)return false}}const signinWithGitHub = () => {setLoading(true)return firebase.auth().signInWithPopup(new firebase.auth.GithubAuthProvider()).then((response) => handleUser(response.user))}const signout = () => {return firebase.auth().signOut().then(() => handleUser(false))}useEffect(() => {const unsubscribe = firebase.auth().onAuthStateChanged(handleUser)return () => unsubscribe()}, [])return {user,loading,signinWithGitHub,signout,}}const formatUser = (user) => {return {uid: user.uid,email: user.email,name: user.displayName,provider: user.providerData[0].providerId,photoUrl: user.photoURL,}}

Rather than have each instance of the useAuth hook fetch the current user, the hook simply calls useContext to get the data from farther up in the component tree. The real magic happens in our <ProvideAuth> component and our useProvideAuth hook which wraps all our authentication methods (in this case we're using Firebase) and then uses React Context to make the current auth object available to all child components that call useAuth.

Sign In with Google

It’s easy to extend useAuth to add support for Sign in with Google (or any other provider).

  • Inside the Firebase Console, go to Authentication
  • Click on Sign-in method
  • Enable Google

Then, we can add a new function to useAuth to Sign in with Google.

import Router from 'next/router'const signinWithGoogle = (redirect) => {setLoading(true)return firebase.auth().signInWithPopup(new firebase.auth.GoogleAuthProvider()).then((response) => {handleUser(response.user)if (redirect) {Router.push(redirect)}})}

Sign In with GitHub

When a user signs in with GitHub, we use the GithubAuthProvider and the GitHub app created earlier to fetch information about the user. Then, we save the response.user in the local state inside this hook.

When a user signs out, or the component is no longer being used, we unsubscribe and set the user to false.

const signinWithGitHub = () => {setLoading(true)return firebase.auth().signInWithPopup(new firebase.auth.GithubAuthProvider()).then((response) => handleUser(response.user))}

How <ProvideAuth> Component is Consumed?

The useAuth React Hook allows us to sign in, sign out, and fetch information about the user. Let's test this out to ensure it's set up correctly.

First, we need to wrap our application with ProvideAuth to access the context.

pages/_app.js

import { ProvideAuth } from '../lib/auth'const App = ({ Component, pageProps }) => {return (<ProvideAuth><Component {...pageProps} /></ProvideAuth>)}export default App

Then, modify the file pages/index.js to include the following code.

pages/index.js

import { useAuth } from '../lib/auth'export default function Index() {const auth = useAuth()return auth.user ? (<div><p>Email: {auth.user.email}</p><button onClick={(e) => auth.signout()}>Sign Out</button></div>) : (<button onClick={(e) => auth.signinWithGitHub()}>Sign In</button>)}

You should now be able to sign in with GitHub and view your email address. The auth.user object will look like this.

{"uid": "WG2acuO8oqW3Np0EXCSn28E19Rs2","email": "course@leerob.io","name": "Lee Robinson","provider": "github.com"}

--

--