Home > Software engineering >  NestJS JWT Module Issues
NestJS JWT Module Issues

Time:08-11

Having issues with the JWTModule in NestJS. I continuously get this error, even when I pass the secret directly into the configuration instead of through my env file. I can't figure out what I am missing.

Error: secretOrPrivateKey must have a value

Quick rundown of how this will work.

  1. Client calls /auth/login with { username: string, password: string }
  2. LocalStrategy Guard will call AuthService's validateUser method, which calls ldapServices authenticateUser method and returns the user.
  3. From there, I have to get the user data from ldap and combine it with some user data we have in our mongo db. This all works fine.
  4. After passing through the guard, it will call AuthService's login method. Which should create and sign the payload with the JWT, using the JwtService (provided by NestJS). This is where my issue occurs.

Here are the files and their code:

app.controller.ts

import { Controller, Get, Post, Request, UseGuards } from '@nestjs/common';
import { AppService } from './app.service';
import { AuthService } from './providers/auth/auth.service';
import { LocalAuthGuard } from './providers/auth/local.strategy';

@Controller()
export class AppController {
  constructor(private readonly authService: AuthService) {}

  @UseGuards(LocalAuthGuard)
  @Post('/auth/login')
  async login(@Request() req) {
    return this.authService.login(req.user);
  }
}

auth.module.ts

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UserModule } from './../../endpoints/user/user.module';
import { PassportModule } from '@nestjs/passport';
import { LocalStrategy } from './local.strategy';
import { JwtModule, JwtService } from '@nestjs/jwt';
import { UserService } from './../../endpoints/user/user.service';
import { LdapService } from './../ldap/ldap-service';
import { jwtConstants } from './constants';
import { JwtStrategy } from './jwt.strategy';

@Module({
  imports: [
    UserModule,
    PassportModule,
    JwtModule.register({
      secret: '2I*5f5OE9tlGIbg*3Q*C',
      signOptions: { expiresIn: '12h' },
    }),
  ],
  providers: [AuthService, LocalStrategy, LdapService],
  controllers: [AuthController],
  exports: [AuthService],
})
export class AuthModule {}

auth.service.ts

import { Injectable } from '@nestjs/common';
import { LdapService } from '../ldap/ldap-service';
import { UserService } from './../../endpoints/user/user.service';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(
    private readonly ldapService: LdapService,
    private readonly userService: UserService,
    private readonly jwtService: JwtService
  ) {}

  async validateUser(username: string, pass: string): Promise<any> {
    const authenticated = await this.ldapService.authenticateUser(
      username,
      pass,
    );

    if (authenticated === true) {
      console.log(authenticated, username, pass)
      return await this.userService.getUserById(username);
    }
    return null;
  }

  async login(user: any) {
    const payload = { ...user, sub: user.accountName };
    console.log(process.env.JWT_SECRET, payload);
    return {
      access_token: this.jwtService.sign(payload),
    };
  }
}

local.strategy.ts

import { Strategy } from 'passport-local';
import { AuthGuard, PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super();
  }

  async validate(username: string, password: string): Promise<any> {
    const user = await this.authService.validateUser(username, password);
    if (!user) throw new UnauthorizedException();
    return user;
  }
}

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}

I am sure I am missing something small. Appreciate the help.

CodePudding user response:

After discussion in the comments it was discovered that AuthService was being recreated by being added to more than one providers array. As the JwtService has @Optional() options to be injected into it, it didn't fail on re-creation, but it did not get the same options passed to JwtModule.register. To rectify this, theAuthModule exports: [AuthService] and any module that needs the AuthService adds imports: [AuthModule] to ensure only one AuthService is created and it has the proper JwtService with the options that are passed to JwtModule

  • Related