#JWT error : secretOrPrivateKey must have a value

1 messages · Page 1 of 1 (latest)

shrewd adder
#

I have setup JWT tokens, but I am unable to get the token for the reason in the title. This is the error:

Nest] 34347 - 07/29/2023, 3:34:49 PM ERROR [ExceptionsHandler] secretOrPrivateKey must have a value
Error: secretOrPrivateKey must have a value
at Object.module.exports [as sign] (/Users/kenflores/Development/Internal Apps/Radiant Data Portal/RadiantDataApi/backend/node_modules/@nestjs/jwt/node_modules/jsonwebtoken/sign.js:105:20)
at JwtService.sign (/Users/kenflores/Development/Internal Apps/Radiant Data Portal/RadiantDataApi/backend/node_modules/@nestjs/jwt/dist/jwt.service.js:35:20)
at AuthService.login (/Users/kenflores/Development/Internal Apps/Radiant Data Portal/RadiantDataApi/backend/src/api/auth/auth.service.ts:56:37)
at AuthController.login (/Users/kenflores/Development/Internal Apps/Radiant Data Portal/RadiantDataApi/backend/src/api/auth/auth.controller.ts:61:29)
at /Users/kenflores/Development/Internal Apps/Radiant Data Portal/RadiantDataApi/backend/node_modules/@nestjs/core/router/router-execution-context.js:38:29
at /Users/kenflores/Development/Internal Apps/Radiant Data Portal/RadiantDataApi/backend/node_modules/@nestjs/core/router/router-execution-context.js:46:28
at /Users/kenflores/Development/Internal Apps/Radiant Data Portal/RadiantDataApi/backend/node_modules/@nestjs/core/router/router-proxy.js:9:17

#

The following are my Strategies and such:
SERVICE:

// auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { GoogleUser } from '../users/google_users/dto/google_users.dto';
import { JwtService } from '@nestjs/jwt';
import { RegularUser } from '../users/regular_users/dto/regular_users.dto';
import { compare, hash } from 'bcryptjs'; // 

@Injectable()
export class AuthService {
  constructor(
    @InjectModel(GoogleUser.name) private readonly googleUserModel: Model<GoogleUser>,
    @InjectModel(RegularUser.name) private readonly userModel: Model<RegularUser>,
    private readonly jwtService: JwtService,
    
  ) {}

  async validateRegularUser(username: string, password: string): Promise<RegularUser | null> {
    const user = await this.userModel.findOne({ username }).exec();
    if (!user) {
      return null;
    }

    const isPasswordValid = await compare(password, user.password);
    if (!isPasswordValid) {
      return null;
    }

    return user.toJSON(); // Convert to JSON to make sure _id is present in the response
  }

async login(user: any){

    const payload = { sub: user.id, username: user.username };
   
    return {
      access_token: this.jwtService.sign(payload)
    };
  
}



  async signup(userDto: RegularUser): Promise<RegularUser> {
    const existingUser = await this.userModel.findOne({ username: userDto.username }).exec();
    if (existingUser) {
      throw new Error('Username already exists');
    }

    const hashedPassword = await hash(userDto.password, 10);
    const newUser = new this.userModel({
      ...userDto,
      password: hashedPassword,
      authProvider: "local"
    });

    return newUser.save();
  }

 
}

#

AuthModule:


// auth/auth.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { JwtModule, JwtService } from '@nestjs/jwt';
import { GoogleUser } from '../users/google_users/dto/google_users.dto'; // Replace with your actual GoogleUser schema
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { GoogleStrategy } from './utils/strategies/google.strategy';
import { SessionSerializer } from './utils/serializer';
import { GoogleUserSchema } from '../users/google_users/schemas/users.schema/users.schema';
import { LocalStrategy } from './utils/strategies/local.strategy';
import { RegularUserSchema } from '../users/regular_users/schemas/regular_users.schema/regular_users.schema';
import { RegularUser } from '../users/regular_users/dto/regular_users.dto';
import { PassportModule } from '@nestjs/passport';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [
    MongooseModule.forFeature([
      { name: GoogleUser.name, schema: GoogleUserSchema },
      { name: RegularUser.name, schema: RegularUserSchema },
    ]),
    PassportModule,
    ConfigModule, // Import your custom ConfigModule here
    JwtModule.registerAsync({
      imports: [ConfigModule], // Import ConfigModule for injecting ConfigService
      useFactory: (configService: ConfigService) => ({
        secret: configService.get<string>('JWT_SECRET'), // Use the ConfigService to get the secret
        signOptions: {
          expiresIn: '60s',
        },
      }),
      inject: [ConfigService], // Inject the ConfigService
    }),
  ],


  controllers: [AuthController],
  providers: [
    GoogleStrategy,
    SessionSerializer,
    LocalStrategy,
    JwtService, // Add RegularStrategy to the providers

    {
      provide: 'AUTH_SERVICE',
      useClass: AuthService,
    },
    AuthService,
  ],
  exports: [AuthService]
})
export class AuthModule {}
#

This is the route that im sending to:

the (login)

// auth/auth.controller.ts
import { Controller, Get, Req, UseGuards, Res, Redirect, Post, Body, Request } from '@nestjs/common';
import {  Response } from 'express';
import { GoogleAuthGuard } from './utils/guards/guards';
import { LocalAuthGuard } from './utils/guards/local.guard';
import { AuthService } from './auth.service';
import { RegularUser } from '../users/regular_users/dto/regular_users.dto';

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService, 
   ) {}
  
  @Get('google/login')
  @UseGuards(GoogleAuthGuard)
  handleLogin() {
    return { msg: 'Google Authentication' };
  }

  // api/auth/google/redirect
  @Get('google/redirect')
  @UseGuards(GoogleAuthGuard)
  handleRedirect() {
    return { msg: 'OK' };
  }

  @Get('status')
  user(@Request() req) {
    console.log(req.user);
    if (req.user) {
      return { msg: 'Authenticated' };
    } else {
      return { msg: 'Not Authenticated' };
    }
  }
  @Get('logout')
  logout(@Request() req, @Res() res: Response): void {
      req.logout((err) => {
          if (err) {
              // handle error
              console.log(err);
              return res.status(500).json({message: 'Logout failed'});
          }
          res.redirect('/');
      });
  }


  @UseGuards(LocalAuthGuard)
  @Post('login')
  login(@Request() req): any{
    
    return this.authService.login(req.user);
  }
  

  @Post('signup')
  async signup(@Body() userDto: RegularUser): Promise<{ message: string }> {
    await this.authService.signup(userDto);
    return { message: 'User registered successfully' };
  }

}
#

This is my strategy:

// auth/utils/strategies/regular.strategy.ts
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from '../../auth.service';
import { RegularUser } from 'src/api/users/regular_users/dto/regular_users.dto';

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


  async validate(username: string, password: string): Promise<RegularUser> {
    const user = await this.authService.validateRegularUser(username, password);
    if (!user) {
      throw new UnauthorizedException('Invalid credentials');
    }

    return user;
  }
}
#

this is my guard:


// auth/utils/guards/local-auth.guard.ts
import { ExecutionContext, Injectable} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Observable } from 'rxjs';

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {
 
  async canActivate(context: ExecutionContext) {
    const result = (await super.canActivate(context)) as boolean;
    const request = context.switchToHttp().getRequest();

    await super.logIn(request); 
    return result
    
  }
  }

#

This is the problematic line:

access_token: this.jwtService.sign(payload)```
#

not returning the access token

shrewd adder
#

so , it works if I do this:


      access_token: this.jwtService.sign(payload, { secret:this.configService.get('JWT_SECRET') }),
#

any reason why I need to explicitly put it in there?

shadow carbon
#

Most likely you have the auth controller and service being registered in another module and it's not using the configuration you are expecting it to

shrewd adder
#

Hmm.. How can I check that?

#

Because I think I made sure it uses only the one I specified in the module

#

I even specifically put the secret in the module

shadow carbon
#

grep AuthController

shrewd adder
#

is that a command

#

never heard of it 😄

shadow carbon
#

Yes, to run in the terminal

shrewd adder
#

its just there

#

not doing anything

#

i dont know what to expect from it

#

sorry 😦

shadow carbon
#

Might try grep AuthController ./src

shrewd adder
#

grep: ./src: Is a directory

shadow carbon
#

It should print out the file name and line where that comment is found

shrewd adder
#

thats all it gave me

#

grep: ./src: Is a directory

shadow carbon
#

grep -r AuthController ./src

shrewd adder
#

./src/api/auth/auth.controller.ts:export class AuthController {
./src/api/auth/auth.module.ts:import { AuthController } from './auth.controller';
./src/api/auth/auth.module.ts: controllers: [AuthController],

shadow carbon
#

Hmm, only in the auth module that's good

shrewd adder
#

yes... I feel like I always try my best to follow the best i can and I still end up in these situations lol

#

What should be my next step? do I have to create somethinng else?

shadow carbon
#

Ah, I see. Remove the JwtService from the auth module

shrewd adder
#

You are right!!!!

#

How did you know? why did that behave that way?

#

Youre amazing

shadow carbon
shrewd adder
#

Oh! Gotcha!