Search code examples
node.jsnestjspassport.jspassport-facebookpassport-google-oauth

use dynamic passport strategy nestjs with multiple clientID and clientSecret


I want to implement passport google and facebook strategy using multiple keys for different apps, like get clientID or something in req params and select google clientID and clientSecret from DB on base of given param i.e users of one application can authenticate using a specific clientID and clientSecret, want to implement something like this but not sure how to do it in nestjs as iam fairly new to nestjs. https://medium.com/passportjs/authenticate-using-strategy-instances-49e58d96ec8c

 // GoogleStrategy code    
@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
  constructor() {
    super({
      clientID: '', // dynamic key from multiple type of application
      clientSecret: '',  // dynamic key from multiple type of application
      callbackURL: '',               // url from user request or hardcoded
      scope: ['email', 'profile'],  //hardcoded or data from request
    });
  }
  async validate(
    accessToken: string,
    refreshToken: string,
    profile: any,
    done: VerifyCallback,
  ): Promise<any> {
    const { name, emails, photos } = profile;
    done(null, profile);
  }
}






 // Goole Controler
   @Controller('google-auth')
export class GoogleAuthController {
  constructor(private readonly googleAuthService: GoogleAuthService) {}

  @Get('login')
  @UseGuards(AuthGuard('google'))
  login(@Param('appID') appID: string, @Req() req) {
    // Query params to switch between two app type
    // e.g app1ID=123132323 or app2ID=2332323      
    //But what now? The strategy get initiated inside the module
  }

  @Get('redirect')
  @UseGuards(AuthGuard('google'))
  redirect(@Req() req) {}

  @Get('status')
  status() {}

  @Get('logout')
  logout() {}
}



//GoogleModule
@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService, GoogleStrategy], //How to use this strategy with multiple 
                                            // clientID and clientSecret on base of a
                                             // param
})
export class AppModule {}

Solution

  • We can do it by creating custom guard and use that in out controller.ts file

    controller.ts file

      @UseGuards(FacebookGuard, AuthGuard('facebook'))
    

    FacebookGuard.ts file

    import { CanActivate, ExecutionContext } from '@nestjs/common';
        import { ClientAppService } from '../clientApp/clientApp.service';
        import { Injectable } from '@nestjs/common';
        import { FacebookStrategy } from '../auth/facebook.strategy';
        
        type Provider = {
          providerType: string;
          providerKey: string;
          providerSecret: string;
          callBackUrl: string;
        };
        
        @Injectable()
        export class FacebookGuard implements CanActivate {
          /**
           * Constructor
           * @param {ClientAppService} clientAppService
           */
          constructor(readonly clientAppService: ClientAppService) {}
        
          async canActivate(context: ExecutionContext): Promise<boolean> {
            const request = context.switchToHttp().getRequest();
            const data = await this.clientAppService.getProviderDetails(
              request.query.appKey,
              'facebook',
            );
            const providerData = data.providerId as any;
             new FacebookStrategy(
              providerData.providerKey,
              providerData.providerSecret,
              providerData.callBackUrl + '/' + request.query.appKey + 
               '/callback',
            );
            return true;
          }
        }