Search code examples
node.jstypescriptauthenticationpassport.jsnestjs

How to implement multiple passport jwt authentication strategies in Nestjs


I have an existing authentication for users which is already working fine. The token for user authentication expires within an hour.

I want to implement another separate authentication strategy a third API that is consuming my Nestjs API. There are separate endpoints for the third-party API, the token should expire with 24 hours. The API has to stay connected to my app for 24 hours.

I don't mind using additional package to achieve this.

I also need to create a guard called thirdParty Guard so that the 3rd part API alone will have access to that endpoint.

This is my jwt.strategy.ts

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
    constructor(private authService: AuthService) {
        super({
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            ignoreExpiration: false,
            secretOrKey: process.env.SECRETKEY
        });
    }

    async validate(payload: any, done: VerifiedCallback) {
        const user = await this.authService.validateUser(payload);
        if (!user) {
            return done(
                new HttpException('Unauthorised access', HttpStatus.UNAUTHORIZED),
                false,
            );
        }
        //return user;
        return done(null, user, payload.iat)
    }
}

ApiKey.strategy.ts

@Injectable()
export class ApiKeyStrategy extends PassportStrategy(HeaderAPIKeyStrategy) {
    constructor(private authService: AuthService) {
        super({
            header: 'api_key',
            prefix: ''
        }, true,
            (apikey: string, done: any, req: any, next: () => void) => {
                const checkKey = this.authService.validateApiKey(apikey);
                if (!checkKey) {
                    return done(
                        new HttpException('Unauthorized access, verify the token is correct', HttpStatus.UNAUTHORIZED),
                        false,
                    );
                }
                return done(null, true, next);
            });
    }
}

and this is the auth.service.ts

@Injectable()
export class AuthService {
    constructor(private userService: UserService) { }

    async signPayLoad(payload: any) {
        return sign(payload, process.env.SECRETKEY, { expiresIn: '1h' });

    }

    async validateUser(payload: any) {
        const returnuser = await this.userService.findByPayLoad(payload);
        return returnuser;
    }

    validateApiKey(apiKey: string) {
        const keys = process.env.API_KEYS;
        const apiKeys = keys.split(',');
        return apiKeys.find(key => apiKey === key);
    }
}

Solution

  • With the above setup, If you are using Passport-HeaderAPIKey then try adding headerapikey in the Guard. The below code worked for me.

    Ref: NestJS extending guard

    import { ExecutionContext, Injectable } from '@nestjs/common';
    import { Reflector } from '@nestjs/core';
    import { AuthGuard as NestAuthGuard } from '@nestjs/passport';
    
    @Injectable()
    export class AuthGuard extends NestAuthGuard(['jwt', 'headerapikey']) {
      constructor(private readonly reflector: Reflector) {
        super();
      }
    
      canActivate(context: ExecutionContext) {
        const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [
          context.getHandler(),
          context.getClass(),
        ]);
    
        if (isPublic) {
          return true;
        }
    
        return super.canActivate(context);
      }
    }