Back to overview

React Firebase AuthProvider

FirebaseHooksReact
import '@/lib/firebase';

import {
  getAuth,
  GithubAuthProvider,
  onAuthStateChanged,
  signInWithPopup,
  signOut,
  User as FirebaseUser,
} from 'firebase/auth';
import React, {createContext, useContext, useEffect, useState} from 'react';

import {createUser} from '@/lib/firestore';
import {User} from '@/models/user';

type AuthState = {
  user: User | null;
  signinWithGithub: () => void;
  signout: () => void;
};

const initialState: AuthState = {
  user: null,
  signinWithGithub: () => {},
  signout: () => {},
};

const AuthContext = createContext < AuthState > initialState;
AuthContext.displayName = 'AuthContext';

export function AuthProvider({children}: {children: React.ReactNode}) {
  const auth = useProvideAuth();
  return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>;
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error(`useAuth must be used within a AuthProvider`);
  }
  return context;
}

function useProvideAuth() {
  const [user, setUser] = (useState < User) | (null > null);

  const provider = new GithubAuthProvider();
  const auth = getAuth();

  const signinWithGithub = () => {
    return signInWithPopup(auth, provider)
      .then((res) => {
        // This gives you a GitHub Access Token. You can use it to access the GitHub API.
        const credential = GithubAuthProvider.credentialFromResult(res);
        const token = credential?.accessToken;

        const user = formatUser(res.user);
        // save to firebase db
        createUser(user);
        setUser(user);
        return user;
      })
      .catch((error) => {
        // Handle Errors here.
        const errorCode = error.code;
        const errorMessage = error.message;
        // The email of the user's account used.
        const email = error.email;
        // The AuthCredential type that was used.
        const credential = GithubAuthProvider.credentialFromError(error);
        // ...
      });
  };

  const signout = () => {
    return signOut(auth).then(() => {
      setUser(null);
    });
  };

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (firebaseUser) => {
      if (firebaseUser) {
        // User is signed in, see docs for a list of available properties
        // https://firebase.google.com/docs/reference/js/firebase.User
        const user = formatUser(firebaseUser);
        setUser(user);
      } else {
        // User is signed out
        setUser(null);
      }
    });

    return () => unsubscribe();
  }, [auth]);

  return {
    user,
    signinWithGithub,
    signout,
  };
}

function formatUser(user: FirebaseUser): User {
  return {
    uid: user.uid,
    email: user.email,
    name: user.displayName,
    provider: user.providerData[0].providerId,
    photoUrl: user.photoURL,
  };
}