使用 @AuthGuard、Passport 在 NestJS 中进行社交登录

PeS*_*PeS 4 authentication node.js passport-twitter passport.js nestjs

所以,我几乎完成了在 NestJS 支持的应用程序中实现社交登录的尝试。但我有一些问题:

首先要事。我有AuthModule并且那里有提供者TwitterGuard

const twitterOptions: IStrategyOptionWithRequest = {
  consumerKey: process.env[ENV.SOCIAL.TWITTER_CONSUMER_KEY],
  consumerSecret: process.env[ENV.SOCIAL.TWITTER_CONSUMER_SECRET],
  callbackURL: process.env[ENV.SOCIAL.TWITTER_CALLBACK_URL],
  passReqToCallback: true,
  includeEmail: true,
  skipExtendedUserProfile: false,
};

export class TwitterGuard extends PassportStrategy(Strategy, 'twitter') {
  constructor() {
    super(twitterOptions);
  }

  // Magical nest implementation, eq to passport.authenticate
  validate(req: Request, accessToken: string, refreshToken: string, profile: Profile, done: (error: any, user?: any) => void) {
    const user: SocialAuthUser = {
      id: profile.id,
      nick: profile.username,
      name: profile.displayName,
    };
    if (profile.emails) {
      user.email = profile.emails.shift().value;
    }
    if (profile.photos) {
      user.avatar = profile.photos.shift().value;
    }

    done(null, user);
  }
}

Run Code Online (Sandbox Code Playgroud)

AuthController

@Controller('auth')
@ApiUseTags('auth')
export class SocialAuthController {

  constructor(private us: UserService) {
  }

  @Get('twitter')
  @UseGuards(AuthGuard('twitter'))
  twitter() {
    throw new UnauthorizedException();
  }

  @Get('twitter/callback')
  @UseGuards(AuthGuard('twitter'))
  async twitterCallback(@ReqUser() socialUser: SocialAuthUser, @Res() response) {
    const user = await this.us.registerSocialUser(socialUser);
    if (user) {
      // console.log('Redirect', '/some-client-route/token');
      response.redirect(`${SITE_URL}/activate/${user.token}`);
    }
    response.sendStatus(401);
  }

}
Run Code Online (Sandbox Code Playgroud)

当我调用 URL 时,/auth/twitter警卫启动并重新路由到 Twitter 页面,要求用户授予对 Twitter 应用程序的访问权限。

如果用户授予访问权限,一切都很好,在回调路由 ( /auth/twitter/callback)上TwitterGuard,再次启动并处理用户validate,存储到request,我可以在控制器中进一步访问它。到目前为止,一切都很好。

但是,如果用户拒绝访问 Twitter 应用程序,则即使在我的任何方法被命中之前,守卫也会在回调路由上返回 401。

我尝试使用authenticate被调用的方法(现在在代码中注释掉),我可以在其中以某种方式调整它,但不知道要返回或做什么。如果这是一种方法,我如何像护照策略那样从那里重定向到 Twitter 身份验证页面?回调中返回什么以继续并设置一些访问被拒绝的标志?

还有其他方法吗?我缺少什么?

提前致谢。

编辑:如果您有疑问做什么@ReqUser(),这里是:

export const ReqUser = createParamDecorator((data, req): any => {
  return req.user;
});
Run Code Online (Sandbox Code Playgroud)

PeS*_*PeS 5

没关系,我找到了解决方案,这个答案很有帮助。在这里发帖以防其他人遇到同样的麻烦。

我创建TwitterAuthGuard

export class TwitterAuthGuard extends AuthGuard('twitter') {
  handleRequest(err, user, info, context) {
    return user;
  }
}
Run Code Online (Sandbox Code Playgroud)

并在回调路由中使用它:

  @Get('twitter/callback')
  @UseGuards(TwitterAuthGuard)
  async twitterCallback(@ReqUser() socialUser: SocialAuthUser, @Res() response) {
    if (socialUser) {
      const user = await this.us.registerSocialUser(socialUser);
      if (user) {
        response.redirect(`...url`);
        return;
      }
    }
    response.redirect(SocialAuthController.authFailedUrl(LoginMethod.TWITTER));
  }
Run Code Online (Sandbox Code Playgroud)

现在,当 Twitter 调用回调路由时,它会进入TwitterAuthGuard handleRequest方法。

如果授予访问权限,user则参数包含来自用户配置文件的数据,并沿着链进一步传递到TwitterGuard validate方法(参见上面的问题)。

如果访问被拒绝,则user参数为false

因此,在控制器回调路由方法中,我获取标准化的用户数据或false参数user,因此我可以检查它是否失败并采取相应的行动。