I was writing an awesome stuff with Express today and I ran into an error, after solving it (thanks to some wonderful geeks on Stackoverflow) I thought it best to write about it just incase anyone runs into this same error in future this article will kind them on how to solve it.
what is the error and why did it occur?
I installed the dotenv dependency with the aim of storing my json web token secret inside it so as to avoid pushing a secret pass to a Github public repo.
Initially, I had put the connection pass of my MongoDB database in it with the name DB_DETAILS and I wanted to add my token secret in the next line. Well, I did just that and boooom! the terminal threw me an error warning at run time.
UnhandledPromiseRejectionWarning: Error: secretOrPrivateKey must have a value
at Object.module.exports as sign
How did I solve this?
You’d be amazed! I only had to replace process.env.TOKEN_SECRET
to ${process.env.TOKEN_SECRET}
using ES6 String Literals.
Yes, that worked for me. Go ahead and try it out.
explicitly configure dotenv in NestJS to solve error
Ooooh, oh! Since NestJS uses dotenv under the hood, you could easily use ConfigService or configure dotenv by changing some files.
jwt.strategy.ts
import * as dotenv from 'dotenv';
dotenv.config();
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
secretOrKey: process.env.TOKEN_SECRET,
});
}
}
Enter fullscreen mode
Exit fullscreen mode
auth.module.ts
import * as dotenv from 'dotenv';
dotenv.config();
@Module({
imports: [
JwtModule.register({
secret: process.env.JWT_SECRET,
}),
],
})
Enter fullscreen mode
Exit fullscreen mode
Let me know in the comments if it works for you!
Issue
I am using jwt to create token, but when i login via postman I get the error “Error: secretOrPrivateKey must have a value” from my console. I have attached my login code. Please anyone who can help me
exports.login = (req, res, next) => {
User.findOne({
where: {
email: req.body.email
}
})
.then(user => {
if (!user) {
return res.status(401).json({
message:
"Auth failed!! either the account does't exist or you entered a wrong account"
});
}
bcrypt.compare(req.body.password, user.password, (err, result) => {
if (err) {
return res.status(401).json({
message: "Auth failed",
token: token
});
}
if (result) {
const token = jwt.sign(
{
email: user.email,
password: user.id
},
process.env.JWT_KEY,
{
expiresIn: "1h"
}
);
res.status(200).json({
message: "Auth granted, welcome!",
token: token
});
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
this is my env.json file
{
"env":{
"MYSQL":"jllgshllWEUJHGHYJkjsfjds90",
"JWT_KEY": "secret"
}
}
Solution
It looks like your application can’t read the environment variable properly.
I don’t know which package you are using to load environment variables but the simplest way is using dotenv package.
After installing it with npm i dotenv, import it as early as possible in your application main file like this:
require("dotenv").config();
Create .env file in your application root folder with this content ( as you see the format is key=value)
MYSQL=jllgshllWEUJHGHYJkjsfjds90
JWT_KEY=secret
Then you can access their values like you already did:
process.env.JWT_KEY
.env file:
Answered By – SuleymanSah
This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0
Issue
When a user logs into the API generates a token so that he has access to other endpoints, but the token expires in 60sec, I made a function to generate a new valid token using the old token (which was stored in the database), but when I’m going to generate a new valid token I’m getting the secretOrPrivateKey must have a value error
The function refreshToken use function login to generate a new token
Nest error:
secretOrPrivateKey must have a value
Error: secretOrPrivateKey must have a value
at Object.module.exports [as sign] (C:Userstalisnova apimyflakes_apinode_modulesjsonwebtokensign.js:107:20)
at JwtService.sign (C:Userstalisnova apimyflakes_apinode_modules@nestjsjwtdistjwt.service.js:28:20)
at AuthService.login (C:Userstalisnova apimyflakes_apisrcauthauth.service.ts:18:39)
at TokenService.refreshToken (C:Userstalisnova apimyflakes_apisrctokentoken.service.ts:39:37)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at TokenController.refreshToken (C:Userstalisnova apimyflakes_apisrctokentoken.controller.ts:12:16)
at C:Userstalisnova apimyflakes_apinode_modules@nestjscorerouterrouter-execution-context.js:46:28
at C:Userstalisnova apimyflakes_apinode_modules@nestjscorerouterrouter-proxy.js:9:17
My code:
Function refreshToken in the file token.service.ts
async refreshToken(oldToken: string) {
let objToken = await this.tokenRepository.findOne({hash: oldToken})
if (objToken) {
let user = await this.userService.findOneOrFail({email:objToken.email})
return this.authService.login(user)
} else {
return new UnauthorizedException(MessagesHelper.TOKEN_INVALID)
}
}
Function login in the file auth.service.ts
async login(user: UsersEntity) {
const payload = { email: user.email, sub: user.idUser }
const token = this.jwtService.sign(payload) // here!!!
this.tokenService.save(token, user.email)
return {
token: token
};
}
Error is on const token = this.jwtService.sign(payload)
Here is the file jwt.strategy.ts
import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { ExtractJwt, Strategy } from "passport-jwt";
import { jwtConstants } from "../constants";
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: jwtConstants.secret,
});
}
async validate(payload: { sub: any; email: any; }) {
return { id: payload.sub, email: payload.email}
}
}
And here local.strategy.ts
import { Injectable, UnauthorizedException } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { Strategy } from "passport-local";
import { MessagesHelper } from "src/helpers/messages.helper";
import { AuthService } from "../auth.service";
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({ usernameField: 'email' });
}
async validate(email: string, password: string): Promise<any> {
const user = await this.authService.validateUser(email, password);
if(!user)
throw new UnauthorizedException(MessagesHelper.PASSWORD_OR_EMAIL_INVALID)
return user;
}
}
this is the AuthModule where is JwtModule.register
@Module({
imports: [
ConfigModule.forRoot(),
UsersModule,
PassportModule,
TokenModule,
JwtModule.register({
secret: jwtConstants.secret,
signOptions: { expiresIn: '60s' },
}),
],
controllers: [AuthController],
providers: [AuthService, LocalStrategy, JwtStrategy],
exports: [JwtModule, AuthService]
})
export class AuthModule {}
Guys i tried to use images, but i’m new user and i still don’t have a reputation, sorry.
Solution
Doing what @Micael Levi mentioned in the comments worked for me, so it would be:
const token = this.jwtService.sign(payload, jwtConstants.secret)
For future reference, I encountered this issue despite my environment variables being defined (process.env.SECRET_KEY being undefined was a common problem seen in other similar questions). So what I did to fix mine was:
return {
access_token: this.jwtService.sign(payload, { secret: process.env.JWT_SEC }),
};
Answered By — kohjx 96
I am using jwt to create token, but when i login via postman I get the error «Error: secretOrPrivateKey must have a value» from my console. I have attached my login code. Please anyone who can help me
exports.login = (req, res, next) => {
User.findOne({
where: {
email: req.body.email
}
})
.then(user => {
if (!user) {
return res.status(401).json({
message:
"Auth failed!! either the account does't exist or you entered a wrong account"
});
}
bcrypt.compare(req.body.password, user.password, (err, result) => {
if (err) {
return res.status(401).json({
message: "Auth failed",
token: token
});
}
if (result) {
const token = jwt.sign(
{
email: user.email,
password: user.id
},
process.env.JWT_KEY,
{
expiresIn: "1h"
}
);
res.status(200).json({
message: "Auth granted, welcome!",
token: token
});
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
this is my env.json file
{
"env":{
"MYSQL":"jllgshllWEUJHGHYJkjsfjds90",
"JWT_KEY": "secret"
}
}
It looks like your application can’t read the environment variable properly.
I don’t know which package you are using to load environment variables but the simplest way is using dotenv package.
After installing it with npm i dotenv, import it as early as possible in your application main file like this:
require("dotenv").config();
Create .env file in your application root folder with this content ( as you see the format is key=value)
MYSQL=jllgshllWEUJHGHYJkjsfjds90
JWT_KEY=secret
Then you can access their values like you already did:
process.env.JWT_KEY
.env file:
Remove the process.env.JWT_SECRET_KEY and do it this way: ${process.env.JWT_SECRET_KEY}
wrap it with backtick.
It solved the problem for me.
Had this issue with NestJS when trying to rely on process.env.X
. Supposedly @nestjs/config
uses dotenv
in the background but it doesn’t work as expected. I either had to use ConfigService
or explicitly configure dotenv
in the given files:
jwt.strategy.ts
import * as dotenv from 'dotenv';
dotenv.config();
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
secretOrKey: process.env.JWT_SECRET,
});
}
}
or
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(configService: ConfigService) {
super({
secretOrKey: configService.get<string>('JWT_SECRET'),
});
}
}
auth.module.ts
import * as dotenv from 'dotenv';
dotenv.config();
@Module({
imports: [
JwtModule.register({
secret: process.env.JWT_SECRET,
}),
],
})
or
@Module({
imports: [
JwtModule.registerAsync({
imports: [ConfigModule]
useFactory: async (configService: ConfigService) => {
return {
secret: configService.get<string>('JWT_SECRET'),
};
},
inject: [ConfigService],
}),
],
})
It works for me only if I concatenate it with an empty string like this:
"" + process.env.JWT_KEY
simply remove the process.env.JWT_KEY and replace with «secret key using» ES6 String Literals
${process.env.JWT_SECRET_KEY}
it solves this for me
Are you sure process.env.JWT_KEY
has a valid value? I believe it is undefined
or null
.
It was accidentally adding the JWTService
as a provider in my AuthModule
. That, it seems was overriding the defaults I had set up when registering the JWTModule
.
What I had before:
@Module({
imports: [
PassportModule,
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => {
const authConfig = configService.get<AuthenticationConfig>(
PathNames.AUTH,
);
return {
secret: authConfig.accessToken.secret,
signOptions: {
expiresIn: authConfig.accessToken.expiryTime,
},
};
},
}),
],
providers: [
AuthService,
UsersService,
PrismaService,
AuthResolver,
// this was the problem
JwtService,
JWTStrategy,
],
exports: [AuthService],
})
export class AuthModule {}
Put the require(‘dotenv’).config() at the top of the server.js file
This solved for me
You can try this, it works for me.
«» + process.env.JWT_KEY
- Just add back quotes « and use the syntax
${process.env.SECRET_KEY}
in them. - I also advise you to use the cross-env package, after installing which you can make two files with environment variables .development.env and .production.env
To configure cross-env
, just paste this into a package.json
"scripts"
field:
"start": "cross-env NODE_ENV = production nest start",
"start:dev": "cross-env NODE_ENV = development nest start --watch"
it is for nestjs
I simply removed process.env.JWT_KEY and replace with «secret» and its working fine.
You can also try and specify an object with a property the path to the configuration file like
require("dotenv").config({path: '/path_to_env_file'});
I also had the same issue I came to realize that the issue was that I didn’t include dotenv package
so
import * as dotenv from 'dotenv';
then put dotenv.config()
on top before any other implementation
step 1: install dotenv
step 2: import that in app.js:
const dotenv = require("dotenv");
step 3:
dotenv.config(`${process.env.SECRET_KEY}`);
your issue will solve
Check that process.env.JWT_KEY
is defined:
console.log(process.env.JWT_KEY)
I have this issue in backend. To solve I create a init.js file, add my config to process.env and require index.js file
const config = require(`<path of config json file>`)
process.env = { ...process.env, ...config }
require('./index')
then get init.js as entry index in webpack
Do not fix this by prepending an emptystring '' +
to your JWT or by wrapping it inside of ${process.env.YOUR_JWT}
. This resolves the error but doesn’t fix the underlying issue.
The problem is actually that dotenv is being invoked after your module code where you try to read from process.env
, therefore process.env.ANYTHING_HERE
wont have been populated by dotenv yet.
Probably in your entrypoint file (i.e. main.ts
you are calling import
on app.module
before calling into dotenv or nest’s wrapper around it.
The comprehensive fix here is to modify main.ts
to call require('dotenv').config({ path: './.env' })
or however you bootstrap your env, and to do this before you import any other file. That way, all your imports will have process.env
populated with your environment variables before they run.
If you just fix the issue by following the solutions above then you’re going to end up setting your secret key to 'undefined'
because what you’re actually just doing is you’re concating empty string with the env variable for your secret before it exists. It solves the error but clearly wont be the secret you wanted from your config file.
It’s also not a good idea to fix it this way because you’re not solving this issue for your other environment variables either. They still wont be available to your modules where they need them because they’re being included after instead of before, and thus you’re highly likely to have other issues in your project where your environment variables are undefined elsewhere too.
This message was occured to me only when I was running E2E tests. In my case I had to explicitly set as an secret option to fix it.
this.jwtService.sign(tokenPayload, { secret: `${process.env.JWT_SECRET}` });
In my case using process.env.JWT_SECRET_KEY.replace(/\n/g, "n")
worked perfectly fine.
I took this reference from how firebase uses keys stored in .env file to parse it. and they use .replace(/\n/g, "n")
to parse the key.
To chime in on this, I found this error was being caused by the contents of the key I was trying to use. I regularly use a «password generator» for such strings, and it had given me the following to use:
<B#LCK$^7(T^fuaQemeR&6s:#@AA[email protected]?T,c’=+kxT?euCP27R/D=uRm893$=^_h^f={c.)MD#[%zg}$K8_D#D-_/
tb2?Q>RFr(}H:Fp#{&[email protected]<KGB28unz
if the above is placed in between back ticks or speech marks it will not be able to be parsed as a string and will therefore cause this error as described above.
As an extra level of de-bugging make sure you first wrap your key in backticks or parenthesis in order to make sure that an un-parsable string isn’t the issue.
This error is coming because .env files is not accessible in auth.js.
so dont write process.env.JWT_KEY
instead of this just write a random string in single quotes.
eg: ‘iamtheprogrammer’
ERROR:
const token = jwt.sign({_id:user._id}, process.env.SECRET)
CORRECT CODE:
const token = jwt.sign({_id:user._id},'iamtryingtoaddsome')
${process.env.JWT_SECRET_KEY}
it worked for me by placing it in auth.module.ts
Perhaps you use 2 .env files (for development and for production). For example, you use cross-env NODE_ENV=development
in package.json file in "start:dev"
. If it is true, don’t forget add
ConfigModule.forRoot({
envFilePath: `.${process.env.NODE_ENV}.env`
}),
to your module file:
@Module({
imports: [
PassportModule,
ConfigModule.forRoot({
envFilePath: `.${process.env.NODE_ENV}.env`
}),
JwtModule.register({
secret: process.env.JWT_KEY,
signOptions: {
expiresIn: '1h'
},
}),
],
providers: [AuthService, LocalStrategy, JwtStrategy],
exports: [AuthService]
})
export class AuthModule {}
Then you haven’t to add something to file with class JwtStrategy and another.
IN token REPLACE GRAB THE FOLLOWING IN BACKTICKS. THIS SYNTEX WORKED FOR ME TRY THIS.
${process.env.JWT_SECRET_KEY}
Use the code above(image), to overcome your problem.
Comments Section
Did you
console.log
the value?
i have installed the dotenv and required as your comment to me but still i get the same error, see the screen shot i have uploaded to my previous question
@mchomvu did you also create .env file in your root folder?
@mchomvu I added a screenshot to show how .env file must be.
Lastly require(«dotenv»).config(); must be in the first lines of main file (like index.js or app.js)
yes i created an env file but as a json file, is it correct with that format?
Thank you. this worked for me. If your .env file is on point and you have used @nest/config, kindly use this solution.
@elonaire When using
@nestjs/config
you should useConfigService
, see my response.
Don’t do that! A secret has that name for a reason. It’s not a secret anymore once you commit it to the repo.
Don’t do that! A secret has that name for a reason. It’s not a secret anymore once you commit it to the repo.
I think
dotenv.config()
is done behind the scenes when you use Nest.js ConfigXXX classes, happened to me when I usedprocess.env.JWT_XXX
that didn’t get picked up in tests, since there’s nodotenv.config()
anywhere 😂.
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center (/help/how-to-answer).
Thank you! This was the reason why my nestjs implementation didnt work as well…
should be
jwt.sign({_id:user._id},{secret})
there is no overload with string as parameter for options
This is not a safe solution friend! You’ll end up with a secret that’s «undefined» instead of what your actual secret is.
This is not a safe solution friend! You’ll end up with a secret that’s «undefined» instead of what your actual secret is.
This is not a safe solution friend! You’ll end up with a secret that’s «undefined» instead of what your actual secret is.
This is not a safe solution friend! You’ll end up with a secret that’s «undefined» instead of what your actual secret is.
This is not a safe solution friend! You’ll end up with a secret that’s «undefined» instead of what your actual secret is.
This is not a safe solution friend! You’ll end up with a secret that’s «undefined» instead of what your actual secret is.
This is not a safe solution friend! You’ll end up with a secret that’s «undefined» instead of what your actual secret is.
Related Topics
mysql
node.js
express
Mentions
Bguiz
Ankit Singh
Shihab
Ahmnouira
Suleyman Sah
Thisismydesign
Kostis
Secret
Ashish Pal
Prathibha Chiranthana
Shallon Kobusinge
Binyam G Kidan
Mchomvu
Rabo Yusuf
Amjad Alshihabi
Naveen Perpaga
Trendy Tv
Leithon English
Baba Ya Ga
Damsel Of Distress
Developer Waseem
Elmatsidis Paul
David Zorychta
Ahmed Mohamed
Himalayansailor
References
stackoverflow.com/questions/58673430/error-secretorprivatekey-must-have-a-value