Google/github login with react-redux doesn't have to be hard. Part-2

This blog is second part of Part 1. Lets get started.

Configuring Firebase in your application

  • Create new folder under src called “config”. Then create a new file called “firebase.config.js” paste the details earlier which you have copied.
import { getApp,getApps,initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: "Your Api key",
  authDomain: "Your Auth Domain",
  projectId: "Your Project ID",
  storageBucket: "fir-auth-9af82.appspot.com",
  messagingSenderId: "384037070653",
  appId: "1:384037070653:web:92ccb6b9b04b69eb45c754"
};

// Initialize Firebase
const app = getApps.length>0 ? getApp() : initializeApp(firebaseConfig)

export {app}

Now we will add the Firebase config to Login Component.

import React from 'react'
import { FcGoogle } from "react-icons/fc"
import { FaGithub } from "react-icons/fa";
import {app} from "../config/firebase.config"
import {getAuth, GoogleAuthProvider} from "firebase/auth"
import { useNavigate } from 'react-router-dom';

const Login = () => {

  //Get details from app regarding user
  const firebaseAuth = getAuth(app)
  const googleProvider = new GoogleAuthProvider()
  const navigate = useNavigate()

  const loginWithGmail = async () => {
    //Refer docs if possible
    //console.log("Google");
   await signInWithPopup(firebaseAuth, googleProvider).then((userCred) => {
    // This gives you a Google Access Token. You can use it to access the Google API.
    if (userCred) {
      //console.log(userCred);
      //Obserever to see the state changed
      firebaseAuth.onAuthStateChanged(cred => {
        cred.getIdToken().then(token => {
          //console.log(token);
          //Now pass the backedn to token

        })
      })
    }
    // ...
  }).catch((error) => {
    // Handle Errors here.
    const errorCode = error.code;
    const errorMessage = error.message;
    // The email of the user's account used.
    const email = error.customData.email;
    // The AuthCredential type that was used.
    const credential = GoogleAuthProvider.credentialFromError(error);
    // ...
  });
  }
  return (
                    .....
             <div className='cursor-pointer w-full px-6 py-3 bg-white rounded-md gap-3 flex items-center justify-center hover:shadow-md' onClick={loginWithGmail}>
                <FcGoogle className="text-4xl"/>
                <p className='text-lg font-semibold text-gray-600'>Sign in with Google</p>
            </div>

            <div className='cursor-pointer w-full px-6 py-3 bg-white rounded-md gap-3 flex items-center justify-center hover:shadow-md'>
                <FaGithub className="text-4xl"/>
                <p className='text-lg font-semibold text-gray-600'>Sign in with Github</p>
            </div>

        </div>
    </div>
  )
}

As you can see there is Token we get, which needs to get send back to backend. Lets create a backend for it.

  • Create a server folder in root directory. In the terminal window, move to server folder.

    firebase init
    
  • In your server folder, Firebase setup will be done. Open Firebase console, go to Project settings. Go under Service accounts tab. Click on Generate new Private key.

Screenshot (108).png

The Downloaded JSON file should be placed in sever/function folder. Rename the file to serviceAccountKey.json Under server dir/folder install

npm i express cors

Copy the following code in server/functions/index.js

const functions = require("firebase-functions");

const admin = require("firebase-admin")
const serviceAccount = require("./serviceAccountKey.json")

const express = require("express")
const cors = require("cors")

const app= express()
app.use(cors({origin:true}))
app.use(express.json())

admin.initializeApp({
    credential:admin.credential.cert(serviceAccount),
})

//Handling user Routes
const userRoute = require("./routes/auth")
//If request comes from "/api/users", navigate to userRoute
app.use("/api/users",userRoute)

//To run sever
exports.app = functions.https.onRequest(app)

In terminal move to , server/functions and run

npm run server

Make sure write correct name of json file serviceAccountKey.json

There will be url : your_url If you have any POSTMAN or ThunderClient extension or Rapid API extension feel free to use it. By selecting GET method: your_url*/api/users/loginValidate* Add the Italic part in URL.

Run now frontend part, make sure run simultaneously both backend as well as frontend. For frontend come under root directory. Run:

yarn start

In the Frontend terminal install axios,

npm i axios

In the src folder, create api/index.js

  • index.js
import axios from "axios";

const baseUrl = 'http://127.0.0.1:5001/fir-auth-9af82/us-central1/app'


export const validateGoogleToken = async (token) => {
    try {
        const res = await axios.get(`${baseUrl}/api/users/loginValidate`,{
            headers:{
                Authorization : `Bearer ${token}`,

            }
        })

        return res.data
    } catch (error) {
        console.log("Something went wrong.!!",error);
        return null

    }
}
  • Move to server/functions/routes/auth.js
const router = require("express").Router()
const admin = require("firebase-admin")

router.get("/loginValidate", async (req,res) => {
    //res.send("Hello auth.js")
    if (!req.headers.authorization) {
        return res.status(500).send({msg:"Invalid token"})
    }
//    console.log(req.headers.authorization.slice(7));   

    const token  = req.headers.authorization.slice(7)
    //return res.status(200).json(token)
    try {
        const decoderValue  = await admin.auth().verifyIdToken(token)
        //console.log(decoderValue);

        if (!decoderValue) {
            return res.status(500).send({msg:`Un-Authorized Access`})   
        }else{
            res.status(200).send({
                success:true,
                data:decoderValue
            })
        }
    } catch (error) {
        return res.status(500).send({msg:`Error : ${error}`})   
    }
})module.exports = router

Go to Login Component in frontend, in loginWithGmail, add…

const loginWithGmail = async () => {
    //Refer docs if possible
    //console.log("Google");
   await signInWithPopup(firebaseAuth, googleProvider).then((userCred) => {
    // This gives you a Google Access Token. You can use it to access the Google API.
    if (userCred) {
      //console.log(userCred);
      //Obserever to see the state changed
      firebaseAuth.onAuthStateChanged(cred => {
        cred.getIdToken().then(token => {
          //console.log(token);
          //Now pass the backedn to token
          validateGoogleToken(token).then((data)=>{
            console.log("Token...",data);
          })
        })
      })

    }
    // ...
  }).catch((error) => {
    // Handle Errors here.
    const errorCode = error.code;
    const errorMessage = error.message;
    // The email of the user's account used.
    const email = error.customData.email;
    // The AuthCredential type that was used.
    const credential = GoogleAuthProvider.credentialFromError(error);
    // ...
  });
  }

Create a Reducer

  • Create src/context folder Create a files called StateProvider.js ,reducer.js ,initialState.js
    1. StateProvider.jsx
import { createContext,useContext,useReducer } from "react";

export const StateContext = createContext()

export const StateProvider = ({reducer,initialState,children}) => (
    <StateContext.Provider value={useReducer(reducer,initialState)}>
        {children}
    </StateContext.Provider>
)

export const useStateValue = () => useContext(StateContext)
  1. initialState.js
export const initialState = {
    user:null
}
  1. reducer.js
export const actionType = {
    SET_USER :"SET_USER"
}

const reducer = (state,action) => {
    console.log("Action...",action);
    console.log("State...",state);
    switch (action.type) {
        case actionType.SET_USER:
            return {
                ...state,
                user:action.user
            }
        default:
            return state;
    }
}

export default reducer

In index.js do following changes

<React.StrictMode>
    <Router>
      <StateProvider initialState={initialState} reducer={reducer}>
        <App />
      </StateProvider>
    </Router>
  </React.StrictMode>
  • To store the data in ContextProvider
    • Login.jsx
.....
import { useStateValue } from '../context/StateProvider';
import { actionType } from '../context/reducer';

....
const [{user},dispatch] = useStateValue()

const loginWithGmail = async () => {
    //Refer docs if possible
    //console.log("Google");
   await signInWithPopup(firebaseAuth, googleProvider).then((userCred) => {
    // This gives you a Google Access Token. You can use it to access the Google API.
    if (userCred) {
      //console.log(userCred);
      //Obserever to see the state changed
      firebaseAuth.onAuthStateChanged(cred => {
        cred.getIdToken().then(token => {
          //console.log(token);
          //Now pass the backedn to token
          validateGoogleToken(token).then((data)=>{
            //console.log("Token...",data);
            dispatch({
              type:actionType.SET_USER,
              user:data.data
            })
          })
        })
      })
    }
    // ...
  }).catch((error) => {
    // Handle Errors here.
    const errorCode = error.code;
    const errorMessage = error.message;
    // The email of the user's account used.
    const email = error.customData.email;
    // The AuthCredential type that was used.
    const credential = GoogleAuthProvider.credentialFromError(error);
    // ...
  });
  }
  • In App.js write similar code to authenticate user
import { useEffect } from "react";
import { Route, Routes } from "react-router-dom";
import Home from "./container/Home";
import Login from "./container/Login";
import {app} from "./config/firebase.config"
import {getAuth} from "firebase/auth"
import { validateGoogleToken } from "./api";
import { useStateValue } from './context/StateProvider';
import { actionType } from "./context/reducer";

function App() {
  const firebaseAuth = getAuth(app)
  const [{user},dispatch] = useStateValue()

  useEffect(() => {
    firebaseAuth.onAuthStateChanged(userCred => {
      if (userCred) {
        userCred.getIdToken().then(token => {
          validateGoogleToken(token).then(res => {
            dispatch({
              type:actionType.SET_USER,
              user:res.data
            })
          })
        })
      }
    }) 
  }, [])

  return (
    <div className="w-screen min-h-screen flex items-center justify-center">
        <Routes>
          <Route path="/" element={<Home/>}/>
          <Route path="/login" element={<Login/>}/>
        </Routes>

    </div>
  );
}
export default App;

In Home.jsx

import React from 'react'
import { useStateValue } from '../context/StateProvider'

import {getAuth} from "firebase/auth"
import {app} from "../config/firebase.config"
import { actionType } from '../context/reducer'
import { useNavigate } from 'react-router-dom'


const Home = () => {

  const [{user},dispatch] = useStateValue()
  const firebaseAuth = getAuth(app)
  const navigate = useNavigate()

  const logoutUser = () => {
    firebaseAuth.signOut().then(() => {
      dispatch({
        type:actionType.SET_USER,
        user:null
      })
      navigate("/login",{
        replace:true
      })
    })
  }


  return (
    <div className="w-screen h-screen flex items-center justify-center">
        <img src={user?.picture} alt="picturek"/>
        <p>{user?.name}</p>
        <p onClick={logoutUser}>........Signout..........</p>
    </div>
  )
}

export default Home

Github Login

Go to Github Component

<div className='cursor-pointer w-full px-6 py-3 bg-white rounded-md gap-3 flex items-center justify-center hover:shadow-md' onClick={loginWithGitHub}>
                <FaGithub className="text-4xl"/>
                <p className='text-lg font-semibold text-gray-600'>Sign in with Github</p>
            </div>
const loginWithGitHub = async () => {
    await signInWithPopup(firebaseAuth,gitProvider).then((data)=>{
      //console.log(data);
      const cred = GithubAuthProvider.credentialFromResult(data)
      const token = cred.accessToken

      dispatch({
        type:actionType.SET_USER,
        user:cred.user
      })
      navigate("/")
    }).catch((err)=>{
      console.log("error",err);
    })

  }

Now the Web app will work with both Google as well as Github. I hope this blog had provided some insightful knowledge. For better knowledge do refer documentation of firebase. Thank you so much.