如何使用 NestJS 和 Passport 设置自定义未经授权的 oauth2 错误

Éme*_*nto 5 exception oauth-2.0 passport.js nestjs

我正在使用自定义护照策略,如果客户端拒绝访问 oauth2 应用程序,我想向客户端发送自定义错误。

这是授权流程:

在此输入图像描述

我想向用户发送自定义错误消息而不是 500 错误。如果成功,当用户授权应用程序时一切都会顺利。

在 NestJS 官方文档中,有一个示例说明了如何执行此操作: https://docs.nestjs.com/security/authentication#extending-guards

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

没有明显的变化,当我分析应用程序日志时,唯一不同的是它引发了异常:

api_1         | [Nest] 5080   - 12/10/2020, 6:13:54 PM   [RouterExplorer] Mapped {/users/sign-up/email, POST} route +0ms
api_1         | [Nest] 5080   - 12/10/2020, 6:13:54 PM   [RouterExplorer] Mapped {/users/sign-up/confirm-email, GET} route +0ms
api_1         | [Nest] 5080   - 12/10/2020, 6:13:54 PM   [NestApplication] Nest application successfully started +3ms
api_1         | [Nest] 5080   - 12/10/2020, 6:14:00 PM   [ExceptionsHandler] Object:
api_1         | {
api_1         |   "name": "AuthorizationError",
api_1         |   "message": "",
api_1         |   "code": "access-denied",
api_1         |   "status": 500
api_1         | }
api_1         |  +6377ms
Run Code Online (Sandbox Code Playgroud)

这些是与此问题相关的代码。

meli.strategy.ts

import { Strategy } from 'passport-oauth2';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';

import UsersService from '@/users/users.service';
import MeliService from '@/meli/meli.service';

@Injectable()
export class MeliStrategy extends PassportStrategy(Strategy, 'meli') {
  constructor(
    private usersService: UsersService,
    private meliService: MeliService,
  ) {
    super({
      authorizationURL: `https://auth.mercadolivre.com.br/authorization?response_type=code&client_id=${process.env.MELI_APP_ID}`,
      tokenURL: 'https://api.mercadolibre.com/oauth/token',
      clientID: process.env.MELI_APP_ID,
      clientSecret: process.env.MELI_APP_SECRET,
      callbackURL: process.env.MELI_REDIRECT_URL,
      scope: 'authorization_code',
    });
  }

  async validate(accessToken: string) {
    const userData = await this.meliService.getUserData(accessToken);
    let user = await this.usersService.findUserByMercadoLibre(userData);

    if (!user) {
      user = await this.usersService.signUpByMercadoLibre(userData);
    }

    return user;
  }
}

export default MeliStrategy;
Run Code Online (Sandbox Code Playgroud)

meli-auth.guard.ts

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

@Injectable()
export default class MeliAuthGuard extends AuthGuard('meli') {
  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 BadRequestException('Just a custom message...');
    }
    return user;
  }
}
Run Code Online (Sandbox Code Playgroud)