Implementing Sign In with Google in NodeJS (without third-party libraries)

Implementing Sign In with Google in NodeJS (without third-party libraries)

Introduction

Recently I was building a project in NextJS in which I needed to implement 'Sign in with Google' without third-party libraries like next-auth, or passport. In this article, we will cover a step-by-step guide for implementing OAuth on your NodeJS app.

Essentials

  1. A MERN Stack or a NextJS app.

  2. This guide uses typescript, but you can use JavaScript too if comfortable with that.

  3. A verified Google account from where we can use Google Developer features.

Google Application Setup

  1. Go to Google Cloud Console.

Image description

  1. Click on create a new project.

Image description

  1. Select the organization and click on Create Project.

  2. Select the recently made project from the above corner.

  3. Go to APIs and Services and select OAuth consent screen from the left sidebar.

  4. Give your App a name that will be seen by the end users and the support email users can contact.

  5. Add a logo for your app, the prescribed 120x120 dimensions.

  6. Add your product's home page, and other links as asked.

  7. Add the domain of your application in the Authorized Domains section.

  8. Go to the next screen and select the userinfo.email and userinfo.profile scope.

  9. Go on to the next screen and add a couple or more email addresses to test your app with.

Image description

  1. After all the steps, go back to the dashboard.

  2. Go on to the credentials screen, from Create credentials, select OAuth Client ID, and select web application.

  3. In the Authorized JavaScript Origins, add the following:

    1. The link to localhost -> http://localhost

    2. The link to localhost with port your applications usually runs on -> http://localhost:3000

    3. The link to Google Developers Playground, https://developers.google.com

    4. The link to your website, for example, https://example.com

    5. Also add the link to the website if you want the authentication on a subdomain too, like https://app.example.com

  4. In the Authorized redirect URIs, add the following:

    1. The redirect link to localhost with the port your applications usually run on -> For example, http://localhost:3000/auth/google/google-callback/oauth/login. Ensure to include google-callback in the redirect URI, as stated in the guidelines.

    2. The link to Google Developers Playground, https://developers.google.com/oauthplayground.

    3. The redirect link to your website, for example, https://example.com/auth/google/google-callback/oauth/login, should be the same as that of localhost.

Image description

  1. Create the credentials, and download the JSON of the credentials, where you will find the client_id and client_secret.

Writing the Code

In your NextJS project, create a file config/index.ts Export the credentials that you stored in the .env file

NEXT_PUBLIC_GOOGLE_OAUTH_CLIENT_ID="your oauth client id"
NEXT_PUBLIC_GOOGLE_OAUTH_CLIENT_SECRET="your oauth client secret"
NEXT_PUBLIC_GOOGLE_OAUTH_REDIRECT_URI="your oauth client redirect uri"
type GOOGLE_AUTH_KEYS = 
    | "client_id" 
    | "client_secret" 
    | "endpoint" 
    | "redirect_uri" 
    | "scopes";

export const oauth_google: Record<GOOGLE_AUTH_KEYS, string> = {
    client_id: process.env.NEXT_PUBLIC_GOOGLE_OAUTH_CLIENT_ID || "",
    client_secret: process.env.NEXT_PUBLIC_GOOGLE_OAUTH_CLIENT_SECRET || "",
    endpoint: "https://accounts.google.com/o/oauth2/v2/auth",
    redirect_uri: process.env.NEXT_PUBLIC_GOOGLE_OAUTH_REDIRECT_URI || "",
    scopes: "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
}

Add a button to your login page, which should be built with guidelines as provided by Google: Branding Guidelines.

Add an onClick event on the button to redirect users to Google's sign-in page.

const query = {
    client_id: oauth_google.client_id,
    redirect_uri: oauth_google.redirect_uri,
    response_type: "code",
    scope: oauth_google.scopes,
};
const url = new URL(oauth_google.endpoint);
url.search = new URLSearchParams(query).toString();
window.location.href = url.toString()

After the user completes the authentication flow, google will redirect to your provided redirect_uri in the .env file. Keep that page as a frontend page instead of an API route.


Now for the further authentication process, we will need to make API calls to our back-end server whenever the redirect_uri page loads where we will handle the data provided by Google after successful sign-in.

Note: In NextJS, you can leverage the getServerSideProps, to make API requests to the server for the further authentication process. For ReactJS make the API calls by calling in useEffect hook. Or if you are using any other framework, handle the API call as told in that.


In the search parameters of the returned page's URI, there is a parameter code, send that to your server's API route, (POST /oauth/google/verify here). Send this code in the request's body.

Backend

POST /oauth/google/verify

Create an API call to https://oauth2.googleapis.com/token, to get the id_token from the code we got from the request body.

const oauthRequest = {
    url: new URL("https://oauth2.googleapis.com/token"),
    params: {
        client_id: oauth_google.client_id,
        client_secret: oauth_google.client_secret,
        code: req.body.code,
        grant_type: "authorization_code",
        redirect_uri: oauth_google.redirect_uri,
    },
};
const oauthResponse = await axios.post(
    oauthRequest.url.toString(),
    null,
    { params: oauthRequest.params }
);
const oauthResponseData = oauthResponse.data;

In the oauthResponseData, there is a parameter, id_token. We will use this id_token to get the user details. To do this, we will install google-auth-library which is Google's officially supported Node.js client library for using OAuth 2.0 authorization and authentication with Google APIs, and then using the verifyIdToken method from the client to get the user details from the id_token.

Create an OAuth Client to verify the ID token.

import { OAuth2Client } from "google-auth-library";

const client = new OAuth2Client();

export const fetchUserFromIdToken = async (idToken: string) => {
    const ticket = await client.verifyIdToken({
        idToken: idToken,
        audience: oauth_google.client_id,
    });
    const payload = ticket.getPayload();
    return payload;
};

After you have got the user details from the id_token, return those details as the API response.

const user = await fetchUserFromIdToken(oauthResponseData.id_token);
return res
    .status(200)
    .json({ data: user, message: "Success" });

This user detail contains a lot of details, here is an example:

{
    "iss": "Issuer of the token", // https://accounts.google.com here,
    "azp": "The client_id of the authorized presenter",
    "aud": "Identifies the audience that this ID token is intended for. It must be one of the OAuth 2.0 client IDs of your application.",
    "sub": "The subject of the token. An identifier for the user, unique among all Google accounts and never reused.",
    "email": "User's email",
    "email_verified": "Boolean", // true usually
    "at_hash": "Access token hash. Provides validation that the access token is tied to the identity token.",
    "name": "Name from user's Google Account",
    "picture": "The URL of the user's Google account's avatar",
    "given_name": "First name from user's Google account",
    "family_name": "Last name from user's Google account",
    "locale": "The language locale of user's location", // 'en-GB'
    "iat": "The time the ID token was issued, represented in Unix time (integer seconds).", // for example, 1708416251
    "exp": "1. The time the ID token expires, represented in Unix time (integer seconds).", // for example, 1708419851
}

From here, you can use any details that you need to handle authentication in your project further.

Conclusion

This article demonstrates the use of Google OAuth for NodeJS applications.

Questions and feedback are most welcome. 😊