Unable to retrieve dotenv JWT secret " Error: secretOrPrivateKey must have a value"

2.7k views Asked by At

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
      )
    }
  }
2

There are 2 answers

1
papillon On

You must import the dotenv library and configure it before being able to use environment variables defined in .env. Adding require('dotenv').config() to main.ts will solve your problem.

0
eol On

I think this can be fixed by importing the ConfigModule to the TestingModule in your unit-test:

 const module: TestingModule = await Test.createTestingModule({
      imports: [ConfigModule.forRoot()],
      providers: [AuthService, AuthRepository, AuthGateway],
    }).compile()