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

Nest.js中Github OAuth2与JWT共存时req.user未定义问题

Nest.js passport-github2认证回调中req.user为undefined的问题

我已在Nest.js中实现JWT认证,当前使用passport-github2做Github第三方认证时遇到问题:Github认证的回调接口里,req.user始终为undefined。

通过日志确认,github.strategy.ts中的validateGithubUser方法已成功找到或创建user对象,但该对象并未传递到请求中。我需要在githubAuthCallback方法中获取到非undefined的req.user。

以下是相关代码:

github.strategy.ts

import { Injectable, Logger } from '@nestjs/common';
import { AppConfig } from '@/lib/config/config.provider';
import { PassportStrategy } from '@nestjs/passport';
import {
  Strategy,
  Profile,
  StrategyOptionsWithRequest,
} from 'passport-github2';
import { AuthenticationService } from '../authentication.service';
import { Request } from 'express';

type VerifyCallback = (error: any, user?: any, info?: any) => void;

const githubOptions: StrategyOptionsWithRequest = {
  clientID: AppConfig.authentication.GITHUB_CLIENT_ID,
  clientSecret: AppConfig.authentication.GITHUB_CLIENT_SECRET,
  callbackURL: AppConfig.authentication.GITHUB_CALLBACK_URL,
  passReqToCallback: true,
  scope: ['user:email'],
};

@Injectable()
export class GithubStrategy extends PassportStrategy(Strategy, 'github') {
  private readonly logger = new Logger(GithubStrategy.name);
  constructor(private readonly authService: AuthenticationService) {
    super(githubOptions);
  }

  async validate(
    req: Request,
    accessToken: string,
    _refreshToken: string,
    profile: Profile,
    done: VerifyCallback,
  ): Promise<any> {
    try {
      console.log('GitHub Profile:', profile);
      const user = await this.authService.validateGithubUser(profile);
      console.log('User object from AuthService:', user);
      if (!user) {
        console.log('No user found');
        return done(null, false);
      }
      console.log('User object passed to done callback:', user);
      return done(null, user);
    } catch (err) {
      this.logger.error('Failed to validate github authentication', {
        error: err,
      });
      throw new Error(err);
    }
  }
}

github.guard.ts

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

@Injectable()
export class GithubAuthGuard extends AuthGuard('github') {
  canActivate(context: ExecutionContext) {
    return super.canActivate(context);
  }

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  handleRequest(err, user, _info) {
    if (err || !user) {
      throw err || new UnauthorizedException();
    }
    return user;
  }
}

authentication.module.ts

import { Global, Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { JwtModule } from '@nestjs/jwt';
import { AuthenticationController } from './authentication.controller';
import { AuthenticationService } from './authentication.service';
import { LocalStrategy } from './strategies/local.strategy';
import { AccessTokenJwtStrategy } from './strategies/access-token.strategy';
import { AppEventHandler } from '@/common/events/app.events';
import { GithubStrategy } from './strategies/github.strategy';

@Global()
@Module({
  imports: [
    PassportModule.register({
      defaultStrategy: ['jwt', 'github'],
      session: false,
    }),
    JwtModule.register({}),
    EventEmitterModule.forRoot(),
  ],
  controllers: [AuthenticationController],
  providers: [
    AuthenticationService,
    LocalStrategy,
    AccessTokenJwtStrategy,
    GithubStrategy,
    AppEventHandler,
  ],
  exports: [AuthenticationService],
})
export class AuthenticationModule {}

auth.controller.ts

@UseGuards(GithubAuthGuard)
@Get('/auth/github')
public githubAuth() {}

@UseGuards(GithubAuthGuard)
@Get('/auth/github/callback')
public async githubAuthCallback(@Req() req: Request) {
  const user = req.user;
  console.log('User object in controller:', user);
  if (!user) {
    return { message: 'Authentication failed' };
  }
  return { message: 'Authentication successful', user: user };
}

问题排查与解决

1. 修复Passport模块配置冲突

Github OAuth2认证依赖临时会话存储state参数,你当前关闭了session会导致认证状态丢失,进而无法传递user对象。修改PassportModule配置:

// authentication.module.ts
PassportModule.register({
  defaultStrategy: 'jwt', // 默认用JWT,Github认证通过守卫单独指定
  session: true, // 开启session支持Github OAuth流程
}),

2. 修正Validate方法的错误处理

validate方法中直接抛出异常会中断Passport流程,需改用done回调传递错误:

// github.strategy.ts
async validate(
  req: Request,
  accessToken: string,
  _refreshToken: string,
  profile: Profile,
  done: VerifyCallback,
): Promise<any> {
  try {
    console.log('GitHub Profile:', profile);
    const user = await this.authService.validateGithubUser(profile);
    console.log('User object from AuthService:', user);
    if (!user) {
      console.log('No user found');
      return done(null, false);
    }
    console.log('User object passed to done callback:', user);
    return done(null, user);
  } catch (err) {
    this.logger.error('Failed to validate github authentication', {
      error: err,
    });
    // 用done回调传递错误,而非抛出异常
    return done(err, null);
  }
}

3. 启用Express会话中间件

Nest.js默认不包含会话中间件,需手动安装配置:

  1. 安装依赖:
npm install express-session
  1. 在main.ts中添加中间件:
import * as session from 'express-session';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // 会话中间件需放在其他中间件之前
  app.use(
    session({
      secret: 'your-secure-secret-key', // 替换为安全密钥
      resave: false,
      saveUninitialized: false,
      cookie: { secure: process.env.NODE_ENV === 'production' }, // 生产环境启用HTTPS
    }),
  );
  
  await app.listen(3000);
}
bootstrap();

4. 验证回调URL一致性

检查Github开发者后台配置的回调URL,确保与AppConfig.authentication.GITHUB_CALLBACK_URL完全一致(包括协议、路径、端口)。

完成以上修改后重启服务,测试Github认证流程即可在回调接口中获取到req.user。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 03:50:55