首页 > 解决方案 > 如何在 Nestjs facebook 策略回调 url 中传递动态参数

问题描述

如何在 facebook 登录回调 url 中传递一些动态参数?

我有不同类型的用户(由“类型”参数区分)使用 facebook 登录进行注册。我已经使用 passport-facebook 创建了一个 facebook 身份验证策略,效果很好。

但是在身份验证之后,当回调 url 被调用时,我需要知道哪种类型的用户请求注册。

我猜我可以在定义回调 url 时传递一个参数

像这样的东西

http://localhost:3000/auth/facebook/callback/type1 http://localhost:3000/auth/facebook/callback/type2

如何将动态值传递到 FacebookStrategy?

或者有什么可能的解决方法来实现这一目标?

// PassportStrategy.ts

@Injectable()
export class FacebookStrategy extends PassportStrategy(Strategy) {
    constructor() {
        super({
            clientID: 'MYID',
            clientSecret: 'MYSCRET',
            callbackURL: "http://localhost:3000/auth/facebook/callback",
            profileFields: ['id', 'displayName', 'emails', 'photos']
        });
    }

    async validate(accessToken: any, refreshToken: any, profile: any) {
        return {
            name: profile.displayName,
            email: profile.emails[0].value,
            provider: "facebook",
            providerId: profile.id,
            photo: profile.photos[0].value
        }
    }
}

// 认证控制器

@Controller('auth')
export class AuthController {
    constructor(
        @Inject(forwardRef(() => AuthService)) private readonly authService: AuthService,
    ) { }

    @Get('/facebook')
    @UseGuards(AuthGuard('facebook'))
    async facebookAuth(@Request() req) {
        return
    }

    @UseGuards(AuthGuard('facebook'))
    @Get('/facebook/callback')
    async facebookCallback(@Request() req) {
        return this.authService.login(req.user);
    }

}

基本上我希望能够调用“/auth/facebook/:type”并在策略中定义的回调 url 中传递类型值

和回调端点类似于“/auth/facebook/callback/:type”

因此,当我调用 authservice.login 函数时,我可以传递该“类型”并决定在第一次注册时创建哪种类型的用户

如果我的方法是错误的,请指导我。谢谢

标签: facebooknestjspassport-facebook

解决方案


我最近一直在处理一个类似的问题,这是我的方法。可能不是最好的,但现在可以使用。

import { Inject, Injectable, Logger } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import passport = require('passport');
import { Strategy } from 'passport-facebook';

@Injectable()
export class FacebookStrategy extends PassportStrategy(Strategy, 'facebook') {
  private readonly logger = new Logger(FacebookStrategy.name);

  constructor(
    @Inject('FACEBOOK_STRATEGY_CONFIG')
    private readonly facebookStrategyConfig,
  ) {
    super(
      facebookStrategyConfig,
      async (
        request: any,
        accessToken: string,
        refreshToken: string,
        profile: any,
        done,
      ) => {
        this.logger.log(profile);

        // take the state from the request query params
        const { state } = request.query;
        this.logger.log(state);

        // register user

        // return callback
        return done(null, profile);
      },
    );
    passport.use(this);
  }
}
import { Controller, Get, HttpStatus, Inject, Param, Query, Req } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Redirect } from '@nestjsplus/redirect';

@Controller('auth')
export class AuthController {
    @Inject('ConfigService')
    private readonly configService: ConfigService;

    @Get(':provider/callback')
    @Redirect()
    async socialCallback(@Req() req, @Param('provider') provider: string, @Query('state') state: string) {
        // here you can use the provider and the state
        return {
            statusCode: HttpStatus.FOUND,
            url: `${this.configService.get('FRONTEND_HOST')}/dashboard`,
        };
    }
}
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { AuthController } from './auth.controller';
import { FacebookStrategy } from './facebook.strategy';
import passport = require('passport');

const facebookStrategyConfigFactory = {
    provide: 'FACEBOOK_STRATEGY_CONFIG',
    useFactory: (configService: ConfigService) => {
        return {
            clientID: `${configService.get('FACEBOOK_CLIENT_ID')}`,
            clientSecret: `${configService.get('FACEBOOK_CLIENT_SECRET')}`,
            callbackURL: `${configService.get('FACEBOOK_OAUTH_REDIRECT_URI')}/callback`,
            profileFields: ['id', 'displayName', 'link', 'photos', 'emails', 'name'],
            passReqToCallback: true,
        };
    },
    inject: [ConfigService],
};

@Module({
    controllers: [AuthController],
    providers: [facebookStrategyConfigFactory, FacebookStrategy],
})
export class AuthModule implements NestModule {
    public configure(consumer: MiddlewareConsumer) {

        const facebookLoginOptions = {
            session: false,
            scope: ['email'],
            state: null,
        };
        consumer
            .apply((req: any, res: any, next: () => void) => {
                const {
                    query: { state },
                } = req;
                facebookLoginOptions.state = state;
                next();
            }, passport.authenticate('facebook', facebookLoginOptions))
            .forRoutes('auth/facebook/*');
    }
}

现在让我解释一下:D。诀窍在于中间件配置。

const facebookLoginOptions = {
    session: false,
    scope: ['email'],
    state: null,
};
consumer
    .apply((req: any, res: any, next: () => void) => {
        const {
            query: { state },
        } = req;
        facebookLoginOptions.state = state;
        next();
    }, passport.authenticate('facebook', facebookLoginOptions))
    .forRoutes('auth/facebook/*');

因此,oAuth 具有此功能,您可以通过登录流程传递状态参数。通过在变量中提取护照选项,我们可以通过在护照之前应用另一个中间件来动态更改状态参数。这样,您现在可以调用http://localhost:3000/auth/facebook/login?state=anything-you-want ,此state查询参数将通过策略传递,也会在回调调用中传递。

我还使用示例创建了一个 git repo:https ://github.com/lupu60/passport-dynamic-state


推荐阅读