I have a project called Bifrost with root.module.ts and in this project I’m using a lib called auth-module that I created with a friend.
root.module.ts from the Bifrost project:
import { Module } from '@nestjs/common';
import { CacheModule } from '@nestjs/cache-manager';
import type { RedisClientOptions } from 'redis';
import { redisStore } from 'cache-manager-redis-yet';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { LoggerModule } from 'nestjs-pino';
import { CameraModule } from '../camera/camera.module';
import { HealthcheckModule } from '../healthcheck/healthcheck.module';
import { ExampleModule } from '../example/example.module';
import { AuthModule } from '../auth/auth.module';
import { AuthModule as ExternalAuthModule } from '@eusouagabriel/auth-module';
import { UserModule } from '../users/user.module';
import { ThirdPartyAppModule } from '../thirdPartyApps/thirdPartyApp.module';
import configuration from '../config/configuration';
import pino from 'pino';
import { PlateRegistryModule } from '../plateRegistry/plateRegistry.module';
import { OcrModule } from '../ocr/ocr.module';
import { SimCardModule } from '../simCard/simCard.module';
import { validate } from '@/validators/env.validation';
import { RouterModule } from '../router/router.module';
import { MqttAuthModule } from '../mqttAuth/mqttAuth.module';
import { AddressModule } from '../address/address.module';
import { InstallationModule } from '../installation/installation.module';
import { SurveillanceModule } from '../surveillance/surveillance.module';
import { BullModule } from '@nestjs/bull';
import { ProvisioningModule } from '../provisioning/provisioning.module';
import { SalesOperationsModule } from '../salesOperations/salesOperations.module';
import { BullBoardModule } from '@bull-board/nestjs';
import { ExpressAdapter } from '@bull-board/express';
import { BullBoardAuthMiddleware } from './middlewares/bullBoardAuth.middleware';
import { DAY_IN_MILLISECONDS } from '@/constants/time.constants';
import { TutkModule } from '../tutk/tutk.module';
import { BffLegacyProvisioningModule } from '../bff/legacyProvisioning/bffLegacyProvisioning.module';
import { InstallationTermModule } from '../installationTerm/installationTerm.module';
import { BffProvisioningModule } from '../bff/Provisioning/bffProvisioning.module';
import { AirtableModule } from '../airtable/airtable.module';
import { ImageModule } from '../images/images.module';
import { NotificationModule } from '../notification/notification.module';
@Module({
imports: [
ConfigModule.forRoot({
validate: validate,
isGlobal: true,
load: [configuration],
}),
CacheModule.registerAsync<RedisClientOptions>({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => {
const host = configService.get<string>('REDIS_HOST');
const port = configService.get<number>('REDIS_PORT');
const url = `redis://${host}:${port}`;
const database = configService.get<number>('REDIS_DB');
return {
store: redisStore,
url,
database,
ttl: 1 * DAY_IN_MILLISECONDS, // default TimeToLive (TTL)
};
},
isGlobal: true,
}),
BullModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
redis: {
db: configService.get<number>('REDIS_DB'),
host: configService.get('REDIS_HOST'),
port: configService.get('REDIS_PORT'),
},
}),
}),
BullBoardModule.forRoot({
route: '/queues',
adapter: ExpressAdapter,
middleware: BullBoardAuthMiddleware,
}),
LoggerModule.forRoot({
forRoutes: [
'addresses',
'auth/*',
'bff/*',
'cameras',
'installationTerms',
'examples/*',
'installations',
'ocr/*',
'provisioning/*',
'routers',
'sim-cards',
'surveillance/*',
'third-party-applications',
'users',
'images/*',
'notification',
'sales-operations',
],
pinoHttp: {
logger: pino({
formatters: {
level: (label, number) => {
return { level: number, status: label };
},
},
redact: ['req.headers.authorization', 'req.headers.cookie'],
}),
},
}),
CameraModule,
HealthcheckModule,
ExampleModule,
AuthModule,
ExternalAuthModule,
UserModule,
ThirdPartyAppModule,
OcrModule,
PlateRegistryModule,
SimCardModule,
RouterModule, // Note: It is not the @nestjs/core Router Module!
MqttAuthModule,
AddressModule,
InstallationModule,
SurveillanceModule,
ProvisioningModule,
SalesOperationsModule,
BffLegacyProvisioningModule,
BffProvisioningModule,
AirtableModule,
TutkModule,
InstallationTermModule,
ImageModule,
NotificationModule,
],
})
export class RootModule {}
auth.module.ts (from the auth-module lib):
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from './strategies/jwtAuth.strategy';
import { ApplicationLoginService } from './login/applicationLoginService';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [PassportModule, ConfigModule],
providers: [JwtStrategy, ApplicationLoginService],
exports: [JwtStrategy, ApplicationLoginService],
})
export class AuthModule {}
jwtAuth.strategy.ts (also a file from the lib):
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { JwtPayloadDto } from './dtos/jwtPayload.dto';
import { AuthenticatedEntity } from '../decorators/authenticatedEntity.decorator';
import { ConfigService } from '@nestjs/config';
import * as jwksRsa from 'jwks-rsa';
import * as https from 'https';
import { firstLetterToUpperCase } from '../utils/string';
import { getAuthorizerUrl, getGabrielInternalCa } from '../utils/config';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor(private readonly configService: ConfigService) {
const issuerUrl = getAuthorizerUrl(configService);
const ca = issuerUrl.endsWith('gabriel.internal')
? getGabrielInternalCa(configService)
: undefined;
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKeyProvider: jwksRsa.passportJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `${issuerUrl}/.well-known/jwks.json`,
requestAgent: new https.Agent(ca ? { ca } : {}),
}),
algorithms: ['RS256'],
});
}
async validate(payload: JwtPayloadDto) {
const authenticatedEntity: AuthenticatedEntity = {
id: payload.sub,
type: firstLetterToUpperCase(payload.sub_type) as
| 'User'
| 'ThirdPartyApp',
roles: payload.roles,
};
return authenticatedEntity;
}
}
The error:
[Nest] 51840 - 09/10/2024, 9:02:21 PM ERROR [ExceptionHandler] Nest can't resolve dependencies of the JwtStrategy (?). Please make sure that the argument ConfigService at index [0] is available in the AuthModule context.
Potential solutions:
- Is AuthModule a valid NestJS module?
- If ConfigService is a provider, is it part of the current AuthModule?
- If ConfigService is exported from a separate @Module, is that module imported within AuthModule?
@Module({
imports: [ /* the Module containing ConfigService */ ]
})
Error: Nest can't resolve dependencies of the JwtStrategy (?). Please make sure that the argument ConfigService at index [0] is available in the AuthModule context.
Potential solutions:
- Is AuthModule a valid NestJS module?
- If ConfigService is a provider, is it part of the current AuthModule?
- If ConfigService is exported from a separate @Module, is that module imported within AuthModule?
@Module({
imports: [ /* the Module containing ConfigService */ ]
})
at Injector.lookupComponentInParentModules (/usr/src/app/node_modules/@nestjs/core/injector/injector.js:254:19)
at processTicksAndRejections (node:internal/process/task_queues:95:5)
at Injector.resolveComponentInstance (/usr/src/app/node_modules/@nestjs/core/injector/injector.js:207:33)
at resolveParam (/usr/src/app/node_modules/@nestjs/core/injector/injector.js:128:38)
at async Promise.all (index 0)
at Injector.resolveConstructorParams (/usr/src/app/node_modules/@nestjs/core/injector/injector.js:143:27)
at Injector.loadInstance (/usr/src/app/node_modules/@nestjs/core/injector/injector.js:70:13)
at Injector.loadProvider (/usr/src/app/node_modules/@nestjs/core/injector/injector.js:97:9)
at /usr/src/app/node_modules/@nestjs/core/injector/instance-loader.js:56:13
at async Promise.all (index 3)
What am I doing wrong?
I tried several examples that I saw here and in the documentation
2