how to get expiry time in JWT?
I want to notice every user the expiration time of login token, right now i could display their userId, email id but unable to display expiresIn timings. Please point out where should i edit my code to display expiresIn field.
router.post("/login", (req, res, next) => {
User.find({ username: req.body.username })
.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(
{
username: user[0].username,
email: user[0].email,
userId: user[0]._id,
},
process.env.JWT_KEY,
{
expiresIn: "10h"
}
);
return res.status(200).json({
message: "Auth successful",
token: token,
userId: user[0]._id,
expiresIn: user[0].exp,
});
}
res.status(401).json({
message: "Auth failed"
});
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
This is the output i m getting as of now.
{
"message": "Auth successful",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImRhcnNoYW4iLCJlbWFpbCI6ImRhcnNoYW5hbi4yNEBnbWFpbC5jb20iLCJ1c2VySWQiOiI1YjU3MTcxNmRjODlhYzZiNGUyY2E0MTciLCJpYXQiOjE1Mzg5ODEyOTYsImV4cCI6MTUzONn0.k0PVole653f_pB3CrQLtdUmv7-c00x1irbQph2i2XaE",
"userId": "5b571716dc89ac6b4e2ca417" ,
"email": "darsh4#gmail.com"}
Look like you have not sent the proper expiration response.
let expiration = '10h'
const token = jwt.sign(
{
username: user[0].username,
email: user[0].email,
userId: user[0]._id,
},
process.env.JWT_KEY,
{
expiresIn: expiration
}
);
return res.status(200).json({
message: "Auth successful",
token: token,
userId: user[0]._id,
expiresIn: expiration, //set expiration time
});
Reference link that helps you : here
Related
When ever i tried to post this API via postman using email and password.
I get User Not Found. I cross checked email and password 100+ times.
In command prompt i am getting UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client.
What i am doing wrong here. Please tell me, Thank you in advance.
exports.loginUser = async (req, res) => {
try{
const user = await userTable.findOne({ email:req.body.email });
if (!user){
res.send({
status: 404,
message: "User Not Found"
});
}
const hashpass = cryptr.decrypt(user.password);
if (hashpass == req.body.password){
const accessToken = jwt.sign({ id: user._id, email: user.email }, process.env.JWT_PASS, {expiresIn:"1d"});
res.send({
status: 200,
data: user,
jwt: accessToken
});
}else{
res.send({
status: 404,
message: "Wrong password"
});
}
}catch(err){
res.status(500).send({
status: 0,
message: "catch issue" + err.message
});
};
};
I'm pretty sure this is happening because your handler continues after sending a response, therefore attempting to modify a response after it's already been sent.
Keep in mind, it could be how you are using the loginUser function. If you wouldn't mind updating your question showing us how you are using it, that would be super helpful!
There are a couple things that come to mind; 1. You may need to add a return statement somewhere, like on line 10/11 for example 2. You could also wrap the code after the first if statement inside an else statement to see if that changes things.
At the end of the day, I'm pretty sure your code is continuing after sending a response, which is why you see that error.
With return
exports.loginUser = async (req, res) => {
try {
let dataToSend = {};
const user = await userTable.findOne({ email: req.body.email });
if (!user) {
res.send({
status: 404,
message: "User Not Found",
});
// Try adding this
return;
}
const hashpass = cryptr.decrypt(user.password);
if (hashpass == req.body.password) {
const accessToken = jwt.sign({ id: user._id, email: user.email }, process.env.JWT_PASS, { expiresIn: "1d" });
res.send({
status: 200,
data: user,
jwt: accessToken,
});
} else {
res.send({
status: 404,
message: "Wrong password",
});
}
} catch (err) {
res.status(500).send({
status: 0,
message: "catch issue" + err.message,
});
}
};
Wrap in else
This is very ugly and should only be used to test in my opinion!
exports.loginUser = async (req, res) => {
try {
const user = await userTable.findOne({ email: req.body.email });
if (!user) {
res.send({
status: 404,
message: "User Not Found",
});
} else {
const hashpass = cryptr.decrypt(user.password);
if (hashpass == req.body.password) {
const accessToken = jwt.sign({ id: user._id, email: user.email }, process.env.JWT_PASS, { expiresIn: "1d" });
res.send({
status: 200,
data: user,
jwt: accessToken,
});
} else {
res.send({
status: 404,
message: "Wrong password",
});
}
}
} catch (err) {
res.status(500).send({
status: 0,
message: "catch issue" + err.message,
});
}
};
I am not getting any response while calling login api from nodejs.
I am handling the catch in frontend as well.
How to get Invalid Credentials message from backend API if credentials doesn't matched.
my Backend login API is -
// api to login user
router.post('/login', function (req, res) {
const valid = validator.isEmail(req.body.email)
if (valid == false) {
var message = { "Success": 0, "Message": "Please enter a valid email." };
res.send(message)
}
userObj.findOne({
email: req.body.email
}).then(user => {
if (!user) {
var message = { "Success": 0, "Message": "User does not exists." };
res.send(message)
} else {
// console.log(bcrypt.compare(req.body.password, user.password))
// var message = { "Success": 1, "User": user };
// res.send(message)
bcrypt.compare(req.body.password, user.password)
.then(isMatch => {
if (isMatch) {
const payload = {
name: user.name,
id: user._id,
email: user.email
}
jwt.sign(payload, 'secret', {
expiresIn: 3600
}, (err, token) => {
if (err) console.error('There is some error in token', err);
else {
res.json({
Success: 1,
token: `${token}`
})
}
})
}
else {
res.json({
Success: 0,
Message: 'Invalid Credentials'
})
}
})
}
})
});
my frontend login action code is -
// Login - get user token
export const loginUser = user => dispatch => {
return axios
.post("http://18.207.190.61:4000/login", {
email: user.email,
password: user.password
})
.then(res => {
// Save to localStorage
// Set token to localStorage
localStorage.setItem("usertoken", res.data.token);
// Set token to Auth header
setAuthToken(res.data.token);
// Decode token to get user data
const decoded = jwt_decode(res.data.token);
// Set current user
localStorage.setItem("username", decoded.name);
dispatch(setCurrentUser(decoded));
return res.data;
})
.catch(err => {
return err;
});
};
finally my login component code is -
this.props.loginUser(user).then((res, err) => {
if (res.Success == "0") {
this.setState({
loading: false,
message: res.Message
});
}
});
How can I get message Message: 'Invalid Credentials' from backend API in front end to print.
Please return response with status codes '200' for success and '401' for invalid credentials and try again. Axios recognises the status codes and tells if there is an error.
if(success)
res.status(200).json({
Success: 1,
token: '${token}'
})
else
res.status(401).json({
Success: 0,
Message: 'Invalid Credentials'
})
Try this once.
1.Remove the catch block in your login action code
2. change your login component code to
this.props.loginUser(user).then((res ) => {
if (res.Success == "0") {
this.setState({
loading: false,
message: res.Message
});
}
});
I am trying to send a verification email with node mailer and send grid when the user signs up. I've implemented needed functions inside User create function. Also added some parts inside login function to check if user email is verified and only after that let them log in. However, it shows some syntax errors in the auth controller and I can't figure out what is exactly wrong?
Here is the code:
Auth Controller
module.exports = {
async CreateUser(req, res) {
const schema = Joi.object().keys({
username: Joi.string()
.min(4)
.max(10)
.required(),
email: Joi.string()
.email()
.required(),
password: Joi.string()
.min(5)
.required(),
});
const { error, value } = Joi.validate(req.body, schema);
if (error && error.details) {
return res.status(HttpStatus.BAD_REQUEST).json({ msg: error.details })
}
const userEmail = await User.findOne({
email: Helpers.lowerCase(req.body.email)
});
if (userEmail) {
return res
.status(HttpStatus.CONFLICT)
.json({ message: 'Email already exist' });
}
const userName = await User.findOne({
username: Helpers.firstUpper(req.body.username)
});
if (userName) {
return res
.status(HttpStatus.CONFLICT)
.json({ message: 'Username already exist' });
}
return bcrypt.hash(value.password, 10, (err, hash) => {
if (err) {
return res
.status(HttpStatus.BAD_REQUEST)
.json({ message: 'Error hashing password' });
}
const body = {
username: Helpers.firstUpper(value.username),
email: Helpers.lowerCase(value.email),
password: hash,
};
User.create(body)
.then(user => {
const token = jwt.sign({ data: user }, dbConfig.secret, {
expiresIn: '5h'
});
res.cookie('auth', token);
res
.status(HttpStatus.CREATED)
.json({ message: 'User created successfully', user, token });
var token = new Token({ _userId: user._id, token: crypto.randomBytes(16).toString('hex') });
token.save(function (err) {
if (err) { return res.status(500).send({ msg: err.message }); }
var transporter = nodemailer.createTransport({
service: 'Sendgrid',
auth: { user: process.env.SENDGRID_USERNAME, pass: process.env.SENDGRID_PASSWORD } });
var mailOptions = {
from: 'no-reply#yourwebapplication.com',
to: user.email, subject: 'Account Verification Token',
text: 'Hello,\n\n' + 'Please verify your account by clicking the link: \nhttp:\/\/' + req.headers.host + '\/confirmation\/' + token.token +
transporter.sendMail(mailOptions, function (err) {
if (err) { return res.status(500).send({ msg: err.message }); }
res.status(200).send('A verification email has been sent to ' + user.email + '.');
})
})
})
.catch(err => {
res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ message: 'Error occured' });
});
});
},
async LoginUser(req, res) {
if (!req.body.username || !req.body.password) {
return res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ message: 'No empty fields allowed' });
}
await User.findOne({ username: Helpers.firstUpper(req.body.username) })
.then(user => {
if (!user) {
return res
.status(HttpStatus.NOT_FOUND)
.json({ message: 'Username not found' });
}
return bcrypt.compare(req.body.password, user.password).then(result => {
if (!result) {
return res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ message: 'Password is incorrect' });
}
if (!user.isVerified)
return res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ message: 'Email is not verified' });
const token = jwt.sign({ data: user }, dbConfig.secret, {
expiresIn: '5h'
});
res.cookie('auth', token);
return res
.status(HttpStatus.OK)
.json({ message: 'Login successful', user, token });
});
})
.catch(err => {
return res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ message: 'Error occured' });
});
}
};
VS shows the first syntax error in this line:
transporter.sendMail(mailOptions, function (err) {
if (err) { return res.status(500).send({ msg: err.message }); }
res.status(200).send('A verification email has been sent to ' + user.email + '.');
})
}) <====== under this ) I don't know what is wrong with it but it shows red line under that )
})
I don't think that ) is the only error in this code. How can I fix this one and other ones if there are others?Here is the model as well:
const userSchema = mongoose.Schema({
username: { type: String },
email: { type: String },
isVerified: { type: Boolean, default: false },
password: { type: String },
and token model
const tokenSchema = new mongoose.Schema({
_userId: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' },
token: { type: String, required: true },
createdAt: { type: Date, required: true, default: Date.now, expires: 43200 }
});
module.exports = mongoose.model('Token', tokenSchema);
Whhat shoud be fixed in this code to make the auth functions work as expected?
How can I fix the above mentioned ) syntax error and other ones as well if they are visible by eye?
for the typo, you just forgot to close the mailOptions object curly brace.
also, make sure all the time to send a response to the client only ones so res.send() should be written one time when all callbacks are done, considering error handling for sure.
here is an update for User.creat function where you can see the res.send place is changed:
User.create(body)
.then(user => {
const token = jwt.sign({ data: user }, dbConfig.secret, {
expiresIn: '5h'
});
res.cookie('auth', token);
var token = new Token({ _userId: user._id, token: crypto.randomBytes(16).toString('hex') });
token.save(function (err) {
if (err) { return res.status(500).send({ msg: err.message }); }
var transporter = nodemailer.createTransport({
service: 'Sendgrid',
auth: { user: process.env.SENDGRID_USERNAME, pass: process.env.SENDGRID_PASSWORD }
});
var mailOptions = {
from: 'no-reply#yourwebapplication.com',
to: user.email, subject: 'Account Verification Token',
text: 'Hello,\n\n' + 'Please verify your account by clicking the link: \nhttp:\/\/' + req.headers.host + '\/confirmation\/' + token.token
}
transporter.sendMail(mailOptions, function (err) {
if (err) { return res.status(500).send({ msg: err.message }); }
res.status(HttpStatus.CREATED).json({ message: 'User created successfully', user, token }); // or you can send another response as you like here
})
})
})
.catch(err => {
res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ message: 'Error occured' });
});
I am trying to send a confirmation email when the user signs up. The interesting fact is that when I click sign up button it works creates the user and navigates to the page where it should navigate. But it doesn't send verification email and in command prompt shows the following error:
Can't set headers after they are sent to the client
Here is the code:
Auth controller
module.exports = {
async CreateUser(req, res) {
const schema = Joi.object().keys({
username: Joi.string()
.min(4)
.max(10)
.required(),
email: Joi.string()
.email()
.required(),
password: Joi.string()
.min(5)
.required(),
});
const { error, value } = Joi.validate(req.body, schema);
if (error && error.details) {
return res.status(HttpStatus.BAD_REQUEST).json({ msg: error.details })
}
const userEmail = await User.findOne({
email: Helpers.lowerCase(req.body.email)
});
if (userEmail) {
return res
.status(HttpStatus.CONFLICT)
.json({ message: 'Email already exist' });
}
const userName = await User.findOne({
username: Helpers.firstUpper(req.body.username)
});
if (userName) {
return res
.status(HttpStatus.CONFLICT)
.json({ message: 'Username already exist' });
}
return bcrypt.hash(value.password, 10, (err, hash) => {
if (err) {
return res
.status(HttpStatus.BAD_REQUEST)
.json({ message: 'Error hashing password' });
}
const age = moment().diff(moment([value.byear, value.bmonth - 1, value.bday]), 'years');
const body = {
username: Helpers.firstUpper(value.username),
email: Helpers.lowerCase(value.email),
password: hash,
};
User.create(body)
.then(user => {
const token = jwt.sign({ data: user }, dbConfig.secret, {
expiresIn: '5h'
});
res.cookie('auth', token);
res
.status(HttpStatus.CREATED)
.json({ message: 'User created successfully', user, token });
var emailtoken = new emailToken({ _userId: user._id, emailtoken: crypto.randomBytes(16).toString('hex') });
emailtoken.save(function (err) {
if (err) { return res.status(500).send({ msg: err.message }); }
var transporter = nodemailer.createTransport({
service: 'Sendgrid',
auth: { api_key:'api key is here' }
});
var mailOptions = {
from: 'email#email.com',
to: user.email, subject: 'Account Verification Token',
text: 'Hello,\n\n' + 'Please verify your account by clicking the link: \nhttp:\/\/' + req.headers.host + '\/confirmation\/' + emailtoken.emailtoken
}
transporter.sendMail(mailOptions, function (err) {
if (err) { return res.status(500).send({ msg: err.message }); }
res.status(HttpStatus.CREATED).json({ message: 'User created successfully', user, emailtoken }); // or you can send another response as you like here
})
})
})
.catch(err => {
res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ message: 'Error occured' });
});
});
},
async LoginUser(req, res) {
if (!req.body.username || !req.body.password) {
return res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ message: 'No empty fields allowed' });
}
await User.findOne({ username: Helpers.firstUpper(req.body.username) })
.then(user => {
if (!user) {
return res
.status(HttpStatus.NOT_FOUND)
.json({ message: 'Username not found' });
}
return bcrypt.compare(req.body.password, user.password).then(result => {
if (!result) {
return res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ message: 'Password is incorrect' });
}
if (!user.isVerified)
return res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ message: 'Email is not verified' });
const token = jwt.sign({ data: user }, dbConfig.secret, {
expiresIn: '5h'
});
res.cookie('auth', token);
return res
.status(HttpStatus.OK)
.json({ message: 'Login successful', user, token });
});
})
.catch(err => {
return res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ message: 'Error occured' });
});
}
};
user model:
const userSchema = mongoose.Schema({
username: { type: String },
email: { type: String },
isVerified: { type: Boolean, default: false },
password: { type: String },
email Token model
const tokenSchema = new mongoose.Schema({
_userId: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' },
emailtoken: { type: String, required: true },
createdAt: { type: Date, required: true, default: Date.now, expires: 43200 }
});
module.exports = mongoose.model('emailToken', tokenSchema);
According to the command prompt, there should be something wrong somewhere here in auth controller:
var transporter = nodemailer.createTransport({
service: 'Sendgrid',
auth: { api_key:'api key is here' }
});
var mailOptions = {
from: 'email#email.com',
to: user.email, subject: 'Account Verification Token',
text: 'Hello,\n\n' + 'Please verify your account by clicking the link: \nhttp:\/\/' + req.headers.host + '\/confirmation\/' + emailtoken.emailtoken
}
transporter.sendMail(mailOptions, function (err) {
if (err) { return res.status(500).send({ msg: err.message }); }
res.status(HttpStatus.CREATED).json({ message: 'User created successfully', user, emailtoken }); // or you can send another response as you like here
})
})
})
Also In the front-end, I am using Angular.
What is wrong? Why doesn't it send an email and throws that strange error?
I want to update token in user collection when the user logs in. So far, I have tried this.
router.post("/login", (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 token2 = jwt.sign(
{
email: user[0].email,iat: Math.floor(Date.now() / 1000) - 30
},
"123",
{
expiresIn: "1h"
}
);
User.update({token : token2 })
.exec()
return res.status(200).json({
message: "Auth successful",
token: token2
});
}
res.status(401).json({
message: "Auth failed"
});
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
here new token is getting generated but it is not being saved in the user collection. I want to update new token in the collection.
Can anyone know where I am missing?
Try Something like:
router.post("/login", async (req, res) => {
try{
const user = await User.find({ email: req.body.email });
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 token2 = jwt.sign(
{
email: user[0].email,iat: Math.floor(Date.now() / 1000) - 30
},
"123",
{
expiresIn: "1h"
}
);
User.update({_id:user[0]._id},{$set:{token : token2 }},{new: true});
return res.status(200).json({
message: "Auth successful",
token: token2
});
}
res.status(401).json({
message: "Auth failed"
});
});
}
catch(err){
res.status(500).json({
error: err
});
}
});
Try below code:
jwt.sign(
{
email: user[0].email,iat: Math.floor(Date.now() / 1000) - 30
},
"123",
{
expiresIn: "1h"
}, function(err, token2) {
User.update({token : token2 }).exec() //Change the query to update
return res.status(200).json({
message: "Auth successful",
token: token2
});
}
);
Let me know if it helps.
Firstly understand why do you need to refresh the token. Once you create the token you can store that token as a session variable in a web, internal storage of your mobile etc...
Instead of refresh of token you can create another token.
In another way you can set timeout for the token. Token will be invalid after that time period.