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
- Go to the Firebase console, make sure you are sign in with your Google account
- Click on "Add project" give your project a descriptive name and follow the prompts to create the project
- Navigate to the
Authentication
section in the sidebar and click onSign-in method
- Click on
Add new provider
, findGoogle
in the list of provides and click onEnable
- Go to your newly created project's
Project settings
, fromGeneral
tab find your "SDK setup and configuration", and select "CDN" - 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}}
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.