How to Add Google Sign-In to Your Node.js Application

I have recently added Sign in with Google feature in my NodeJS app P_Blog which allows users to quickly and easily login in using their existing Google account. Sign in with Googgle feature is very convenient as it is trusted and no need to create and memorize password. In this blog post, I would like to explain how I implemented Sign in with Google feature in my nodejs app. I have used Nodejs(Expressjs) and Express-handlebars in the app. The app already has traditional email/password login system. Now, let's get right into it.

Setup a Firebase Project

  1. Go to the Firebase console, make sure you are sign in with your Google account
  2. Click on "Add project" give your project a descriptive name and follow the prompts to create the project
  3. Navigate to the Authentication section in the sidebar and click on Sign-in method
  4. Click on Add new provider, find Google in the list of provides and click on Enable
  5. Go to your newly created project's Project settings, from General tab find your "SDK setup and configuration", and select "CDN"
  6. Copy whole script, we need to download the Service Account JSON file for backend but for that we will come latter

Add Continue with Google button

We have set up the project, Now let's create a file googleLogin.handlebars in partials folder as we will use this button in login and signup pages. Following code will go in this file including copied project's SDK and configuration.

 <script type="module">
  // Import the functions you need from the SDKs you need
  import { initializeApp } from "https://www.gstatic.com/firebasejs/10.8.1/firebase-app.js";
  import {getAuth, GoogleAuthProvider, signOut, signInWithPopup} from "https://www.gstatic.com/firebasejs/10.8.1/firebase-auth.js";


  // 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
  // For Firebase JS SDK v7.20.0 and later, measurementId is optional
  const firebaseConfig = {
    apiKey: "",
    authDomain: "",
    projectId: "",
    storageBucket: "",
    messagingSenderId: "",
    appId: "",
    measurementId: ""

  };

  // Initialize Firebase
  const app = initializeApp(firebaseConfig);
const auth = getAuth(app)
</script>

I have used SVG google icon, here are html and css for that-

<style>
  .google-btn-container {
    background-color: rgb(66, 133, 244);
    color: rgb(255,
        255, 255);
    height: 50px;
    width: 240px;
    border: none;
    text-align: center;
    box-shadow: rgba(0, 0, 0, 0.25) 0px 2px 4px 0px;
    font-size: 16px;
    line-height:
      48px;
    display: block;
    border-radius: 1px;
    transition: background-color 0.218s ease 0s, border-color 0.218s ease 0s, box-shadow 0.218s ease 0s;
    font-family:
      Roboto, arial, sans-serif;
    cursor: pointer;
    user-select: none;
    margin: .5rem 0;
  }

  .google-icon {
    width: 48px;
    height: 48px;
    text-align: center;
    display:
      block;
    margin-top: 1px;
    margin-left: 1px;
    float: left;
    background-color:
      rgb(255, 255, 255);
    border-radius: 1px;
    white-space: nowrap;
    display: flex;
    align-items: center;
    justify-content: center;
  }
</style>


<div class="google-btn-container">
  <span class="google-icon">

    <svg
      width="32px"
      height="32px"
      viewBox="0 0 32 32"
      data-name="Layer 1"
      id="Layer_1"
      xmlns="http://www.w3.org/2000/svg"
      fill="#000000"
    ><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g
        id="SVGRepo_tracerCarrier"
        stroke-linecap="round"
        stroke-linejoin="round"
      ></g><g id="SVGRepo_iconCarrier"><path
          d="M23.75,16A7.7446,7.7446,0,0,1,8.7177,18.6259L4.2849,22.1721A13.244,13.244,0,0,0,29.25,16"
          fill="#00ac47"
        ></path><path
          d="M23.75,16a7.7387,7.7387,0,0,1-3.2516,6.2987l4.3824,3.5059A13.2042,13.2042,0,0,0,29.25,16"
          fill="#4285f4"
        ></path><path
          d="M8.25,16a7.698,7.698,0,0,1,.4677-2.6259L4.2849,9.8279a13.177,13.177,0,0,0,0,12.3442l4.4328-3.5462A7.698,7.698,0,0,1,8.25,16Z"
          fill="#ffba00"
        ></path><polygon
          fill="#2ab2db"
          points="8.718 13.374 8.718 13.374 8.718 13.374 8.718 13.374"
        ></polygon><path
          d="M16,8.25a7.699,7.699,0,0,1,4.558,1.4958l4.06-3.7893A13.2152,13.2152,0,0,0,4.2849,9.8279l4.4328,3.5462A7.756,7.756,0,0,1,16,8.25Z"
          fill="#ea4435"
        ></path><polygon
          fill="#2ab2db"
          points="8.718 18.626 8.718 18.626 8.718 18.626 8.718 18.626"
        ></polygon><path
          d="M29.25,15v1L27,19.5H16.5V14H28.25A1,1,0,0,1,29.25,15Z"
          fill="#4285f4"
        ></path></g></svg>
  </span>

  <span class="google-text">
    Continue with Google

  </span>

</div>

Now, we need to add this in both Login and Signup page.

 {{> googleLogin}} 

google-btn

Send userIdToken to Backend

Once user click on Continue with Google button google displays a screen asking user to choose which account to use and outlining the permissions requested by the app. Upon user grants permission, Google provides a secure ID token which contains user information. We need to send this token to backend for verification. Once verified, we will navigate user to the home page. Here is code for that,


const sendUserIdToken = async(userIdToken)=>{

  try{
   const res = await fetch("/googleLogin",{
    headers:{
"Content-Type":"application/json"
    },
    method:"POST",
    body: JSON.stringify({userIdToken})
  })
if(!res.ok){
  throw new Error("Error sending token")
}
const responseData = await res.json();
return responseData;

  }catch(er){
    console.log(er)
  }
}


const googleSignIn = async ()=>{

  try{
const proveder = new GoogleAuthProvider();
const result = await signInWithPopup(auth, proveder);
const userIdToken = await result.user.getIdToken();

if(userIdToken){
 const responseData = await sendUserIdToken(userIdToken);
 if(responseData?.success){
  window.location.href = "/"
 }
  }
  }catch(er){
    console.log(er)
  }


}

const googleBtn = document.querySelector(".google-btn-container");

googleBtn.addEventListener("click", googleSignIn);

In above code, we added a click event in googleBtn which will initiate authentication with Google and obtain a user ID token upon successfull. sendUserIdToken sends token to the backend for verification and returns response. If we have success response which we will set up in backend in a moment, user will redirect to homepage.

Token verification in the Backend

First of all, we need to download service-account-key.json file from Firebase console. For that go to your Project settings and from Service accounts tab download .json file. We need firebase-admin package as well which enables access to Firebase services from nodejs environment. To install run npm install --save firebase-admin in your terminal.

I have created googleLoginRoute inside routes folder and following code goes in that file:

const firebase = require("firebase-admin");
const serviceAccount = require("../service-account-key.json");
firebase.initializeApp({
  credential: firebase.credential.cert(serviceAccount),
});

const Users = require("../module/user");
const setUserDataInSession = require("../utils/setUserDataInSession");


const googleLoginRoute = require("express").Router();

googleLoginRoute.post("/", async (req, res) => {
  const { userIdToken } = req.body;
if(!userIdToken || typeof userIdToken !== "string"){
   return  res.status(400).json({success:false, error:"Missing or Invalid token"})
}

  try {
    const {name, picture, email} = await firebase.auth().verifyIdToken(userIdToken);
    
    const foundUser = await Users.findOne({email});

    if(!foundUser){
        const newUserObj ={
            name,
            email,
            authMethod:"Google",
            password:null,
            profileURL: picture
        }
    const newUser =  await new Users(newUserObj).save();
setUserDataInSession(req, newUser);
    } 
   setUserDataInSession(req, foundUser);
    res.status(200).json({ success: true, error: null });
  } catch (er) {
    console.log(er);
    res.status(401).json({ success: false, error: "Invalid Token" });
  }
});

module.exports = googleLoginRoute;

In above code, first, we set up firebase-admin. We imported Users module to save user information in database. We imported setUserDataInSession function which will set user information in session once user is verified.

When we got POST request at /googleLogin endpoint, first we validate the request body whether the userIdToken is present or not. Then, it attempts to verify the Google ID token using Firebase Authentication. If the token is valid, we extract the user's name, picture and email from the decoded token as we need these to save it to our database, if user is logging in for the first time. If no user is found in the database with provided email, we create a new user and save user to database and set user data in the session. Finally, we send a success response to the frontend.

We are all set, now we just need to required this in app.js file to handle /googleLogin route as following

const googleLoginRoute = require("./routes/googleLoginRoute");

app.use("/googleLogin", googleLoginRoute);

Thank you for reading! I hope this guide was helpful in integrating Google Sign-In into your Node.js application.

Comments

Nishanta pandey
Very informative blog post.
Madhav Pandey
Thank you, Nishanta.
Ny ky Duyen
Very beginners friendly blog post and well explained.
Madhav Pandey
Thank you, Duyen.