Google login with PassportStrategy in NestJS backend not working called by React WebApp

Viewed 3089

I've implemented Google login with PassportStrategy in NestJS backend (NestJS backend development is based on this guide: https://medium.com/@nielsmeima/auth-in-nest-js-and-angular-463525b6e071).

@Get('google')
@UseGuards(AuthGuard('google'))
googleLogin()
{
    // initiates the Google OAuth2 login flow
}

@Get('google/callback')
@UseGuards(AuthGuard('google'))
googleLoginCallback(@Req() req, @Res() res)
{
    // handles the Google OAuth2 callback
    const jwt: string = req.user.jwt;
    if (jwt)
        return res.status(HttpStatus.OK).cookie('jwt', jwt, {
            httpOnly: true
        }).redirect('/');
    else 
        res.redirect('http://localhost:4200/login/failure');
}

With @UseGuards(AuthGuard('google')) should start the google login:

super({
        clientID    : 'XXX',     // <- Replace this with your client id
        clientSecret: 'XXX', // <- Replace this with your client secret
        callbackURL : 'http://myapp.herokuapp.com/auth/google/callback',
        passReqToCallback: true,
        scope: ['profile']
    })

If I put the call auth/google in the address bar of a browser it works.

If I call the method auth/google from a React WebApp it doesn't work and the network requests are those in the screenshot. I've no data in response.

App Requests

Google Detail

It seems that the callback doesn't start, but "passReqToCallback: true" works.

React frontend request:

fetch("http://<url>/auth/google", {
  method: "GET",
  mode: 'no-cors',
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json'
  }
})
.then(
  (result) => {
        if (result) {
          history.push({
            pathname: '/home'
          });
        }
  },
  (error) => {
    
  }
);

Backend logs when login goes well:

2021-02-18T10:34:02.724706+00:00 heroku[router]: at=info method=GET path="/auth/google/" host=myapp.herokuapp.com request_id=310ac45a-3657-4c5f-96dc-f0a1350429db fwd="93.146.33.170" dyno=web.1 connect=1ms service=10ms status=302 bytes=371 protocol=http

2021-02-18T10:34:03.321194+00:00 app[web.1]: {

2021-02-18T10:34:03.321209+00:00 app[web.1]: id: 'XXX',

2021-02-18T10:34:03.321210+00:00 app[web.1]: displayName: 'XXX',

Backend logs when login goes bad:

2021-02-18T10:36:29.284759+00:00 heroku[router]: at=info method=GET path="/auth/google" host=myapp.herokuapp.com request_id=2f56f65b-9236-4710-b029-dd973ec0c9a3 fwd="54.187.137.25" dyno=web.1 connect=0ms service=3ms status=302 bytes=371 protocol=http

nothing else

Any ideas? Could you help me?

Thank you.

1 Answers

The way you implemented google log in would be useful for a full stack app. Since you are using a separate react app (let's call it SPA) you need to make a few changes. First, you don't need the /google endpoint in your back end. That part should be done by the SPA. After the user logs in with google it should redirect him to the SPA again. This redirect will give you the user info and an access token in the query parameters.

Only then do you forward that information to the /google/callback route in your back end. You already have google token verification with the google auth guard so the only thing left is to use the information that google gave you to log in the user. I guess in your case that means generating a JWT. Do not use the token that google provided. Generate your own JWT.

From google documentation:

If you use Google Sign-In with an app or site that communicates with a backend server, you might need to identify the currently signed-in user on the server. To do so securely, after a user successfully signs in, send the user's ID token to your server using HTTPS. Then, on the server, verify the integrity of the ID token and use the user information contained in the token to establish a session or create a new account.

Related