Cannot return JWT token [duplicate] - node.js

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I've built this sign in route that creates a JWT token, but outside of the function that creates it, the token is undefined, so I'm unable to use it outside of that function.
router.post('/login', (req, res) => {
const { errors, isValid } = validateLoginInput(req.body);
if (!isValid) {
return res.status(400).json(errors);
}
const email = req.body.email;
const password = req.body.password;
User.findOne({ email }).then(user => {
if (!user) {
return res.status(404).json({ emailnotfound: "Email not found"});
}
bcrypt.compare(password, user.password).then(isMatch => {
if (isMatch) {
const payload = {
id: user.id,
name: user.name
};
var token =jwt.sign(
payload,
keys.secretOrKey,
{ expiresIn: 31556926 },
(err, token) => {
res.json({
success: true,
token: "Bearer " + token
});
});
console.log(token)
} else {
return res.status(400).json({ passwordincorrect:
"Password incorrect"});
}
});
});
});
When the code hits that console.log statement, it shows that the token is undefined instead of returning the token.

It seems you might have a problem with the token being created. The code is not checking if the token got created successfully.
(err, token) => {
res.json({
success: true,
token: "Bearer " + token
})
}
Change this to :
(err, token) => {
if (err){
console.log("Error while creating token:"+err.message);
console.error(err);
//send an error response as well if needed.
} else {
res.json({
success: true,
token: "Bearer " + token
})
}
This will help you figure out what the problem might be.

The problem seems to be here:
var token =jwt.sign(
payload,
keys.secretOrKey,
{ expiresIn: 31556926 },
(err, token) => {
res.json({
success: true,
token: "Bearer " + token
});
});
Can you check the JWT library that you are using to see if it actually accepts a callback? The correct syntax on most JWT libraries for the sign function is:
var token = jwt.sign(payload, secretKey, options);
console.log("Token :" + token);
The options usually contains an expiresIn defined in seconds.

Related

Jsonwebtoken : invalid token

I setup JWT in my express app and I get this error : invalid token. But when I copy this token to jwt.io, exp date is correct and data ok.
Here is my controller :
module.exports.signIn = async (req, res) => {
const { email, password } = req.body;
const user = await UserModel.findOne({ email });
if (!user) {
return res.status(400).json({
message: "User not found."
});
}
const isValidPassword = await bcrypt.compare(password, user.password);
if (!isValidPassword) {
return res.status(400).json({
message: "Invalid password."
});
}
const token = jwt.sign({ id: user._id }, process.env.SECRET_KEY, {
expiresIn: "10h"
});
res.status(200).json({
message: "User logged in.",
token
});
};
And my middleware :
const jwt = require("jsonwebtoken");
module.exports.verifyToken = (req, res, next) => {
const token = req.headers.authorization;
console.log(token);
if (!token) {
return res.status(403).json({
message: "No token provided."
});
}
jwt.verify(token, process.env.SECRET_KEY, (err, decoded) => {
if (err) {
return res.status(401).json({
message: "Unauthorized."
});
}
req.userId = decoded.id;
next();
});
};
What I do is :
sign in
copy token to Headers authorizations
make my get request to another route that has the verifyToken
And then I get the error.
When I console.log(token) I get Bearer ...[the token]
Any idea why ?
You need four arguments in the verify function
jwt.verify(token, process.env.PUBLIC_KEY, {}, (err, decoded) => {
if (err !== null && err instanceof TokenExpiredError) {
cb('TOKEN_EXP');
return Json.builder(Response.HTTP_UNAUTHORIZED_TOKEN_EXP);
}
if (err instanceof JsonWebTokenError) {
cb('IN_VALID_TOKEN');
return Json.builder(Response.HTTP_UNAUTHORIZED_INVALID_TOKEN);
}
cb(decoded);
});

Call a normal function from a export function but the return result is not projected properly [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
I am quite new to Node JS, I have some problem to retrieve the accessToken from the generateAccessToken method when it called from the login exports function. After I compile the code, it shows an undefined value in the console log.
I have attempted to add async on the generateAccessToken and the export function but both of them are not working. I have validated the accessToken can be printed within the generateAccessToken function.
exports.generateAccessToken = function(req,res) {
const user = {
username: req.body.username
};
const accessToken = jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15s' });
User.update({ access_token: accessToken }, { where: { username: user.username } }).then((resp) => {
//console.log(resp);
if (resp != 1) {
res.status(403).send({ error: "Failed to update access token" });
}
return accessToken;
}).catch((err) => {
res.status(500).send({ error: err.message });
});
}
exports.login = function (req, res) {
const username = req.body.username;
const accessToken = module.exports.generateAccessToken(req,res);
console.log(accessToken); //return undefined value here.
const refreshToken = generateRefreshToken(username);
return res.status(200).send({ message: "You have successfully login!", refreshToken: refreshToken, accessToken: accessToken });
}
Update:
exports.generateAccessToken = function(req,res) {
const user = {
username: req.body.username
};
const accessToken = jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15s' });
User.update({ access_token: accessToken }, { where: { username: user.username } }).then((resp) => {
console.log(resp);
if (resp != 1) {
res.status(403).send({ error: "Failed to update access token" });
}
}).catch((err) => {
res.status(500).send({ error: err.message });
});
return accessToken;
}
exports.login = async function (req, res) {
const username = req.body.username;
const accessToken = await module.exports.generateAccessToken(req,res);
const refreshToken = generateRefreshToken(username);
return res.status(200).send({ message: "You have successfully login!", refreshToken: refreshToken, accessToken: accessToken });
}
You are not returning accessToken generateAccessToken. You return it in the then-part of User.update but that doesn’t mean it becomes the return value of generateAccesToken.

How to get current user in another file while working on postman and node.js

I have this code for login. How do I use the current user's information from this code to another file using postman and node.js?
exports.loginUser = (req,res, next) => {
User.find({email: req.body.email})
.exec()
.then(user => {
if(user.length < 1) {
return res.status(401).json({
message: 'Auth failed'
});
}
bcrypt.compare(req.body.password, user[0].password, (err ,result) => {
if(err){
return res.status(401).json({
message: 'Auth failed'
});
}
if (result) {
const token = jwt.sign({
email: user[0].email,
userId: user[0]._id
},
process.env.JWT_KEY ,
{
//options
expiresIn: "1h"
});
You should tell exactly what you want, what you said is confusing, but If you mean how to pass the logged in user to the next middleware, you gotto assign the user to req
exports.loginUser = async (req, res, next) => {
const user = await User.find({ email: req.body.email }).exec()
if (user.length < 1) {
return res.status(401).json({
message: 'Auth failed'
});
}
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
return res.status(401).json({
message: 'Auth failed'
});
}
if (result) {
const token = jwt.sign({
email: user[0].email,
userId: user[0]._id
},
process.env.JWT_KEY, {
//options
expiresIn: "1h"
});
req.user = user[0];
return next();
}
})
}
Then in the next middleware you have access to logged in user, using req.user.
UPDATE:
To implement the functionality that you want, according to what you described in the comment:
Before anything import these packages:
const jwt = require("jsonwebtoken");
const { promisify } = require("util");
First you implement a route that checks for credentials and sends back a signed jwt:
exports.login = CatchAsync(async(req, res, next) => {
const { email, password } = req.body;
if (!email || !password) {
return next(new Error("Please provide email and password"));
}
const user = await UserModel.findOne({email});
if (!user) {
return next(new Error("There is no user with that email"));
}
if(!(await bcrypt.compare(password, user.password))) {
// actually the pass is not correct but for security reasons we don't say that
return next(new Error("Email or password is not correct");
}
// pass the user id to jwt so later can identify user
const token = jwt.sign({ id: user._id }, 'yourJwtSecret', {
expiresIn: '90d',
});
// httpOnly prevents access to token in client's browser, so it is safe
const cookieOptions = {
expires: new Date(
Date.now() + 90 * 24 * 60 * 60 * 1000
),
httpOnly: true,
};
res.cookie("jwt", token, cookieOptions);
res.status(200).json({
status: 'success',
message: 'logged in successfully'
});
});
Then for every route that needs to check for logged In user, use this middleware:
exports.isLoggedIn = CatchAsync(async(req, res, next) => {
// Check if there is a token
// if no token was provided it means user is not logged in
let token;
if (req.cookies.jwt) {
token = req.cookies.jwt;
} else {
return next();
}
// Verify token
// decoded now has access to id of user
let decoded;
try {
decoded = await promisify(jwt.verify)(token, 'yourJwtSecret');
} catch (err) {
// if token was modified or expired or not valid
return next();
}
// get the user
const user = await UserModel.findOne({
_id: decoded.id
});
// access granted, user is logged in
req.user = user; // you can access the logged in user in the next middleware
res.locals.user = user; // you can access the logged in user in template engines
next();
});
If the user is not logged in, req.user won't be assigned. therefore in next middlewares if req.user was undefined you know user is not logged in.
for more info jwt docs.
If you have never taken any NodeJs course, I'd recommend this course

Refreshing JWT access token with refresh token within single middleware function on a post route

I'm trying to learn JWT authentication in express and one thing that I'm came across this code from Github
that this guy has initialised an middleware function to authenticate and check expiry of access token as per below:
app.post("/protected", auth, (req, res) => {
return res.json({ message: "Protected content!" });
})
async function auth(req, res, next) {
let token = req.headers["authorization"];
token = token.split(" ")[1]; //Access token
jwt.verify(token, "access", async (err, user) => {
if (user) {
req.user = user;
next();
} else if (err.message === "jwt expired") {
return res.json({
success: false,
message: "Access token expired"
});
} else {
console.log(err);
return res
.status(403)
.json({ err, message: "User not authenticated" });
}
});
}
and a separate route for refreshing the access token with the help of refresh token
app.post("/refresh", (req, res, next) => {
const refreshToken = req.body.token;
if (!refreshToken || !refreshTokens.includes(refreshToken)) {
return res.json({ message: "Refresh token not found, login again" });
}
// If the refresh token is valid, create a new accessToken and return it.
jwt.verify(refreshToken, "refresh", (err, user) => {
if (!err) {
const accessToken = jwt.sign({ username: user.name }, "access", {
expiresIn: "20s"
});
return res.json({ success: true, accessToken });
} else {
return res.json({
success: false,
message: "Invalid refresh token"
});
}
});
});
So, my question is how secure it is and how can I create single middleware function that could do both authentication and refreshing access token without hitting the app.post('/refresh') as in my view it wouldn't be a smooth experience to deal with it in frontend API management within react
Edit
My middleware seems to work well but it doesn't identify the wrong refresh token and then actually getting worked on protected route
app.post('/home', authenticateUser, (req, res) => {
res.send('welcome');
});
async function authenticateUser(req, res, next) {
let token = req.headers['authorization'];
token = token.split(' ')[1];
jwt.verify(token, JWT_AUTH_TOKEN, async (err, phone) => {
if (phone) {
req.phone = phone;
next();
} else if (err) {
const refreshToken = req.body.refreshToken;
if (!refreshToken || !refreshTokens.includes(refreshToken)) {
return res.json({ message: 'Refresh token not found, login again' });
} else {
jwt.verify(refreshToken, JWT_REFRESH_TOKEN, (err, phone) => {
if (!err) {
const accessToken = jwt.sign({ phone }, JWT_AUTH_TOKEN, { expiresIn: '30s' });
return res.json({ success: true, accessToken });
} else {
return res.json({
success: false,
message: 'Invalid refresh token'
});
}
next();
});
}
} else {
console.log(err);
return res.status(403).json({ err, message: 'User not authenticated' });
}
});
}

JWT gives JsonWebTokenError "invalid token"

I have used jsonwebtoken for token verification in my Node Application .
Here jwt.sign works perfectly . But when jwt.verify gives following error
"auth": false,
"message": {
"name": "JsonWebTokenError",
"message": "invalid token"
}
}
Here is my Post and Get Router
router.post('/signup',(req,res)=>{
const body = _.pick(req.body,['username','email_id','name','college','password','dob','gender','city','joinedOn','bio']);
User.findOne({'username':body.username},function(err,user){
if(err){
res.status(404).send(err)
}else if(user){
res.status(404).send('User with Username Exists')
}else{
var user = new User(body);
user.save().then((user) => {
var token = jwt.sign({ username: user.username},'secret', {
"algorithm": "HS256",
expiresIn: 86400 // expires in 24 hours
});
res.status(200).send({ auth: true, token: token });
}, (e) => {
res.status(400).send(e)
})
}
})
});
router.get('/me', VerifyToken, function(req, res) {
User.findOne({username:req.username}, function (err, user) {
if (err) return res.status(500).send(err);
if (!user) return res.status(404).send("No user found.");
res.status(200).send(user);
});
});
Below is verifyToken Function
function verifyToken(req, res, next) {
var token = req.headers['x-access-token'];
if (!token)
return res.status(403).send({ auth: false, message: 'No token provided.' });
console.log(token)
jwt.verify(token,'secret', function(err, decoded) {
if (err)
return res.status(500).send({ auth: false, message: err });
//req.username = decoded.username;
console.log(decoded)
next();
});
}
I can't figure out what's wrong in my program .Any suggestions would be appreciated .
Thanks
If you are passing in a token to your jwt.verify function like so Bearer *************...., ensure to split the token first before passing it in to jwt by doing
const token = req.headers.authorization.split(' ')[1];
jwt.verify(token)
Hope this helps someone.
My Code is true . The mistake I was doing that I was giving access token with double quote("token") in Postman. That's why postman was giving me following error
"auth": false, "message": { "name": "JsonWebTokenError", "message": "invalid token" } }
I had the same issue. Basically the token should not have brearer information. When I stripped it out it started working as expected.
For instance:
Failed when I used brearer *************....
Worked when I used *************....
I had a similar error because I persisted the token in localStorage with JSON.stringify, which adds two double quotes to the token, hence resulting in an invalid token when verifying it.
// What caused the error
localStorage.setItem('jwt', JSON.stringify(token));
Solution, either omit JSON.stringify, or parse the token when verifying:
localStorage.setItem('jwt', token);
// or
const token = JSON.parse(localStorage.getItem('jwt'));
const token = req.header('token');
try {
const decoded = jwt.verify(JSON.parse(token), privateKey);
console.log(decoded)
} catch(err) {
console.log('err', err)
}
when you pass token from service convert into JSON.parse(token) from local storage then pass to verify

Resources