You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS+Fastify+Passport全局Guard如何按装饰器切换认证策略

Solution

To dynamically switch Passport strategies based on your @SetAccessRight decorator, follow these steps:

1. Ensure Strategies Have Unique Names

First, register each JWT strategy with a unique name using the second argument of PassportStrategy:

// src/auth/strategies/jwt-access.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, ExtractJwt } from 'passport-jwt';

@Injectable()
export class JwtAccessStrategy extends PassportStrategy(Strategy, 'jwt-access') {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: process.env.JWT_ACCESS_SECRET,
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}

// src/auth/strategies/jwt-refresh.strategy.ts
@Injectable()
export class JwtRefreshStrategy extends PassportStrategy(Strategy, 'jwt-refresh') {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: process.env.JWT_REFRESH_SECRET,
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub };
  }
}

// src/auth/strategies/jwt-confirm.strategy.ts
@Injectable()
export class JwtConfirmStrategy extends PassportStrategy(Strategy, 'jwt-confirm') {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: process.env.JWT_CONFIRM_SECRET,
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, email: payload.email };
  }
}

2. Implement the Dynamic Guard

Create a custom guard that uses Reflector to fetch the access right, maps it to the corresponding strategy name, and leverages NestJS's built-in AuthGuard integration:

// src/auth/guards/dynamic-jwt.guard.ts
import { Injectable, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Reflector } from '@nestjs/core';
import { AccessRight } from '../access-right.enum';

@Injectable()
export class DynamicJwtGuard extends AuthGuard() {
  constructor(private readonly reflector: Reflector) {
    super();
  }

  // Dynamically select strategy based on decorator value
  protected getAuthenticateOptions(context: ExecutionContext) {
    const accessRight = this.reflector.get<AccessRight>(
      'accessRight', // Matches metadata key from @SetAccessRight
      context.getHandler(),
    );

    return {
      strategy: this.resolveStrategyName(accessRight),
      session: false, // Disable sessions for JWT auth
    };
  }

  // Centralized error handling for all strategies
  protected handleRequest(err: any, user: any, info: any) {
    if (err || !user) {
      throw err || new UnauthorizedException(info?.message || 'Invalid or missing token');
    }
    return user;
  }

  // Map AccessRight enum to strategy names
  private resolveStrategyName(accessRight?: AccessRight): string {
    switch (accessRight) {
      case AccessRight.refresh:
        return 'jwt-refresh';
      case AccessRight.confirm:
        return 'jwt-confirm';
      default:
        return 'jwt-access'; // Fallback to default access strategy
    }
  }
}

3. Register the Guard Globally

Set the dynamic guard as your global authentication guard in the root module:

// src/app.module.ts
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { DynamicJwtGuard } from './auth/guards/dynamic-jwt.guard';
import { JwtAccessStrategy } from './auth/strategies/jwt-access.strategy';
import { JwtRefreshStrategy } from './auth/strategies/jwt-refresh.strategy';
import { JwtConfirmStrategy } from './auth/strategies/jwt-confirm.strategy';

@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: DynamicJwtGuard,
    },
    JwtAccessStrategy,
    JwtRefreshStrategy,
    JwtConfirmStrategy,
  ],
})
export class AppModule {}

4. Use the Decorator as Before

Your existing route decorator usage will now automatically switch the authentication strategy:

import { Get } from '@nestjs/common';
import { SetAccessRight } from './auth/access-right.decorator';
import { AccessRight } from './auth/access-right.enum';

@Get('refresh')
@SetAccessRight(AccessRight.refresh)
async refresh() {
  // Refresh token logic
}

@Get('confirm')
@SetAccessRight(AccessRight.confirm)
async confirmEmail() {
  // Email confirmation logic
}

// Defaults to jwt-access strategy
@Get('profile')
async getProfile() {
  // Profile fetch logic
}

Key Fixes for Your Original Error

Calling a strategy's authenticate() method directly breaks Passport's internal context (like the error function binding). By extending AuthGuard, we reuse NestJS's pre-built integration with Passport, which handles request/response context and callback binding correctly.

内容的提问来源于stack exchange,提问作者Alex Green

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 22:06:00