Implement Google OAuth in NestJS using Passport

Implement Google OAuth in NestJS using Passport

This article teaches you how to use Passport to implement Google OAuth in a NestJS application.

Introduction

Authentication is an essential part of most applications. Implementing authentication in your application depends on requirements specific to the application.

This article teaches you how to use Passport to implement Google OAuth in a NestJS application.

NestJS is a Node.js framework for building server-side applications. NestJS supports typescript out of the box. On the other hand, Passport is an authentication middleware for Node.js that supports over 500 authentication strategies, e.g., username and password, google, Facebook, etc.

Prerequisite

You need to have:

  • A basic understanding of JavaScript. TypeScript is preferred but not mandatory.
  • Basic knowledge of the NestJS framework.
  • A Google account — create one for free here.

Repository

Find the completed project on GitHub: github.com/folucode/google-oauth-app

Creating a NestJs Application

To get started, install Node.js if you don’t already have it installed. Download the version compatible with your Operating System and follow the installation instructions.

You can verify that Node.js is installed successfully by running the command below:

$ node -v
// v18.4.0 - node version printed to the terminal

Next, to scaffold a NestJS project using the Nest CLI (short for command line interface), run the commands below in your terminal:

$ npm i -g @nestjs/cli
$ nest new <project-name>

The commands above install NestJS CLI globally on your computer and creates a new project directory with your supplied project name. The created project directory has all the core NestJS starter files and modules.

To start the NestJS development server, run the command below in the project directory:

$ npm run start:dev

Creating an App on Google Console

Before using Google OAuth in your project, you must create an app on Google Cloud Console. Fill in the project name, click CREATE, and navigate to the app dashboard.

Click on the OAuth consent screen from the sidebar, select External and click CREATE.

Selecting the option External means any google account can use this app. On the next page, make sure only to fill the following as this is a test application:

  • Application Name
  • User support email
  • Developer contact information

Click SAVE AND CONTINUE. On the next page,

  1. Click ADD OR REMOVE SCOPES
  2. Select the first two options and click UPDATE to save the changes.
  3. Click SAVE AND CONTINUE and complete the subsequent steps.

Get API keys

Go to your app dashboard. At the top of the page, click on CREATE CREDENTIALS and select the OAuth client ID option. Follow the next steps:

  1. Select your application type, in this case, Web Application.
  2. Fill in the name field or go with the default name.
  3. Set the Authorized JavaScript origin and Authorized redirect URI fields. For this app, use localhost:3000 and localhost:3000/auth/google-redirect, respectively.
  4. Copy the Client ID and Client Secret or download it as JSON. The keys are always available on your app dashboard.

Setup Google OAuth

In the project directory, run the following commands:

npm i --save @nestjs/passport passport passport-google-oauth20 @nestjs/config
npm i -D @types/passport-google-oauth20

Create a new file in the src folder named google.strategy.ts. In the file, paste the following code:

import { PassportStrategy } from '@nestjs/passport';
import { Strategy, VerifyCallback } from 'passport-google-oauth20';
import { Injectable } from '@nestjs/common';

@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
  constructor() {
    super({
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      callbackURL: 'http://localhost:3000/auth/google-redirect',
      scope: ['email', 'profile'],
    });
  }
  async validate(
    accessToken: string,
    refreshToken: string,
    profile: any,
    done: VerifyCallback,
  ): Promise<any> {
    const { name, emails, photos } = profile;
    const user = {
      email: emails[0].value,
      firstName: name.givenName,
      lastName: name.familyName,
      picture: photos[0].value,
      accessToken,
      refreshToken,
    };
    done(null, user);
  }
}

In this file,

  1. We first import the necessary dependencies.
  2. The PassportStrategy class is a module in the ‘@nestjs/passport’ package. A class called GoogleStrategy extends the PassportStrategy. Note that every strategy class that uses Passport must extend the PassportStrategy class.
  3. In the super method, we instantiate the clientID, clientSecret, callbackURL, and scope properties in the constructor function.
    1. The clientID and clientSecret are your application‘s ID and Secret key from Google when you created the app.
    2. The callbackURL is the endpoint in your app google will redirect after authenticating a user.
    3. The scope is an array of the specific user information you want to get back from google.
  4. The validate method executes after Google returns the requested user information. In this method, you decide what to do with the user information returned by Google. You then return the result using the done method.

Store keys in environment variables

In the root of the project folder, create a .env file and put the Client ID and Client Secret keys in it.

GOOGLE_CLIENT_ID= <your-client-id>
GOOGLE_CLIENT_SECRET= <your-client-secret>

Create Google OAuth Guard

To use Google OAuth Strategy in the endpoints, you need a Guard Class that extends the AuthGuard class and specifies google as the strategy to use.

Now, in the src folder, create a google-oauth.guard.ts file and paste the following code in it:

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class GoogleOAuthGuard extends AuthGuard('google') {
  constructor(private configService: ConfigService) {
    super({
      accessType: 'offline',
    });
  }
}

Note: we specify accessType to be offline so that Google can return a refresh token after successful authentication.

Create Auth routes

Open the app.controller.ts file in the src folder and replace its content with the code below. In this code, there are two routes. The first initializes Google authentication, and the other is the callback Google calls to after authentication.

Also, notice that both routes use the GoogleOAuthGuard.

import { GoogleOAuthGuard } from './google-oauth.guard';
import { Controller, Get, Request, UseGuards } from '@nestjs/common';
import { AppService } from './app.service';

@Controller('auth')
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  @UseGuards(GoogleOAuthGuard)
  async googleAuth(@Request() req) {}

  @Get('google-redirect')
  @UseGuards(GoogleOAuthGuard)
  googleAuthRedirect(@Request() req) {
    return this.appService.googleLogin(req);
  }
}

Note: The googleLogin method doesn’t exist yet. You would get an error for that.

Return the user data after login

In the app.service.ts file, replace the contents with this code code:

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  googleLogin(req) {
    if (!req.user) {
      return 'No user from google';
    }

    return {
      message: 'User information from google',
      user: req.user,
    };
  }
}

The googleLogin method returns either a ‘No user from google’ error if the authentication failed or the user information if the authentication is successful.

Tie it all together

The app can’t know to use GoogleStrategy until you tell it. In the app.module.ts file, import the GoogleStrategy class and add it as a service in the providers array.

You also need to load the environment variables into the app. Do that by importing the ConfigModule class from the @nestjs/config” package and call its forRoot method in the imports array.

import { GoogleStrategy } from './google.strategy';
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [ConfigModule.forRoot()],
  controllers: [AppController],
  providers: [AppService, GoogleStrategy],
})
export class AppModule {}

Run the app using the command below and navigate to localhost:3000/auth to test the app

$ npm run start:dev

Conclusion

This article showed you how to implement Google OAuth sign-in using Passport in NestJS applications.

Resources

Connect

Feel free to connect with me across my social media handles