DEV Community

Madhav Pandey
Madhav Pandey

Posted on

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 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 signed in with your Google account
  2. Click on "Add project" and 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>
Enter fullscreen mode Exit fullscreen mode

I have used SVG google icon, here is 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>
Enter fullscreen mode Exit fullscreen mode

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


 {{> googleLogin}}

Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

In above code, we added 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 return response. If we have success response which we will setup 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;

Enter fullscreen mode Exit fullscreen mode

In above code, first, we setup 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);
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)