9

我已经研究了这两者passport-facebook以及passport-facebook-token与 NestJS 的集成。问题在于 NestJS 使用自己的实用程序(例如 AuthGuard)抽象了护照实现。

因此,ExpressJS文档中的样式实现不适用于 NestJS。@nestjs/passport例如,这与软件包不兼容:

var FacebookTokenStrategy = require('passport-facebook-token');

passport.use(new FacebookTokenStrategy({
    clientID: FACEBOOK_APP_ID,
    clientSecret: FACEBOOK_APP_SECRET
  }, function(accessToken, refreshToken, profile, done) {
    User.findOrCreate({facebookId: profile.id}, function (error, user) {
      return done(error, user);
    });
  }
));

这篇博文展示了一种passport-facebook-token使用不熟悉的、不符合AuthGuard.

@Injectable()
export class FacebookStrategy {
  constructor(
    private readonly userService: UserService,
  ) {
    this.init();
  }
  init() {
    use(
      new FacebookTokenStrategy(
        {
          clientID: <YOUR_APP_CLIENT_ID>,
          clientSecret: <YOUR_APP_CLIENT_SECRET>,
          fbGraphVersion: 'v3.0',
        },
        async (
          accessToken: string,
          refreshToken: string,
          profile: any,
          done: any,
        ) => {
          const user = await this.userService.findOrCreate(
            profile,
          );
          return done(null, user);
        },
      ),
    );
  }
}

这里的问题是,这似乎与 NestJS 期望您处理护照策略的方式完全不同。它是一起被黑的。它也可能在未来的 NestJS 更新中中断。这里也没有异常处理;我无法捕获异常,例如由于正在使用的回调性质而InternalOAuthError引发的异常。passport-facebook-token

是否有一种干净的方法来实现其中一个passport-facebookpassport-facebook-token以便它使用@nestjs/passport'validate()方法?来自文档:对于每个策略,Passport 将调用验证函数(使用 @nestjs/passport 中的 validate() 方法实现)。应该有一种方法可以在构造函数中传递 a clientIdclientSecret然后将其余逻辑放入validate()方法中。

我想最终结果看起来类似于以下内容(这不起作用):

import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import FacebookTokenStrategy from "passport-facebook-token";


@Injectable()
export class FacebookStrategy extends PassportStrategy(FacebookTokenStrategy, 'facebook')
{

    constructor()
    {
        super({
            clientID    : 'anid',     // <- Replace this with your client id
            clientSecret: 'secret', // <- Replace this with your client secret
        })
    }


    async validate(request: any, accessToken: string, refreshToken: string, profile: any, done: Function)
    {
        try
        {
            console.log(`hey we got a profile: `, profile);

            const jwt: string = 'placeholderJWT'
            const user = 
            {
                jwt
            }

            done(null, user);
        }
        catch(err)
        {
            console.log(`got an error: `, err)
            done(err, false);
        }
    }

}

在我的特殊情况下,我对callbackURL. 我只是在验证客户端已转发到服务器的访问令牌。我只是把上面说得很清楚。

另外,如果您好奇,上面的代码会产生一个,InternalOAuthError但我无法在策略中捕获异常以查看真正的问题是什么,因为它没有正确实现。我知道在这种特殊情况下,access_token我传递的是无效的,如果我传递一个有效的,代码就可以工作。通过正确的实现,虽然我将能够捕获异常,检查错误,并能够向用户发出适当的异常,在本例中为 HTTP 401。

InternalOAuthError: Failed to fetch user profile

很明显,异常是在validate()方法之外抛出的,这就是为什么我们的 try/catch 块没有捕获InternalOAuthError. 处理此异常对于正常的用户体验至关重要,我不确定在此实现中 NestJS 处理它的方式是什么,或者应该如何进行错误处理。

4

2 回答 2

5

您正在使用您所进行的Strategy使用extends PassportStrategy()类设置的正确轨道。为了从护照中捕获错误,您可以扩展AuthGuard('facebook')并添加一些自定义逻辑到handleRequest(). 您可以在此处阅读有关它的更多信息,或查看文档中的此代码段:

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

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  canActivate(context: ExecutionContext) {
    // Add your custom authentication logic here
    // for example, call super.logIn(request) to establish a session.
    return super.canActivate(context);
  }

  handleRequest(err, user, info) {
    // You can throw an exception based on either "info" or "err" arguments
    if (err || !user) {
      throw err || new UnauthorizedException();
    }
    return user;
  }
}

是的,这是使用 JWT 而不是 Facebook,但底层逻辑和处理程序是相同的,所以它应该仍然适合您。

于 2020-05-16T15:53:32.353 回答
0

就我而言,我曾经使用passport-facebook-token旧版本的巢。要升级,需要调整策略。我也对回调 url 不感兴趣。

这是一个passport-facebook-token使用嵌套约定并受益于依赖注入的工作版本:

import { Injectable } from '@nestjs/common'

import { PassportStrategy } from '@nestjs/passport'
import * as FacebookTokenStrategy from 'passport-facebook-token'

import { UserService } from '../user/user.service'
import { FacebookUser } from './types'

@Injectable()
export class FacebookStrategy extends PassportStrategy(FacebookTokenStrategy, 'facebook-token') {
  constructor(private userService: UserService) {
    super({
      clientID: process.env.FB_CLIENT_ID,
      clientSecret: process.env.FB_CLIENT_SECRET,
    })
  }

  async validate(
    accessToken: string,
    refreshToken: string,
    profile: FacebookTokenStrategy.Profile,
    done: (err: any, user: any, info?: any) => void,
  ): Promise<any> {
    const userToInsert: FacebookUser = {
      ...
    }

    try {
      const user = await this.userService.findOrCreateWithFacebook(userToInsert)

      return done(null, user.id) // whatever should get to your controller
    } catch (e) {
      return done('error', null)
    }
  }
}

这将创建facebook-token可以在控制器中使用的。

于 2022-01-03T15:19:56.760 回答