Encountering some challenges with the registerAsync
function in combination with JwtModule
and JwtService
. While browsing various discussions, it seems like many were stuck on ConfigModule
, but that's not a part of my project.
Let me provide some context:
// secret-key.helper.ts
/*
Retrieve the JWT secret value from AWS Secrets Manager. This is used to
sign JWTs. Imports are already included.
*/
async function retrieveJwtSecret(): Promise<string> {
const secretClient = new SecretsManagerClient({
region: process.env.AWS_REGION,
});
const getSecretCom = new GetSecretValueCommand({
SecretId: process.env.JWT_SECRET_ARN,
});
const jwtSecret = (await secretClient.send(getSecretCom)).SecretString;
return jwtSecret;
}
// auth.module.ts
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtModule } from '@nestjs/jwt';
import { retrieveJwtSecret } from '../../utils/secret-key.helper';
@Module({
imports: [
JwtModule.registerAsync({
useFactory: async () => ({
global: true,
secret: await retrieveJwtSecret(),
signOptions: { expiresIn: '16h' },
}),
}),
],
controllers: [AuthController],
providers: [AuthService],
})
export class AuthModule {}
// my.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Request } from 'express';
import { logEvent } from '../log-event.helper';
import { JwtService } from '@nestjs/jwt';
import { JwtPayloadDto } from 'src/models/auth/dto/jwt-payload.dto';
@Injectable()
export class MyGuard implements CanActivate {
constructor(private jwtService: JwtService) {}
private extractTokenFromHeader(request: Request): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
/* JWT Authentication */
const token = this.extractTokenFromHeader(req);
if (!token) {
logEvent('ERROR: No token found.', req.headers);
return false;
} else {
const payload = this.jwtService.decode(token) as JwtPayloadDto;
logEvent('Token found.', payload);
return true;
}
}
}
The first snippet demonstrates how I fetch the JWT secret key from AWS Secrets Manager. The second code block showcases the auth
module with the usage of retrieveJwtSecret()
method within the registerAsync
global import (which is causing issues). The third block is regarding the guard being utilized.
When Nest encounters the imported Guard in the first module, it throws a "can't resolve dependencies" error. Switching from registerAsync
to register
, manually extracting the secret key in
main.ts</code, and assigning it to <code>process.env
resolves the issue.
The specific error message reads:
ERROR [ExceptionHandler] Nest can't resolve dependencies of the MyGuard (?).
Please make sure that the argument JwtService at index [0] is available in the BenefitsModule context.
Potential solutions: ...
By utilizing console.log
, it was determined that the execution initiates in the retrieveJwtSecret
function and has access to the necessary .env variables during runtime. However, the process never completes the
const jwtSecret = await secretClient.send...
call. It is speculated that Nest continues loading modules requiring JwtModule
before obtaining the secret and finalizing the setup of JwtModule
.
If feasible, I prefer avoiding the implementation of register
as previously mentioned. Is there a way to compel Nest to await async
dependencies?
Thank you!