I am trying to secure my app by hiding the secret JWT key into a dotenv file stored locally at the root of the app, but the documentation isn't clear enough to me, and I keep getting this error when running the tests :
console.error
Error: secretOrPrivateKey must have a value
at Object.module.exports [as sign] (/Users/amehmeto/HeroesJobs/BFF/bff-candidates/node_modules/jsonwebtoken/sign.js:107:20)
at JwtService.sign (/Users/amehmeto/HeroesJobs/BFF/bff-candidates/node_modules/@nestjs/jwt/dist/jwt.service.js:27:20)
at AuthService.generateToken (/Users/amehmeto/HeroesJobs/BFF/bff-candidates/src/auth/auth.service.ts:62:37)
at AuthService.verifySmsCode (/Users/amehmeto/HeroesJobs/BFF/bff-candidates/src/auth/auth.service.ts:49:19)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at Object.<anonymous> (/Users/amehmeto/HeroesJobs/BFF/bff-candidates/src/auth/auth.service.spec.ts:83:30)
49 | return this.generateToken(candidateId)
50 | } catch (e) {
> 51 | console.error(e)
| ^
52 | throw new HttpException(
53 | ResponseMessage.INVALID_CODE,
54 | HttpStatus.FORBIDDEN
at AuthService.verifySmsCode (auth/auth.service.ts:51:15)
at Object.<anonymous> (auth/auth.service.spec.ts:83:30)
Here is the .env
:
JWT_SECRET=secretKey
Here is the src/app.module.ts
:
import { Module } from '@nestjs/common'
import { StatusModule } from './status/status.module'
import { CitiesModule } from './cities/cities.module'
import { PopupModule } from './popup/popup.module'
import { CandidateModule } from './candidate/candidate.module'
import { NotificationModule } from './notification/notification.module'
import { ChatModule } from './chat/chat.module'
import { AuthModule } from './auth/auth.module'
import { SkillInviteModule } from './skill-invite/skill-invite.module'
import { ConfigModule } from '@nestjs/config'
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
StatusModule,
CitiesModule,
PopupModule,
CandidateModule,
NotificationModule,
ChatModule,
AuthModule,
SkillInviteModule,
],
})
export class AppModule {}
The test file :
describe('AuthService', () => {
let service: AuthService
let authRepository: AuthRepository
let authGateway: AuthGateway
let jwtService: JwtService
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
JwtModule.register({
secret: process.env.JWT_SECRET,
}),
],
providers: [AuthService, AuthRepository, AuthGateway],
}).compile()
service = module.get<AuthService>(AuthService)
authRepository = module.get<AuthRepository>(AuthRepository)
authGateway = module.get<AuthGateway>(AuthGateway)
jwtService = module.get<JwtService>(JwtService)
})
it('should be defined', () => {
expect(service).toBeDefined()
})
[...]
it('should generate a token that expires within one hour when code is valid', async () => {
const payload = { candidateId: '1' }
const verifyResponse = await service.verifySmsCode(phoneNumber, code)
expect(verifyResponse).toHaveProperty(
'access_token',
jwtService.sign(payload)
)
expect(verifyResponse).toHaveProperty('expires_in', 1000 * 60 * 60)
})
and finally the code under test :
async verifySmsCode(
phoneNumber: string,
code: string
): Promise<VerifyResponseObject> {
try {
const candidateId = await this.authRepository.verifySmsCode(
phoneNumber,
code
)
return this.generateToken(candidateId)
} catch (e) {
console.error(e)
throw new HttpException(
ResponseMessage.INVALID_CODE,
HttpStatus.FORBIDDEN
)
}
}
You must import the
dotenv
library and configure it before being able to use environment variables defined in.env
. Addingrequire('dotenv').config()
tomain.ts
will solve your problem.