Creating account email verification not working - node.js

I am trying to have it so when a user signs up to my site that they get an email saying that they need to verify their email to use the site. It currently sends the email with the token just fine but when I go to the confirmation link it fails with a status 404.
Route
app.post('/confirmation/:token',
users.confirmationPost);
Require controller
var users = require('../controllers/users-controller');
confirmationPost
exports.confirmationPost = function (req, res, next) {
req.assert('email', 'Email is not valid').isEmail();
req.assert('email', 'Email cannot be blank').notEmpty();
req.assert('token', 'Token cannot be blank').notEmpty();
req.sanitize('email').normalizeEmail({ remove_dots: false });
// Check for validation errors
var errors = req.validationErrors();
if (errors) return res.status(400).send(errors);
// Find a matching token
Token.findOne({ token: req.body.token }, function (err, token) {
if(err) throw err;
if (!token) return res.status(400).send({ type: 'not-verified', msg: 'We were unable to find a valid token. Your token my have expired.' });
// If we found a token, find a matching user
User.findOne({ _id: token._userId }, function (err, user) {
if(err) throw err;
if (!user) return res.status(400).send({ msg: 'We were unable to find a user for this token.' });
if (user.isVerified) return res.status(400).send({ type: 'already-verified', msg: 'This user has already been verified.' });
// Verify and save the user
user.isVerified = true;
user.save(function (err) {
if (err) { return res.status(500).send({ msg: err.message }); }
res.status(200).send("The account has been verified. Please log in.");
});
});
});
};
Passport signup
passport.use('signup', new LocalStrategy({
usernameField: 'email',
passReqToCallback : true
},
function(req, email, password, done) {
var findOrCreateUser = function(){
User.findOne({ email: req.body.email }, function(err, existingUser) {
if(err){
console.log(err);
}
if (existingUser) {
req.flash('form', {
email: req.body.email
});
return done(null, false, req.flash('error', 'An account with that email address already exists.'));
}
// edit this portion to accept other properties when creating a user.
var user = new User({
email: req.body.email,
password: req.body.password // user schema pre save task hashes this password
});
user.save(function(err) {
if (err) return done(err, false, req.flash('error', 'Error saving user.'));
var token = new Token({ _userId: user._id, token: crypto.randomBytes(16).toString('hex') });
token.save(function (err) {
if (err) return done(null, false, req.flash('error', err.message));
// Send the email
var message = 'Hello,\n\n' + 'Please verify your account by clicking the link: \nhttp:\/\/' + req.headers.host + '\/confirmation\/' + token.token + '.\n';
sendEmail('"Phantom Asset Management" noreply#phantomam.com', user.email, 'Account Verification Token', message);
});
var time = 14 * 24 * 3600000;
req.session.cookie.maxAge = time; //2 weeks
req.session.cookie.expires = new Date(Date.now() + time);
req.session.touch();
return done(null, user, req.flash('success', 'A verification email has been sent to ' + user.email + '.'));
});
});
};
process.nextTick(findOrCreateUser);
})
);
When I go to /confirmation/:token I get my error 404 page and nothing in my console. Only thing I see there is my debugger showing an error 404 for that route.

Related

why findOne () not return any value in mogooDB

why findOne does not return any value. I am trying to find the user with his email address but this function returning null. and I am exit from the function. help me solve this function, actually, this is a password reset post route. I am getting a username but when I reach to user.findOne () its return me null value . and also token undefined, i don't know how to fix, give me some solution
app.post('/check-auth', (req, res, next)=> {
console.log(req.body)
async.waterfall([
function(done) {
crypto.randomBytes(20, function(err, buf) {
var token = buf.toString('hex');
done(err, token);
});
},
async function(token, done) {
const user = await User.findOne({ email: req.body.username });
console.log(user);
if (!user) {
req.flash('error', 'No account with that email address exists.');
return res.redirect('/forget-password');
}
// Do your stuff with the user
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
user.save(function(err) {
done(err, token, user);
});
},
function(token, user, done) {
var smtpTransport = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'xyz#gmail.com',
pass: 'jvjvsdjfvjdvjdvdv'
}
});
console.log(user.email)
var mailOptions = {
from: 'passwordreset#demo.com',
to: req.body.username,
subject: 'Node.js Password Reset',
text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
'http://' + req.headers.host + '/reset/' + token + '\n\n' +
'If you did not request this, please ignore this email and your password will remain unchanged.\n'
};
smtpTransport.sendMail(mailOptions, function(err) {
req.flash('info', 'An e-mail has been sent to ' + user.email + ' with further instructions.');
done(err, 'done');
});
}
], function(err) {
if (err) return next(err);
res.redirect('/forgot');
});
});
You are mixing async, await with callbacks.
Try that following way.
async function(token, done) {
const user = await User.findOne({ email: req.body.username });
console.log(user);
if (!user) {
req.flash('error', 'No account with that email address exists.');
return res.redirect('/forget-password');
}
// Do your stuff with the user
}
If you want to handle errors then wrap the whole code block inside try and catch.
I hope this will solve your issue.

Getting an error that token is expired when trying to reset password following click on email link. Using Node js, express and mongoose

I have created a user registration and login. All working fine. Trying to implement password reset. All is working fine up to the point of filling in the new password and confirming same. Then getting an error that token has expired. No idea why. I'm pretty new to coding (a few month) so sincere apologies if I am not posting this properly. Have used the site a lot but it's my first time posting... Any help would be very much appreciated. Thank you.
p.s
I'm including the main js file. I'm not sure if I need to include any of the other files but as they are quite a number, I'll wait to see if anyone asks to see anything else. Thanks again.
//users.js
const express = require('express');
const router = express.Router();
const passport = require('passport');
const bcrypt = require('bcryptjs');
const async = require('async');
const nodemailer = require('nodemailer');
const crypto = require('crypto');
// User model
const User = require('../models/User');
// Login Page
router.get('/login', function(req, res){
res.render('login');
});
// Register Page
router.get('/register', function(req, res){
res.render('register');
});
// Register Handle
router.post('/register', function (req, res){
const { name, email, password, password2 } = req.body;
let errors = [];
// Check required fields
if(!name || !email || !password || !password2) {
errors.push({ msg: 'Please fill in all fields' });
}
// Check passwords match
if(password !== password2) {
errors.push({ msg: 'Passwords do not match' });
};
// Check password length
if(password.length < 6) {
errors.push({ msg: 'Password should be at least 6 characters' });
};
// If there are errors
if(errors.length > 0){
res.render('register', {
errors,
name,
email,
password,
password2
});
} else {
//Validation passed
User.findOne({ email: email })//This is going to return a promise
.then(function (user){
//If User exists
if(user){
errors.push({ msg: 'Email already registered' });
res.render('register', {
errors,
name,
email,
password,
password2
});
} else {
//If email not registered create a new User
const newUser = new User({
name,// es6 instead of 'name: name' and same for below
email,
password
});
// Hash Password
bcrypt.genSalt(10, function(err, salt){
bcrypt.hash(newUser.password, salt, function(err, hash){
if(err) throw err;
//Set password to hashed
newUser.password = hash;
// Save user
newUser.save()
.then(function(user){
req.flash('success_msg', 'You have been successuflly registered and can now log in');
res.redirect('/users/login');
})
.catch(err, function(){
console.log(err);
});
});
});
}
});
}
});
// Login Handle
router.post('/login', function(req, res, next){
passport.authenticate('local', {
successRedirect: '/dashboard',
failureRedirect: '/users/login',
failureFlash: true
})(req, res, next);
});
// Logout Handle
router.get('/logout', function(req, res){
req.logout();
req.flash('success_msg', 'You have been logged out');
res.redirect('/users/login');
});
// Forgot Password Page
router.get('/forgot', function (req, res){
res.render('forgot');
});
// Forgot Password Handle
router.post('/forgot', function(req, res, next) {
async.waterfall([
function(done) {
crypto.randomBytes(20, function(err, buf) {
var token = buf.toString('hex');
done(err, token);
});
},
function(token, done) {
User.findOne({ email: req.body.email }, function(err, user) {
if (!user) {
req.flash('error', 'No account with that email address exists.');
return res.redirect('/users/forgot');
}
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
user.save(function(err) {
done(err, token, user);
});
});
},
function(token, user, done) {
var smtpTransport = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'nirshihor#gmail.com',
pass: '<password>'
}
});
var mailOptions = {
to: user.email,
from: 'nirshihor#gmail.com',
subject: 'WSS Password Reset',
text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
'http://' + req.headers.host + '/users/reset/' + token + '\n\n' +
'If you did not request this, please ignore this email and your password will remain unchanged.\n'
};
smtpTransport.sendMail(mailOptions, function(err) {
console.log('mail sent');
req.flash('success_mail_sent', 'An e-mail has been sent to ' + user.email + ' with further instructions.');
done(err, 'done');
});
}
], function(err) {
if (err) return next(err);
res.redirect('/users/forgot');
});
});
router.get('/reset/:token', function(req, res) {
User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
if (!user) {
req.flash('error_token', 'Password reset token is invalid or has expired.');
return res.redirect('/users/forgot');
}
res.render('reset', {token: req.params.token});
});
});
router.post('/reset/:token', function(req, res) {
async.waterfall([
function(done) {
User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
if (!user) {
req.flash('error_token', 'Password reset token is invalid or has expired.');
return res.redirect('back');
}
if(req.body.password === req.body.confirm) {
user.setPassword(req.body.password, function(err) {
user.resetPasswordToken = undefined;
user.resetPasswordExpires = undefined;
//Update user in database
user.save(function(err) {
req.logIn(user, function(err) {
done(err, user);
});
});
})
} else {
req.flash("error_passwords_match", "Passwords do not match.");
return res.redirect('back');
}
});
},
function(user, done) {
var smtpTransport = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'nirshihor#gmail.com',
pass: 'N1rSh1hor?#'
}
});
var mailOptions = {
to: user.email,
from: 'nirshihor#mail.com',
subject: 'Your password has been changed',
text: 'Hello,\n\n' +
'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
};
smtpTransport.sendMail(mailOptions, function(err) {
req.flash('success_password_change', 'Success! Your password has been changed.');
done(err);
});
}
], function(err) {
res.redirect('/home');
});
});
module.exports = router;
Managed to sort it. I put together two different tutorials I followed. One was for registering users and logging in, and the other was for resetting password. I failed to implement the hashing process for the password reset as I did in the first tutorial, which caused the problem. Now resolved. If anyone experiencing the same issue requires code sample - happy to supply.

Creating use with passport while logged in with passport

I am trying to have it so a user can login and then create clients (another sub user) with passport. I have the main user accounts working but I can not get the client accounts to work.
I need the sessions to be set to false, I tried the same code before I put that in and it failed on the serialize user part of passport. It is saving the client user data to the database but it crashing the server.
Please ask if you have questions or I need to add something to thew post.
This is the error message I get when I go to sign them up:
ERROR MESSAGE
_http_outgoing.js:356
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:356:11)
at ServerResponse.header (/home/ubuntu/workspace/asset-management/node_modules/express/lib/response.js:767:10)
at ServerResponse.send (/home/ubuntu/workspace/asset-management/node_modules/express/lib/response.js:170:12)
at done (/home/ubuntu/workspace/asset-management/node_modules/express/lib/response.js:1004:10)
at /home/ubuntu/workspace/asset-management/node_modules/swig/lib/swig.js:565:9
at /home/ubuntu/workspace/asset-management/node_modules/swig/lib/swig.js:690:9
at tryToString (fs.js:456:3)
at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:443:12)
MY ROUTE
app.post('/dashboard/client/new',
setRedirect({auth: '/client/dashboard', success: '/dashboard/client/new', failure: '/dashboard/client/new'}),
isAuthenticated,
registrations.postClientSignup);
postClientSignup
exports.postClientSignup = function(req, res, next){
req.assert('email', 'Please sign up with a valid email.').isEmail();
req.assert('password', 'Password must be at least 6 characters long').len(6);
var errors = req.validationErrors();
if (errors) {
req.flash('errors', errors);
req.flash('form', {
email: req.body.email
});
return res.redirect('/signup');
}
// calls next middleware to authenticate with passport
passport.authenticate('clientSignup', {
session: false,
successRedirect: '/dashboard/client/new', // Select redirect for post signup
failureRedirect: '/dashboard/client/new',
failureFlash : true
})(req, res, next);
next();
};
PASSPORT CODE
passport.use('clientSignup', new LocalStrategy({
usernameField: 'email',
passReqToCallback : true
},
function(req, email, password, done) {
var findOrCreateUser = function(){
Client.findOne({ email: req.body.email }, function(err, existingUser) {
if(err){
console.log(err);
}
if (existingUser) {
req.flash('form', {
email: req.body.email
});
return done(null, false, req.flash('error', 'An account with that email address already exists.'));
}
// edit this portion to accept other properties when creating a user.
var user = new Client({
email: req.body.email,
password: req.body.password, // user schema pre save task hashes this password
bizID: req.body.bizID,
role: req.body.role
});
console.log(req.body.email);
console.log(req.body.password);
user.save(function(err) {
if (err) return done(err, false, req.flash('error', 'Error saving user.'));
var time = 14 * 24 * 3600000;
req.session.cookie.maxAge = time; //2 weeks
req.session.cookie.expires = new Date(Date.now() + time);
req.session.touch();
return done(null, user, req.flash('success', 'A verification email has been sent to ' + user.email + '.'));
});
});
};
process.nextTick(findOrCreateUser);
})
ALSO IN PASSPORT
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});

Passport not working after mail-gun verify email

I am using passport.js and mailgun-validate-email to try and validate an email then create a user account based off that email. I can get it to validate the email but it will not finish the POST request to actually create the user. It will eventually show a post log but it does't even give a status code which I found weird. I am using morgan to see this. Normally it does but this time it is just - where the status code is to be.
Passport.use signup middleware
passport.use('signup', new LocalStrategy({
usernameField: 'email',
passReqToCallback : true
},
function(req, email, password, done) {
var findOrCreateUser = function(){
console.log(req.body.email);
User.findOne({ email: req.body.email }, function(err, existingUser) {
if(err){
console.log(err);
}
if (existingUser) {
req.flash('form', {
email: req.body.email
});
return done(null, false, req.flash('error', 'An account with that email address already exists.'));
}
// edit this portion to accept other properties when creating a user.
var user = new User({
email: req.body.email,
password: req.body.password // user schema pre save task hashes this password
});
user.save(function(err) {
if (err) return done(err, false, req.flash('error', 'Error saving user.'));
var token = new Token({ _userId: user._id, token: crypto.randomBytes(16).toString('hex') });
token.save(function (err) {
if (err) return done(null, false, req.flash('error', err.message));
var email = req.body.email;
// Send the email
var message = 'Hello,\n\n' + 'Please verify your account by clicking the link: \nhttp:\/\/' + req.headers.host + '\/confirmation\/' + token.token + '\/' + email + '\n';
sendEmail('"Phantom Asset Management" noreply#phantomam.com', user.email, 'Account Verification Token', message);
});
var time = 14 * 24 * 3600000;
req.session.cookie.maxAge = time; //2 weeks
req.session.cookie.expires = new Date(Date.now() + time);
req.session.touch();
return done(null, user, req.flash('success', 'A verification email has been sent to ' + user.email + '.'));
});
console.log('done');
});
};
process.nextTick(findOrCreateUser);
})
);
Then my controller
exports.postSignup = function(req, res, next){
req.assert('email', 'Please sign up with a valid email.').isEmail();
req.assert('password', 'Password must be at least 6 characters long').len(6);
var errors = req.validationErrors();
if (errors) {
req.flash('errors', errors);
req.flash('form', {
email: req.body.email
});
return res.redirect('/signup');
}
validator(req.body.email, function (err, result){
if(err) {
return console.log('Error: ', err);
} else {
console.log('Result: ', result);
if(result.is_valid == false){
req.flash('error', 'Looks like your email is not valid did you mean ' + result.did_you_mean + '?');
return res.redirect(req.redirect.failure);
} else if(result.is_valid == true) {
// calls next middleware to authenticate with passport
passport.authenticate('signup', {
successRedirect: '/dashboard', // Select redirect for post signup
failureRedirect: '/signup',
failureFlash : true
});
}
}
});
(req, res, next);
next();
};
If my understanding of how I have it is correct it first checks to make sure it is an email then the password is the right length. If there is errors it lets the user know and redirect them back to /signup. Then it goes into the validator and either give an error to the console or goes into checking if it is valid or not. If it is not valid is shows the user an error but if valid goes into my passport middleware.
Which should take and check if a user exists then flag error is need be. It saves the user and a token for that user to use to verify their email address. This all works fine expect the saving of the user and token.
It looks like it might be a request.js issue, I am getting error: esockettimedout. The only dependencies for mailgun-validate-email is just request 2.25.0. The esockettimedout is on request.js:813:19

Type error: done is not a function (nodemailer)

I'm using nodemailer for a forgot password app, the email executes for the forgot password, but when the user resets their password they don't get a confirmation email. not sure why that's not happening.
seems to be throwing an error on the done(err) line. below is the get handler for the reset token
//get handler for reset token
app.post('/reset/:token', function(req, res, next) {
async.waterfall([
function(done) {
User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
if (!user) {
req.flash('error', 'Password reset token is invalid or has expired.');
return res.redirect('back');
}
user.password = req.body.password;
user.resetPasswordToken = undefined;
user.resetPasswordExpires = undefined;
user.save(function(err) {
req.logIn(user, function(err) {
done(err, user);
});
});
});
},
function (token, user, done) {
var options = {
service: 'Mailgun',
auth: {
user: 'postmaster#Sandboxxxxxxxxxxx.mailgun.org',
pass: 'xxxxxxxxxxxxxx'
}
};
var transporter = nodemailer.createTransport(smtpTransport(options))
var mailOptions = {
to: user.email,
from: 'postmaster#Sandbox65b418bcf76c4a5e909aedb7b6e87b45.mailgun.org',
subject: 'Your password has been changed',
text: 'Hello,\n\n' +
'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
};
transporter.sendMail(mailOptions, function(err) {
req.flash('success', 'Success! Your password has been changed.');
done(err);
});
}
], function(err) {
res.redirect('/');
});
});
You're incorrectly calling done function in the first waterwall task. The second task is expected two parameters: token, user and additional callback. But you pass only one parameter: user.
Add token parameter in done function:
...
user.save(function(err) {
req.logIn(user, function(err) {
done(err, token, user); // TODO: initialize token
});
});
....

Resources