Implementing Sign In with Google in NodeJS (without third-party libraries)
Tags: oauth, javascript, tutorial, typescript
Published: 2024-02-20 15:12:35

## Introduction

Recently I was building a project in [NextJS](https://nextjs.org/) in which I needed to implement 'Sign in with Google' without third-party libraries like [next-auth](https://next-auth.js.org/), or [passport](https://www.passportjs.org/). 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](https://console.cloud.google.com).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mx13grrwi8srrki1nged.png)

2. Click on create a new project.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/meksusep4oqm0z09m1k5.png)

3. Select the organization and click on Create Project.
4. Select the recently made project from the above corner.
5. Go to `APIs and Services` and select `OAuth consent screen` from the left sidebar.
6. Give your App a name that will be seen by the end users and the support email users can contact.
7. Add a logo for your app, prescribed `120x120` dimensions.
8. Add your product's home page, and other links as asked.
9. Add the domain of your application in the `Authorized Domains` section.
10. Go to the next screen and select the `userinfo.email` and `userinfo.profile` scope.
11. Go on to the next screen and add a couple or more email addresses to test your app with.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bq452t55wokyyi5qx3j0.png)

12. After all the steps, go back to the dashboard.
13. Go on to the credentials screen, from `Create credentials`, select `OAuth Client ID`, and select web application.
14. 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`
15. 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.
	3. The link to Google Developers Playground, `https://developers.google.com/oauthplayground`.
	4. 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hodiccpwxl6k1b6kdiyn.png)

16. 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

```shell
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"
```


```typescript
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](https://developers.google.com/identity/branding-guidelines).

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

```typescript
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.

```typescript
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 use the `verifyIdToken` method from the client to get the user details from the `id_token`.

Create an OAuth Client to verify the ID token.

```typescript
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.

```typescript
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:

```json
{
	"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.  😊