I'm trying to use a jwt.strategy.ts to protect my endpoints with jwt verification. Everything was going ok until I decided to import a custom JWTService inside this jwt.strategy which contains its own JWTModule but Nest doesn't seems to recognize it. I can use the JWTService in other services but it doesn't work inside the strategy. What should I do ? What am I doing wrong ?
The NEST Message:
[Nest] 53 - 09/22/2022, 6:32:25 PM ERROR [ExceptionHandler] Nest can't resolve dependencies of the JwtStrategy (ConfigService, ?). Please make sure that the argument Object at index [1] is available in the AuthModule context.
Potential solutions:
- If Object is a provider, is it part of the current AuthModule?
- If Object is exported from a separate @Module, is that module imported within AuthModule?
@Module({
imports: [ /* the Module containing Object */ ]
})
Error: Nest can't resolve dependencies of the JwtStrategy (ConfigService, ?). Please make sure that the argument Object at index [1] is available in the AuthModule context.
Potential solutions:
- If Object is a provider, is it part of the current AuthModule?
- If Object is exported from a separate @Module, is that module imported within AuthModule?
@Module({
imports: [ /* the Module containing Object */ ]
})
The JWTModule:
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { jwtOptions } from './jwt.config';
import { JWTService } from './jwt.service';
@Module({
imports: [JwtModule.registerAsync(jwtOptions)],
providers: [JWTService],
exports: [JWTService],
})
export class JWTModule {}
The JWTService:
import { Request } from 'express';
import { DecodeOptions } from 'jsonwebtoken';
import { Injectable, UnprocessableEntityException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService, JwtSignOptions, JwtVerifyOptions } from '@nestjs/jwt';
import { CookieHttpConfig } from '../auth';
@Injectable()
export class JWTService {
constructor(
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
) {}
sign(payload: string | object | Buffer, options?: JwtSignOptions) {
return this.jwtService.sign(payload, options);
}
async signAsync(payload: string | object | Buffer, options?: JwtSignOptions) {
return this.jwtService.signAsync(payload, options);
}
verify(token: string, options?: JwtVerifyOptions) {
return this.jwtService.verify(token, options);
}
async verifyAsync(token: string, options?: JwtVerifyOptions) {
return this.jwtService.verifyAsync(token, options);
}
decode(token: string, options?: DecodeOptions) {
return this.jwtService.decode(token, options);
}
async getToken(tokenPayload: any): Promise<string> {
try {
const token: string = await this.jwtService.signAsync(tokenPayload);
return `Bearer ${token}`;
} catch (error) {
throw new UnprocessableEntityException(error.message);
}
}
async refreshToken(
cookieName: string,
request: Request,
payload: any,
): Promise<void> {
const token: string = await this.getToken(payload);
request.res.cookie(cookieName, token, CookieHttpConfig.Options());
}
}
jwt.strategy:
import { JWTService } from '@app/common';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
import { Request } from 'express';
import { ExtractJwt, Strategy } from 'passport-jwt';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
private readonly configService: ConfigService,
private readonly jwtService: JWTService,
) {
super({
jwtFromRequest: ExtractJwt.fromExtractors([
(request: Request) => {
try {
const token = request.signedCookies['Authorization'].split(' ')[1];
return token;
} catch (error) {
return null;
}
},
]),
ignoreExpiration: false,
secretOrKey: configService.get('AUTH_JWT_SECRET'),
});
}
async validate(request: Request, payload: any): Promise<any> {
const tokenPayload = {
email: payload.email,
id: payload.id,
};
await this.jwtService.refreshToken('Authorization', request, tokenPayload);
return tokenPayload;
}
}
Extra information about my project:
I divided the project into a monorepo, so I imported the JWTModule inside the AuthModule but it still doesn't work. The jwt.strategy.ts and the JWTModule is inside a shared lib created at the same level as the apps folders containing the microservices.
The AuthModule:
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { UsersModule } from '../users/users.module';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import {
AuthLibModule,
JWTModule,
JwtStrategy,
LocalStrategy,
} from '@app/common';
import { Web3Module } from '../web3/web3.module';
import { VonageModule } from '@app/common';
@Module({
imports: [
UsersModule,
PassportModule,
JWTModule,
Web3Module,
VonageModule,
AuthLibModule,
],
controllers: [AuthController],
providers: [AuthService, LocalStrategy, JwtStrategy],
})
export class AuthModule {}