DEV Community

How to Implement Login with Google in Nest JS

Michael Owolabi on April 30, 2020

In this article, we are going to implement (OAuth) login with google in Nest JS. Coming from Express, implementing OAuth in Nest JS may seem not so...
Collapse
 
egormkn profile image
Egor Makarenko • Edited

@imichaelowolabi, done callback should not be used in validate method, because it is invoked internally by nestjs/passport. It is better to just return user object from validate:

async validate (accessToken: string, refreshToken: string, profile: any): Promise<any> {
    const { name, emails, photos } = profile
    const user = {
      email: emails[0].value,
      firstName: name.givenName,
      lastName: name.familyName,
      picture: photos[0].value,
      accessToken
    }
    return user;
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
kaivanmehta14 profile image
Kaivan Mehta • Edited

Thanks for this detailed explanation.

I am working on angular + nestJs project, which are configured at different ports (localhost:4200 and localhost:8080). I am trying to request from angular to nestJs.

But it is not redirected on CallbackUrl, rather it is giving me some CORS error. However,
I have put, localhost:8080 and my endpoint at authorized URI and also configured app.enablecors() in main.js

I am having this error instead,
Access to XMLHttpRequest at 'https://accounts.google.com/o/oauth2/v2/auth?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fv1%2Fauth%2Fredirect&scope=email%20profile&client_id=XXX' (redirected from 'localhost:8080/v1/auth') from origin 'localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

....
However, after clicking that redirection link manually, it is working properly. Did I miss anything?
Any help is appreciated.

Collapse
 
imichaelowolabi profile image
Michael Owolabi

Hi Kaivan,

Thanks for the feedback. As per the error you're getting, this simply has to do with the "Authorized Javascript origin" and the redirect URI. Kindly crosscheck these two values from your google console to ensure they are the correct endpoints for your frontend and backend app.

Collapse
 
kaivanmehta14 profile image
Kaivan Mehta

Thanks for your reply,
As I am requesting from angular and default port is 4200, I have given localhost:4200 in authorized JavaScript uri and I am redirecting it to the backend, thus I have given localhost:8080 over there in the redirect uri.

Kindly can refer this picture.
dev-to-uploads.s3.amazonaws.com/up...

Thread Thread
 
wassilabeny profile image
WassilaBen

Hello Kaivan,
Did you find a solution? cuz I have the same problem. I'm using nextJs and I have a cors problem even if I've enabled it

Thread Thread
 
joe105598055 profile image
Joe

Hello WassilaBen,
Did you find a solution? cuz I have the same problem too...😅

Collapse
 
absdev profile image
Abubakar

In your Nest Js app open your main.ts add this
after creating an instance of nest application
Image description
Change the origin to localhost:8080

Collapse
 
mehdiparastar profile image
mehdiparastar

I've faced the same issue, please share your solution if you find finally.

Collapse
 
humayunkabir profile image
Humayun Kabir • Edited

I am getting the following error after applying this method.

node_modules/apollo-server-core/dist/ApolloServer.d.ts:5:8 - error TS1259: Module '"/Users///node_modules/@types/ws/index"' can only be default-imported using the 'esModuleInterop' flag

5 import WebSocket from 'ws';
~~~~~~~~~

node_modules/@types/ws/index.d.ts:270:1
270 export = WebSocket;
~~~~~~~~~~~~~~~~~~~
This module is declared with using 'export =', and can only be used with a default import when using the 'esModuleInterop' flag.

Collapse
 
humayunkabir profile image
Humayun Kabir

Sorry, this Error is not related to this module.

Collapse
 
imichaelowolabi profile image
Michael Owolabi

Thanks for the clarification Humayun

Collapse
 
zicjin profile image
zicjin • Edited

InternalOAuthError: Failed to obtain access token
at GoogleStrategy.OAuth2Strategy._createOAuthError (C:\Work_test\google-oauth-nestjs\node_modules\passport-oauth2\lib\strategy.js:408:17)

i just test github.com/iMichaelOwolabi/google-...

I got the correct google account login interface and logged in successfully, but reported an error when calling back the localhost:3000/google/redirect?cod....

How can I tell if I have a problem with my google project settings or JS code?

Collapse
 
imichaelowolabi profile image
Michael Owolabi

Hi Zicjin, try to reconfirm your redirect URL both on google and in your code as well as the scope. Sometimes when asking for non-existent scope, you may get such error.
Please let me know how it goes.

Collapse
 
smasala profile image
Steven Masala

For others: double check your client ID and secret for the typos.

Collapse
 
zlfikar24 profile image
zlfikar24

I am getting following error when I ran npm run start

src/app.controller.ts:1:49 - error TS2307: Cannot find module '@nestjs/common' or its corresponding type declarations.

1 import { Controller, Get, Req, UseGuards } from '@nestjs/common';
~~~~~~~~
src/app.module.ts:1:24 - error TS2307: Cannot find module '@nestjs/common' or its corresponding type declarations.

1 import { Module } from '@nestjs/common';
~~~~~~~~
src/app.service.ts:1:28 - error TS2307: Cannot find module '@nestjs/common' or its corresponding type declarations.

1 import { Injectable } from '@nestjs/common';
~~~~~~~~
src/google.strategy.ts:5:28 - error TS2307: Cannot find module '@nestjs/common' or its corresponding type declarations.

5 import { Injectable } from '@nestjs/common';
~~~~~~
src/google.strategy.ts:14:17 - error TS2580: Cannot find name 'process'. Do you need to install type definitions for node? Try npm i --save-dev @types/node.
14 clientID: process.env.GOOGLE_CLIENT_ID,
~~
~
src/google.strategy.ts:15:21 - error TS2580: Cannot find name 'process'. Do you need to install type definitions for node? Try npm i --save-dev @types/node.
15 clientSecret: process.env.GOOGLE_SECRET,
~~
~~~
src/main.ts:1:29 - error TS2307: Cannot find module '@nestjs/core' or its corresponding type declarations.

1 import { NestFactory } from '@nestjs/core';
~~~~~~
../node_modules/@nestjs/passport/dist/auth.guard.d.ts:1:29 - error TS2307: Cannot find module '@nestjs/common' or its corresponding type declarations.

1 import { CanActivate } from '@nestjs/common';
~~~~~~~~
../node_modules/@nestjs/passport/dist/interfaces/auth-module.options.d.ts:1:38 - error TS2307: Cannot find module '@nestjs/common/interfaces' or its corresponding type declarations.

1 import { ModuleMetadata, Type } from '@nestjs/common/interfaces';
~~~~~~~~~~~
../node_modules/@nestjs/passport/dist/passport.module.d.ts:1:31 - error TS2307: Cannot find module '@nestjs/common' or its corresponding type declarations.

1 import { DynamicModule } from '@nestjs/common';
~~~~~~~~
../node_modules/dotenv/types/index.d.ts:2:23 - error TS2688: Cannot find type definition file for 'node'.

2 ///
~~~~
../node_modules/dotenv/types/index.d.ts:23:17 - error TS2580: Cannot find name 'Buffer'. Do you need to install type definitions for node? Try npm i --save-dev @types/node.

Collapse
 
fahridevz profile image
Muhammad Ali Fahri

You miss install @nestjs/core and @nestjs/common, and don't forget to install @types/node an dev dependencies 😅

Collapse
 
cziermann profile image
cziermann

Thx for the Article,

But i have an problem with this.
I am using graphl with this first i had an problem that i had not an Response object i fixed this with

  getResponse(context: ExecutionContext) {
    const ctx = GqlExecutionContext.create(context);
    const res = ctx.getContext().res;
    return res;
  }
Enter fullscreen mode Exit fullscreen mode

but now i dont get any real response at all.

i debuged alot and found out that the google-strategy never calls success, error or fail to realy end the transaction.

the only function that calls these is self._oauth2.getOAuthAccessToken

but this gets also never called.
i also could not find where this function is called at all i search for this function in my node_modules folder and only found the declaration.

Thx for Help

Greetings

Collapse
 
cziermann profile image
cziermann

i think i found an fix for that.
the problem is that the oauth2 strategy is looking for req.query.code

so one fix is just write the token in the req.query.code in the getRequest function

  getRequest(context: ExecutionContext) {
    const ctx = GqlExecutionContext.create(context);
    const req = ctx.getContext().req;
    req.query.code = tokenExtractor(req);
    return req;
  }
Enter fullscreen mode Exit fullscreen mode
Collapse
 
cziermann profile image
cziermann

did think that this fixes the issue but no.
does anyone has experience with nestjs + passport (google) and graphql?

Collapse
 
ijazsharif profile image
Muhammad Ijaz

I am facing this error when login with google
Any body face this issue and resolved it?

{"statusCode":404,"message":"Cannot GET /google/redirect?code=4/0AX4XfWg_370s0nW_gRVvaBql7g8u0TPVmgoLkly22yTxM7JIccz0-tFAht0ELvLn6BOQ7A&scope=email%20profile%20https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile%20openid&authuser=0&prompt=consent","error":"Not Found"}

Collapse
 
imichaelowolabi profile image
Michael Owolabi

Hi,
From the error message you shared, it seems the redirect endpoint doesn't exist or doesn't have use the right HTTP method in your app.
You should confirm that you have the google redirect endpoint setup in your application with the right GET http method and try again

Collapse
 
microchip78 profile image
microchip78

How to load ClientID, ClientSecret and CallbackURL from database instead of loading from env in this example. Here is the code where you are loading it from env.

constructor() {
    super({
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_SECRET,
      callbackURL: 'http://localhost:3000/google/redirect',
      scope: ['email', 'profile'],
    });
  }
Collapse
 
thisismydesign profile image
thisismydesign

Hey Michael, this is a nice guide to get started, but isn't it incomplete? Every time I visit a guarded page I'm redirected to Google's OAuth page again. Shouldn't there be a step to use the received accessToken? Or how should the server handle subsequent requests?

Collapse
 
imichaelowolabi profile image
Michael Owolabi • Edited

Hello @thisismydesign thank you for your kind words.

About your question, yes the received access token should be used but the use will differ across various applications which will be determined by the use case in the application and the developer. My aim in writing this article is just to show to authenticate with Google OAuth in a Nest application.

Collapse
 
eybel profile image
Abel Simon • Edited

Hi there! thanks for the article, but I keep having this error that I can't fix. I get:

"Access blocked: Authorization Error
Missing required parameter: scope
Error 400: invalid_request"

I do have the scope parameter, and I have my callback URL also, any other ideas? I don't know what else to try.


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

@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
  constructor(private readonly configService: ConfigService) {
    super({
      clientID: configService.get<string>('GOOGLE_CLIENT_ID'),
      clientSecret: configService.get<string>('GOOGLE_CLIENT_SECRET'),
      callbackURL: configService.get<string>('GOOGLE_CALLBACK_URL'),
      scope: ['profile', 'email'],
    });
  }

  async validate(
    req: any,
    accessToken: string,
    refreshToken: string,
    profile: Profile,
    done: VerifyCallback,
  ): Promise<any> {
    // Validate or create user based on Google profile
    const userProfile = {
      email: profile._json.email,
      firstname: profile._json.given_name,
      lastname: profile._json.family_name,
      picture: profile._json.picture,
    };

    // You can access the request object using req.user if needed

    done(null, userProfile);
  }
}

Enter fullscreen mode Exit fullscreen mode
Collapse
 
upsetmania profile image
Ups3t • Edited

hi, thanks for the code works perfectly;).
on the other hand I have a question when the page reloads I have an

Error Server:
error: TokenError: Bad Request
at GoogleStrategy.OAuth2Strategy.parseErrorResponse,

Page:
statusCode: 500
message: "Internal server error",

how would you solve the problem? thank you so much.

Collapse
 
joeberetta profile image
Joe Beretta

Hi, have same issue, could u help with solving this error?

Collapse
 
soumikchakrab11 profile image
Soumik Chakraborty • Edited

the refreshtoken comes as undefined and even the accestype offline parameter wont solve it. Please Help

Collapse
 
mercurialuroboros profile image
MercurialUroboros

Does anybody know why after login I get this

Alt text of image

Collapse
 
knnwulf profile image
Rodolfo

Well done, very useful.

Collapse
 
imichaelowolabi profile image
Michael Owolabi

Thanks Rodolfo, glad it helped.

Collapse
 
kuetabby profile image
Ivan

this one's neat!! but can u make the graphql version of this ??

Collapse
 
barinbritva profile image
Barin Britva

Thanks a lot for the article!

Collapse
 
imichaelowolabi profile image
Michael Owolabi

You're welcome Barin. Glad it helped

Collapse
 
danoseun profile image
'Seun Somefun

Nice piece. Keep it up!!!

Collapse
 
imichaelowolabi profile image
Michael Owolabi

Thank you man

Collapse
 
rahulto profile image
rahulto

Good article

Collapse
 
subu profile image
Subramanya Shenoy • Edited

Hi Any tutorial u suggest to integrate with this stack :
NestJS + GraphQL + passport-google-oauth20

completely hit the roadblock on this one. 😢

Collapse
 
douglashfonseca profile image
DouglasHFonseca

Hi I'm looking for a way to integrate google calendar using nestjs. However I am confused with authentication using OAuth2.

Collapse
 
olojamuyiwa profile image
OlojaMuyiwa

Nice content boss. it was really helpful

Collapse
 
ruchitgandhineu profile image
Ruchit Gandhi

Hello,

Objective **: To get **refresh_token.

Can you please suggest on how to add
access_type=offline
approval_prompt=force

as parameter for the OAuth url?

Collapse
 
tausvels profile image
Tausif Ezaj Khan

Hi Michael,
How to store the user info in cookie session in Nest? Could you point me to the right direction?

Collapse
 
villekaa profile image
Goffy • Edited

Hi, I think this example is for jwt.
If you want to use a cookie session you can add a route guard that creates a session cookie for you.

Controller

    @Post('/signin')
    @UseGuards(LoginGuard)
    signin(@Request() req: IUserRequest): void {
        console.log(`@AuthController ${JSON.stringify(req.user)}`)
    }
Enter fullscreen mode Exit fullscreen mode

Guard

import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class LoginGuard extends AuthGuard('local') {
    async canActivate(context: ExecutionContext): Promise<boolean> {
        const result = (await super.canActivate(context)) as boolean;
        const request = context.switchToHttp().getRequest();
        await super.logIn(request);
        return result;
    }
}
Enter fullscreen mode Exit fullscreen mode

At this point you have a cookie and you can redirect the user back to front end ( if you have one ) and do another request to ask user information.

Collapse
 
oleksandrkrupko profile image
Oleksandr Krupko

Man, thanks a lot for the great article! I've set up login using google in 10 mins thanks to it!

Collapse
 
heyrohit profile image
Rohit Gupta

Works Perfectly

Collapse
 
ayobamy profile image
Ahmed Olawale

Thanks man.
Super useful and straith forward.
No errors encountered too.
Thanks a lot.

Collapse
 
amouratoglou profile image
Agustin Mouratoglou

5 mins setup and my app now supports google login.
i'm trying to make now authentication to connect to google calendar api and bring calendar data.
any hint?

Collapse
 
oybekberdiyev profile image
Oybek Berdiyev

how to get phone number