You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nest.js集成Google OAuth遇重定向CORS错误问题及解决

Nest.js + Next.js Google OAuth 跨域问题及解决方案

问题场景

  • Nest.js 后端运行在 5000 端口,Next.js 前端运行在 3000 端口
  • Google Cloud Console 已配置源 URI 和重定向 URI 为 http://localhost:3000

前端错误

  • 从 http://localhost:3000 访问经 http://localhost:5000/auth/google 重定向的 Google OAuth 地址时,触发 CORS 策略拦截,提示缺少 Access-Control-Allow-Origin 标头
  • Google OAuth 地址的 GET 请求返回 net::ERR_FAILED 302 (Found)

相关 Nest.js 代码片段

Google OAuth 策略

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);
  }
}

Google OAuth 守卫

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

Auth 控制器

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');
  }
}

Auth 服务

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,
      );

      // 用户已存在但未绑定当前登录方式
      if (!providerExists) {
        foundUser.authenticationProviders.push(user.authenticationProviders[0]);
        await foundUser.save();
      }

      userPayload = foundUser;
      accessTokenPayload = foundUser._id.toString();
    } else {
      // 用户不存在则创建新用户
      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,
    });
  }
}

补充说明

直接在浏览器访问 http://localhost:5000/auth/google 可正常完成授权并获取用户信息,但通过 Next.js 发起请求时立即触发 CORS 错误。

解决方案

  1. 调整 Google 控制台配置

    • 源 URI 保持为 http://localhost:3000
    • 重定向 URI 修改为 http://localhost:3000/auth/google-redirect(可根据实际路由调整)
  2. 前端登录逻辑修改
    放弃 AJAX 请求方式,直接通过页面跳转发起授权请求,代码示例:

    const handleLogin = async (method: string) => {
      window.location.replace(`${apiBaseUrl}/auth/${method.toLowerCase()}`);
    };
    
  3. 完整流程说明

    • 调用上述方法后,页面跳转到 Google 授权界面
    • 用户完成登录授权后,Google 会将页面重定向到 http://localhost:3000/auth/google-redirect?code=<授权码>
    • 前端在该页面中,携带 URL 查询参数请求后端的 http://localhost:5000/auth/google-redirect 接口
    • 后端通过授权码完成身份验证,返回用户详情并设置登录 Cookie

内容的提问来源于stack exchange,提问作者Prashant Patel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 04:46:35