Reputation: 162
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.
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.
Upvotes: 2
Views: 1696
Reputation: 70061
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
Upvotes: 4