Whenever a user registers i am sending him an email which contains the link which user needs to click to get verified. I am passing a token in that link. When the user clicks the link he should get verified but i am not able to do this. I can just retrieve the token from the link but i am unable to find the user in the database and update the value.
Here is my code:
router.route('/verify')
.get(isNotAuthenticated, function(req, res){
var verifyToken = req.query.id;
var user = User.findOne({ 'secretToken': verifyToken });
if (!user) {
req.flash('error', 'No user found with this email id, please check your email id or incorrect link');
res.redirect('/users/register');
return;
}
user.active = true;
user.secretToken = '';
user.save();
req.flash('success', 'Thank you! Now you may login.');
res.redirect('/users/login');
res.redirect('login');
Try using promise to do this instead of assignment.
User.findOne({ 'secretToken': verifyToken })
.then(user => {
// do something with user
})
.catch(err => {
// do something with error
})
If you are using JWT to validate your routes you can:
1 - Generate the link verification with one "hash value"(token), save this token in the user document (user collection).
send the link e.g. "https://site/user/verify?token=3f0621f7e4c41ece51926a40cee4dae0be65ct7"
2 - Disable the security for this route:
app.use(jwt({secret: process.env.SECRET}).unless({path: ['/users/verify']}));
3 - Receive the request to verify the user:
router.put('/verify', bodyParser.json(), function (req, res, next) {
try {
const user = userController.verify(req.query.token);
res.status(200).json(user);
} catch (err) {
next(err);
}
});
4 - Find and update the user(as verified):
User.findOneAndUpdate(
{
token: token,
},{$set:{verified:true}})
.then((result) => {
return result;
}).catch((err) => {
//throw error
});
If you need to wait the update for execute other sttufs, you can use async/wait: Async/Await Tutorial
Related
I am not receiving any errors but when I test my endpoint it's taking longer and no response until I terminate the request. I am sending a user an email with a token that will be used to validate if he exists in the database so that he can change his/her password but I have not succeeded for the last two days. I am frustrated now, I have never done this before.
The Middleware that sends the reset password link
export class sendGridEmail {
static async sendResetPasswordEmail(email, token) {
sendGrid.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: `${email}`,
from: `${process.env.VERIFIED_SENDER}`, // Change to your verified sender
subject: "RESET YOUR PASSWORD",
text: `You are receiving this email because you (or someone else) has requested the reset of a password. Follow this link ${process.env.BASE_URL}/api/resetpassword/${token}`,
};
return sendGrid
.send(msg)
.then(() => {
console.log(`password rest link has been sent to: ${email}`);
})
.catch((err) => {
console.log(err);
});
}
}
The Component that sends the reset password link
export const sendResetPasswordLink = asynchandler(async (req, res) => {
const { email } = req.body;
const user = await userModel.findOne({ email });
if (!user) {
res.status(404);
res.json({ message: "account with this email was not found" });
} else if (user) {
const token = AuthToken(user._id);
try {
await sendGridEmail.sendResetPasswordEmail(user.email, token);
res.status(200);
res.json({
message: `password reset link hase been sent to: ${user.email}`,
});
} catch (error) {
res.status(500);
res.json({ message: error });
}
} else {
res.status(500);
res.json({ message: "Internal Server Error" });
}
});
The Route that tries to save the password. Am I getting it wrong by verifying the token in the params using jwt and then checking if the user exists or am I missing out something ?
export const resetPassword = asynchandler(async (req, res) => {
const { resetToken } = req.params;
const private_key = process.env.PRIVATE_KEY;
const payload = jwt.verify(resetToken, private_key);
const user = await userModel.findById(payload.id);
console.log(payload.id);
if (!user) {
res.status(404);
res.jsonp({ message: "token has expired" });
}else if(user){
user.password= req.body.password
await user.save();
await resetToken.delete();
await sendMessage.sendPasswordResetSuccess(user.number);
res.status(200);
res.json({message:"password changed succesfully"});
}else{
res.status(500)
res.json({message:"no token was procide"})
}
});
The routes
app.post('/api/resetlink', sendResetPasswordLink);
app.put("/api/resetpassword/:resetToken", resetPassword);
not really sure if my title is correct but my problem is that I have this reset password token checker in my api that seems to get affected by another api that finds a specific user, this api has user validation.
Here is what they look like:
//get specific user
router.get('/:id', validateToken, async (req, res) => {
const id = req.params.id
const user = await User.findByPk(id);
res.json(user);
});
//reset-password token check
router.get('/reset-pass', async (req, res) => {
await User.findOne({
where: {
resetPasswordToken: req.body.resetPasswordToken,
resetPasswordExpires: {
[Op.gt]: Date.now()
}
}
}).then(user => {
if(!user) {
res.status(401).json({ error: 'Password reset link is invalid or has expired.'})
} else {
res.status(200).send({
username: user.username,
message: 'Password reset link Ok!'
});
}
});
});
then here is the validateToken
const validateToken = (req, res, next) => {
const accessToken = req.cookies['access-token'];
if (!accessToken)
return res.status(401).json({error: 'User not authenticated!'});
try {
const validToken = verify(accessToken, JWT_SECRET)
req.user = validToken;
if(validToken) {
req.authenticated = true;
return next();
}
} catch(err) {
res.clearCookie('access-token')
return res.status(400).json({error: err}).redirect('/');
}
};
when I comment out the get specific user api the reset password token check works. If I remove validateToken it returns null instead of giving me the username and message.
One of the things I notice is the route param "/:id", that means that literally everything would be processed by get specific user because all routes start with "/", only use params in routes with a prefix like "/user/:id" that way only the routes that starts with "/user" will execute that code.
Change your code to:
//get specific user
router.get('/user/:id', validateToken, async (req, res) => {
const id = req.params.id
const user = await User.findByPk(id);
res.json(user);
});
I am quite new to Node.js / Express and development of web apps. I try to do a simple user registration where I hash the password with bcrypt before saving the hash to mongodb. The login form, which should allow a user to login, does subsequently lookup a user in the db and then compares the two passwords.
Certain routes in my web app I do want to protect so that only authenticated user have access to them. So when successfully login in I do send a Json Web Token (jwt) along the response header which should then be used - when redirected to the protected '/lobby' route - to authenticate the user and allow him / her to proceed to that route.
However, I always get the following error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
So it looks like it already sends back a response to the client before trying to set the header which of course is then not possible anymore.
I would highly appreciate your help here!
I do use the following code:
Register function
async function register(req, res) {
//Check with user already exists
const emailExists = await User.findOne({email: req.body.email});
if(emailExists) {
return res.status(400).send('User already exists!');
};
//Hash the password and create new user from request data
bcrypt.hash(req.body.password, 10, async function (err, hashedPass){
if(err){
res.json({
error: err
});
}
let user = new User({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: hashedPass,
password2: hashedPass
});
try {
await user.save();
}catch (err) {
res.status(400).send(err);
};
});
res.render('index');
};
Login function
async function login(req, res) {
const user = await User.findOne({email: req.body.email});
if(!user) {
return res.status(400).json({message: 'User not found!'}).render('index');
};
bcrypt.compare(req.body.password, user.password).then((result)=> {
if(result){
const token = jwt.sign({_id: user._id}, process.env.TOKEN_SECRET);
res.setHeader('auth-token', token.toString());
res.redirect('/lobby');
}else {
return res.status(400).json({message: 'Passwords do not match!'}).render('index');
}
}).catch((err)=> {
console.log(err);
});
};
As a middleware to the '/lobby' route (i.e. when someone does a get request to '/lobby') I use a "verifyToken" function which should ensure correct authentication of the user via jwt.
verifyToken function
const jwt = require('jsonwebtoken');
module.exports = function(req, res, next) {
console.log('verify function started');
const token = req.header('auth-token');
console.log(token);
if(!token) {
res.status(401).json({
message: 'Access denied!'
});
};
try {
const verified = jwt.verify(token, process.env.TOKEN_SECRET);
req.user = verified;
next();
}catch (err) {
res.status(400).json({
message: 'Invalid token!'
});
};
};
As said, I would very much appreciate your help here! I assume the problem is much simpler than I think it is :-).
Cheers
You forgot to return the response in few cases. So it continues to execute other code aswell, that's where server trying to send the response again, which is why you're getting that error.
Change your response like the following.
verifyToken function
const jwt = require('jsonwebtoken');
module.exports = function(req, res, next) {
console.log('verify function started');
const token = req.header('auth-token');
console.log(token);
if(!token) {
return res.status(401).json({ // <-- here you need to `return`
message: 'Access denied!'
});
};
try {
const verified = jwt.verify(token, process.env.TOKEN_SECRET);
req.user = verified;
next();
}catch (err) {
return res.status(400).json({
message: 'Invalid token!'
});
};
};
Register function
async function register(req, res) {
//Check with user already exists
const emailExists = await User.findOne({email: req.body.email});
if(emailExists) {
return res.status(400).send('User already exists!');
};
//Hash the password and create new user from request data
bcrypt.hash(req.body.password, 10, async function (err, hashedPass){
if(err) {
return res.json({ // <-- here as well
error: err
});
}
let user = new User({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: hashedPass,
password2: hashedPass
});
try {
await user.save();
return res.render('index'); // <-- assuming this is your success response
}catch (err) {
return res.status(400).send(err); <-- here too
};
});
};
Looks like in the Login function the header gets set. I can see this via console.log(res.header('auth-token'));. Subsequently the redirect to "/lobby" gets called because the verifyToken function does start.
However, in the verifyToken function the respective header is then undefined. Because I always also get a 'Access denied!' message.
As said, I do call the verifyToken function as middleware when doing a get request to the /lobby route. The route for '/lobby' looks as follows:
const express = require('express');
const router = express.Router();
const lobbyCtrl = require('../controllers/lobby');
const verify = require('./verifyToken');
router.get('/', verify, lobbyCtrl.display);
module.exports = router;
I have a profile section in my angular app and right now i have 5 users let's say.
I have a route where users have to change the password. I want to verify if users are correctly logged in and has passed authentication and they cannot change password for any other users.
router.get('/change-password/:username', (req, res) => {
User.findOne({
username: req.params.username
}).then(user => {
if (user) {
res.status(200).json(user);
} else if (!user) {
res.status(404).json({
message: 'user not found'
});
}
});
});
what if user A is logged in and he change the parameter to B and then change the password ? is there any way I dont pass parameter and get current user who is logged In
Basically is like this, when you log the user in from back end, you send a response with a token to the front end. You save this token to the local storage to have it in every request to the back end. Them, you use a middleware function to check if the token is provided in the header of the request like a bearer. So the answer is: you don't have to check the auth every request, you just check if the token is provided by middleware and if it is correct.
If you are using express, the most apps use a middleware in the auth service class like this:
module.exports.isAuthorized = function(req, res, next) {
User.findById(req.session.userId).exec(function (error, user) {
if (error) {
return next(error);
} else {
if (user === null) {
var err = new Error('Not authorized! Go back!');
err.status = 400;
return next(err);
} else {
return next();
}
}
});
}
At the node.js routes:
var auth = require('./auth');
// GET route after registering
router.get('/clientPage', auth.isAuthorized, function (req, res, next) {console.log("1114");
res.sendFile(path.join(__dirname + '/../views/clientPage.html'));
});
As you can see, the second param says that before make the request, it will execute the middleware function auth.isAuthorized.
strong textI am building node.js + mongodb rest api. I use jwt user auth and I have a problem. I need to get details of authenticated user (user_id, name), think they can be obtained from token, but I dont know how to do this. How is it possible to do ?
UPDATED
I am doing a post request
router.route('/articles')
.post(function (req, res) {
var article= new Article();
article.user_id = ???; // here needs user_id
article.imdb_id = req.body.imdb_id;
article.title = req.body.title;
article.thumb = req.body.thumb;
article.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Added' });
});
});
I need to insert into articles collection authors id (user_id), but I dont know how to get the authenticated user_id.
Tried to do this:
var token = req.body.token || req.query.token || req.headers['x-access-token'];
if (token) {
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
req.decoded = decoded;
console.log(decoded);
next();
}
});
decoded returns all info about user (name, password, _id). Is it possible to get only user_id and name from here?
When you sign a JSON web token you can pass it a user object. Here you can store whatever user data you need. This object is then signed and encoded and set as the token. When you send a request to your API passing the JWT in the auth header your validation function should return this user object back to you if the JWT is valid.
I like to use the Hapi framework for creating my Restful APIs so I will give an example using Hapi.
In your server.js file you need to register the hapi-auth-jwt2 package:
server.register(require('hapi-auth-jwt2'), (err) => {
if (err) {
throw err;
}
server.auth.strategy('jwt', 'jwt', {
key: config.jwt.secret,
validateFunc: auth.validate,
verifyOptions: { algorithms: ['HS256'] }
});
server.auth.default('jwt');
});
Your validation function:
export default {
validate: (tokenObject, req, callback) => {
validateToken(tokenObject.user_id, (err, user) => {
if (err) {
callback(Boom.unauthorized('User is unauthorized.'), false);
} else {
req.user = user;
callback(null, true);
}
});
}
};
The validateToken function should take the user id that you got from the token and query for the user. If a user is found then you know the token is valid and you can return and store the rest of the user information.
To create a token I use "jsonwebtoken" package:
generateToken: (user_id, name, callback) => {
'use strict';
callback(null, JWT.sign({
user_id: user_id,
name: name
}, config.JWT.SECRET, {
expiresIn: 86400
}));
}
Let's say you need to verify if the token sent from user In the headers already In your Database or not (we're going to call it protect)
const {promisify} = require('util');
const jwt = require('jsonwebtoken');
const User = require('./../models/userModel');
...
exports.protect = catchAsync(async(req, res, next) => {
// 1) Getting token and check if it's there in headers
let token;
//authorization is the name of the header token
if (req.headers.authorization) {
token = req.headers.authorization;
}
if (!token) {
return next(new AppError('You are not logged in! Please Login To get Access.', 401));
}
// 2) Verification Token is a valid token
const decoded = await promisify(jwt.verify)(token, process.env.JWT_SECRET);
// WE CAN GET THE USER ID FROM DECODED
// 3) Check if user still exists not deleted
const currentUser = await User.findById(decoded.id);
if (!currentUser) {
return next(new AppError('the user does not exist.', 401));
}else{
// WHAT EVER YOU WANT TO DO AFTER CHECKING USER FOUND IN DATABASE
})