1

I'm currently facing an issue with my Nest.js application that utilizes Google OAuth for authentication. The Nest.js backend is running on port 5000, and the frontend (Next.js) is running on port 3000. I've set up the Google Cloud Console with the correct origin URI and redirect URI, both set to "http://localhost:3000".

I am facing below error in frontend Next.js

Here's a snippet of my Nest.js Auth Controller, Google OAuth Strategy, and Google OAuth Guard:

// Nest.js google oauth strategy

export class GoogleOAuthStrategy extends PassportStrategy(Strategy, 'google') {
  constructor(private readonly configService: ConfigService) {
    super({
      clientID: configService.get('googleOAuthClientId'),
      clientSecret: configService.get('googleOAuthClientSecret'),
      callbackURL: configService.get('googleOAuthCallbackURL'),
      scope: ['email', 'profile'],
    });
  }

  async validate(
    accessToken: string,
    refreshToken: string,
    profile: Profile,
    done: VerifyCallback,
  ): Promise<any> {
    const { id, name, emails, photos } = profile;

    const user: User = {
      type: 'individual',
      email: emails[0].value,
      firstName: name.givenName,
      lastName: name.familyName,
      picture: photos[0].value,
      authenticationProviders: [{ name: 'google', id }],
    };

    done(null, user);
  }
}

// Nest.js google oauth guard

export class GoogleOAuthGuard extends AuthGuard('google') {
  constructor() {
    super({
      accessType: 'offline',
    });
  }
}

// Nest.js auth controller

export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Get('google')
  @UseGuards(GoogleOAuthGuard)
  async googleAuth() {
    return HttpStatus.OK;
  }

  @Get('google-redirect')
  @UseGuards(GoogleOAuthGuard)
  googleAuthRedirect(
    @Req() req: Request,
    @Res({ passthrough: true }) res: Response,
  ) {
    return this.authService.login(req, res, 'google');
  }
}

// Nest.js auth service

export class AuthService {
  constructor(
    private readonly configService: ConfigService,
    private readonly usersService: UsersService,
    private readonly jwtService: JwtService,
  ) {}

  async login(req: Request, res: Response, provider: Provider): Promise<void> {
    const user = req.user as User;

    if (!user) {
      throw new NotFoundException(`No user from ${provider}`);
    }

    let userPayload: User;
    let accessTokenPayload: string;

    const foundUser = await this.usersService.findByEmail(user.email);

    if (foundUser) {
      const providerExists = foundUser.authenticationProviders.some(
        (provider) => provider.name === user.authenticationProviders[0].name,
      );

      // User found with different provider
      if (!providerExists) {
        foundUser.authenticationProviders.push(user.authenticationProviders[0]);
        await foundUser.save();
      }

      userPayload = foundUser;
      accessTokenPayload = foundUser._id.toString();
    } else {
      // Save user to mongodb if it does not exists already
      const newUser = await this.usersService.create(user);

      userPayload = newUser;
      accessTokenPayload = newUser._id.toString();
    }

    const accessToken = this.jwtService.sign({ id: accessTokenPayload });

    res.cookie('CARDTRIKA_ACCESS_TOKEN', accessToken, {
      maxAge: this.configService.get('cookieMaxAge'),
      httpOnly: true,
    });

    res.status(HttpStatus.OK).json({
      statusCode: HttpStatus.OK,
      success: true,
      message: 'User information',
      data: userPayload,
    });
  }
}

If I make get request to http:localhost:5000/auth/google directly from the browser it redirects to google api > consent screen and then I can login through gmail account and finally can get user information successfully. But the same is not happening with Next.js. As soon as I make request it says CORS error.

SOLUTION:

So, basically there are two endpoints. /auth/google and /auth/google-redirect. And on google console, origin-uri should be http://localhost:3000 and redirect-uri should be set to http://localhost:3000/auth/google-redirect(wherever we want to manage in frontend).

Now we have to request to /auth/google from origin localhost:5000. So I made it as below,

const handleLogin = async (method: string) => {
 window.location.replace(`${apiBaseUrl}/auth/${method.toLowerCase()}`);
};

So it opens up consent screen and after logging in it will redirect to http://localhost:3000/auth/google-redirect?code=<some-code-and-other-things> and it frontend I have managed this url page and made another request to http://localhost:5000/auth/google-redirect with queryString. So in backend now, the google strategy know it's authenticated due to the known code it provided and give us user's details.

1 Answer 1

0

will be happy to be able to help you

I think the problem is that you haven't set the cors domains in your backend, to allow to receive request from the other domains.

In your bootstrap, write app.enableCors({origin: corsDomains}), where the corsDomains is the array of the domains, (e.g. http://localhost:3000)

Sign up to request clarification or add additional context in comments.

1 Comment

Hey, thanks for the reply. Actually I was facing the issue due to incorrect flow. I have written the solution in my question. And yes, you are right. also need to enable cors.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.