I am trying to use csrf in add employee function. However, whenever I hit submit I alway get ForbiddenError: invalid csrf token. I am not sure the way I did csrf correctly.
On the other hand, I have a login and register form. It works fine. And I did the same steps for add employee.
I've been reading some other posts but I didn't understand them. Can anyone point me to the correct way? I am really appreciate.
const express = require('express');
const router = express.Router();
const {uploadProduct, uploadForEmployee, uploadForVendor} = require('../upload');
const validation = require('validator');
const bcrypt = require('bcryptjs');
const csrf = require('csurf');
const csrfProtection = csrf();
//Load User model
const User = require('../../models/User');
router.get('/viewEmployee', (req, res) => {
User.find({$or: [{role: 'Admin'}, {role: 'Employee'}] }, (err, doc) => {
res.status(200).render('./admin/viewEmployee', {
user: req.user,
doc: doc,
link: "/admin/employees/"
})
})
});
router.delete('/:email', (req, res) => {
User.deleteOne({email: req.params.email}, (err, user) => {
if(err) {
console.log('Error while deleting product');
} else {
console.log(req.params.email + ' deleted');
res.redirect('/admin/employees/viewEmployee');
}
});
});
router.use(csrfProtection);
router.get('/addEmployee', (req, res) => {
res.render("./admin/employeeForm", {csrfToken: req.csrfToken()});
});
router.post('/addEmployee', uploadForEmployee.single('image'), (req, res) => {
let {name, email, password, phone, address, role} = req.body;
let image = req.file;
let errors = [];
if(!name || !email || ! password || !phone || !address || !role) {
errors.push({msg: 'Please enter all fields.'});
}
if(!image) {
errors.push({msg: 'Please upload a photo of yourself.'});
}
//sanitize input
name = validation.escape(name);
email = validation.escape(email);
password = validation.escape(password);
phone = validation.escape(phone);
address = validation.escape(address);
//Validate email
if(!validation.isEmail(email)){
errors.push({msg: 'Bad email.'})
} else {
email = validation.normalizeEmail(email, [
'all_lowercase', 'gmail_remove_dots','gmail_remove_subaddress', 'gmail_convert_googlemaildotcom',
'outlookdotcom_remove_subaddress', 'yahoo_remove_subaddress', 'icloud_remove_subaddress'
])
}
if (password.length < 6) {
errors.push({ msg: 'Password must be at least 6 characters.' });
}
if (errors.length > 0) {
res.render('./admin/employeeForm', {
errors,
name,
email,
password,
phone,
address,
role,
image,
csrfToken: req.csrfToken()
});
} else {
User.findOne({ email: email }).then(user => {
if (user) {
errors.push({ msg: 'Email already exists.' });
res.render('./admin/employeeForm', {
errors,
name,
email,
password,
phone,
address,
role,
image,
csrfToken: req.csrfToken()
});
} else {
const newUser = new User({
name: name,
email: email,
password: password,
phone: phone,
address: address,
role: role,
imageUrl: '/images/employees/' + image.filename
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser
.save()
.then(user => {
req.flash(
'success_msg',
'You are now registered and can log in.'
);
res.redirect('/admin/employees/viewEmployee');
})
.catch(err => console.log(err));
});
});
}
});
}
});
module.exports = router;
This is my login and register functions. These ones worked with csrf just fine.
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const passport = require('passport');
const {ensureLoggedIn, ensureLoggedOut} = require('connect-ensure-login');
const validation = require('validator');
const csrf = require('csurf');
const csrfProtection = csrf();
// Load User model
const User = require('../models/User');
//Profile
router.get('/profile', ensureLoggedIn('/users/login'), (req, res, next) => {
res.render('profile')
});
// Logout
router.get('/logout', (req, res) => {
req.logout();
req.flash('success_msg', 'You are logged out.');
res.redirect('/users/login');
});
// Register
router.post('/register', (req, res) => {
let { name, email, password, password2 } = req.body;
let errors = [];
if (!name || !email || !password || !password2) {
errors.push({ msg: 'Please enter all fields' });
}
//sanitize input
name = validation.escape(name);
email = validation.escape(email);
password = validation.escape(password);
password2 = validation.escape(password2);
//Validate email
if(!validation.isEmail(email)){
errors.push({msg: 'Bad email.'})
} else {
email = validation.normalizeEmail(email, [
'all_lowercase', 'gmail_remove_dots','gmail_remove_subaddress', 'gmail_convert_googlemaildotcom',
'outlookdotcom_remove_subaddress', 'yahoo_remove_subaddress', 'icloud_remove_subaddress'
])
}
if (password != password2) {
errors.push({ msg: 'Invalid Password.' });
}
if (password.length < 6) {
errors.push({ msg: 'Password must be at least 6 characters.' });
}
if (errors.length > 0) {
res.render('./home/register', {
errors,
name,
email,
password,
password2,
csrfToken: req.csrfToken()
});
} else {
User.findOne({ email: email }).then(user => {
if (user) {
errors.push({ msg: 'Email already exists.' });
res.render('./home/register', {
errors,
name,
email,
password,
password2,
csrfToken: req.csrfToken()
});
} else {
const newUser = new User({
name,
email,
password
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser
.save()
.then(user => {
req.flash(
'success_msg',
'You are now registered and can log in.'
);
res.redirect('/users/login');
})
.catch(err => console.log(err));
});
});
}
});
}
});
// Login
router.post('/login', (req, res, next) => {
passport.authenticate('local', {
successReturnToOrRedirect: '/',
failureRedirect: '/users/login',
failureFlash: true
})(req, res, next);
});
router.use(csrfProtection);
// Login Page
router.get('/login', ensureLoggedOut('/'), (req, res, next) => {
res.render('./home/login', {
csrfToken: req.csrfToken()
})
});
// Register Page
router.get('/register', ensureLoggedOut('/'), (req, res, next) => {
res.render('./home/register', {
csrfToken: req.csrfToken()
})
});
module.exports = router;
Related
I am new to MERN. I was using this code below . I followed a youtube tutorial and it was working fine for me for 4 to 5 days but now suddenly it has stopped working. I didn't change anything. I am not able to login, logout or even fetch data. My postman is giving positive results using these api but it won't work on my code. I want to remind you guys again, it was working fine for 4 to 5 days.
const User = require("../model/user");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const signup = async (req, res, next) => {
const { fname, lname, email, password, role, phone } = req.body;
let existingUser;
try {
existingUser = await User.findOne({ email: email });
} catch (err) {
console.log(err);
}
if (existingUser) {
return res.status(400).json({ message: "user already exists" });
}
const hashedPassword = bcrypt.hashSync(password);
const user = new User({
fname,
lname,
email,
password: hashedPassword,
phone,
role,
});
try {
await user.save();
} catch (err) {
console.log(err);
}
return res.status(201).json({ message: user });
};
const login = async (req, res, next) => {
const { email, password } = req.body;
let existingUser;
try {
existingUser = await User.findOne({ email: email });
} catch (err) {
console.log(err);
}
if (!existingUser) {
return res
.status(400)
.json({ message: "user doesn't exist. Please signup" });
}
const isPasswordCorrect = bcrypt.compareSync(password, existingUser.password);
if (!isPasswordCorrect) {
return res.status(401).json({ message: "invalid email or password" });
}
const token = jwt.sign({ id: existingUser._id }, "change1122", {
expiresIn: "1h",
});
res.cookie(String(existingUser._id), token, {
path: "/",
expires: new Date(Date.now() + 1000 * 3600),
httpOnly: true,
sameSite: "lax",
});
return res
.status(200)
.json({ message: "user logged in sucessfully", user: existingUser, token });
};
const verifyToken = (req, res, next) => {
const cookies = req.headers.cookie;
const token = cookies.split("=")[1];
if (!token) {
res.status(404).json({ message: "no token found" });
}
jwt.verify(String(token), "change1122", (err, user) => {
if (err) {
return res.status(404).json({ message: "invalid token" });
}
req.id = user.id;
});
next();
};
const getUser = async (req, res, next) => {
const id = req.id;
let user;
try {
user = await User.findById(id, "-password");
} catch (err) {
console.log(err);
}
if (!user) {
res.status(404).json({ message: "user not found with the id" });
}
return res.status(200).json({ user });
};
const logout = async (req, res, next) => {
const cookies = req.headers.cookie;
console.log(cookies);
const token = cookies.split("=")[1];
if (!token) {
res.status(404).json({ message: "no token found" });
}
const user = req.id;
res.clearCookie(`${user}`);
req.cookies[`${user}`] = "";
return res.status(200).json({ message: "successfully logged out" });
};
exports.signup = signup;
exports.login = login;
exports.verifyToken = verifyToken;
exports.getUser = getUser;
exports.logout = logout;
Here is the error
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at new NodeError (node:internal/errors:372:5) at ServerResponse.setHeader (node:_http_outgoing:576:11)
at ServerResponse.header (E:\Reacct\pos\server\node_modules\express\lib\response.js:794:10)
at ServerResponse.send (E:\Reacct\pos\server\node_modules\express\lib\response.js:174:12)
at ServerResponse.json (E:\Reacct\pos\server\node_modules\express\lib\response.js:278:15)
at getUser (E:\Reacct\pos\server\controller\user-controller.js:86:25)
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
code: 'ERR_HTTP_HEADERS_SENT'
}
[nodemon] app crashed - waiting for file changes before starting...
I think i have an issue with cookies or token, I am new so i don't understand it properly.
I have vue js application running on localhost:3000 and express js restful api running on localhost:4000.
Whenever new user registers, the request is sent to /api/v1/signup endpoint and after successfull registration verification email is sent with url including
localhost:4000/api/v1/verify-email/:token
After user click link in the email how to inform vue js application that email validated? use socket or ...?
Register controller
async store(req, res) {
let errors = {}
let valid = false
await signupSchema.validate({
first_name: req.body.first_name?.trim(),
last_name: req.body.last_name?.trim(),
email: req.body.email?.trim(),
password: req.body.password?.trim(),
password_confirmation: req.body.password_confirmation?.trim(),
}, { abortEarly: false })
.then(() => valid = true)
.catch((err) => {
err.inner.map((inn) => errors[inn.path] = inn.errors)
})
if (!valid) return res.status(400).json({ errors: errors, message: 'Validation errors' })
const data = {
first_name: req.body.first_name.trim(),
last_name: req.body.last_name?.trim(),
email: req.body.email.trim(),
password: bcrypt.hashSync(req.body.password, 12)
}
let id = null
// create user
await knex('users').insert(data)
.then((res) => id = res[0])
.catch((err) => {
logger.log(err)
return res.status(500).json({ error: err.message })
})
// create verification token
const verificationToken = jwt.sign({ id: id }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: 3600 })
await (new VerificationEmail(verificationToken, { id })).queue(data.email)
const user = await knex('users')
.select([ 'id', 'email', 'first_name', 'last_name', 'verified_at', 'deleted_at', 'created_at', 'updated_at' ])
.where('id', id)
.first()
.then((res) => res)
// sign jwt tokens
const accessToken = jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, { expiresIn: 60 })
const refreshToken = jwt.sign(user, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '1h' })
return res.json({ id: id, message: 'Successfully registered', accessToken, refreshToken })
}
VerifyEmailController
class VerifyEmailController {
/**
*
* #param {*} req
* #param {*} res
*/
async store(req, res) {
const user = await knex('users').where('id', req.params.id).first().then(res)
if (!user) res.status(404).json({ message: 'User not found' })
// validate token
jwt.verify(req.params.token, process.env.ACCESS_TOKEN_SECRET, async (err, data) => {
if (err) return res.status(401).json({ message: 'Invalid token' })
if (data.id !== user.id) res.status(401).json({ message: 'Invalid token' })
await knex('users').where('id', req.params.id)
.update({ verified_at: new Date() })
return res.json({ message: 'Your account verified. Thanks' })
})
}
}
VerificationEmail
class VerificationEmail {
token;
user;
constructor(token, user) {
this.token = token
this.user = user
}
layout(content) {
const str = fs.readFileSync(path.join(__dirname, '../../views/vendor/mail/default.ejs'), 'utf-8')
return ejs.render(str, { content })
}
build() {
const url = `${process.env.APP_URL}/api/v1/verify-email/${this.user.id}/${this.token}`
const str = fs.readFileSync(path.join(__dirname, '../../views/email/verification.ejs'), 'utf-8')
const content = ejs.render(str, { url })
return this.layout(content)
}
async queue(to) {
const html = this.build()
return emailQueue.add({ to, subject: 'Verify email address', html })
}
}
Signup page
I have a working implementation with passport-local-mongoose but would like to use async/await to keep things consistent, avoid nested callbacks and be comfortable that all errors are caught and handled.
I have sorted out the register method, but am stuck on the others. For example I've found that req.login() requires a callback so I can't await that one.
Here's my controller:
const User = require('../models/User')
const { populateModel } = require('../helpers/modelHelper')
const responseHelper = require('../helpers/responseHelper')
const { body, validationResult } = require('express-validator')
const passport = require('passport')
const jwt = require('jsonwebtoken')
const JwtStrategy = require('passport-jwt').Strategy
const ExtractJwt = require('passport-jwt').ExtractJwt
const options = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.AUTH_SECRET
}
// register working as expected async
module.exports.register = async (req, res, next) => {
const user = new User(),
fillable = [
'title',
'firstName',
'lastName',
'practice',
'addressLine1',
'addressLine2',
'city',
'state',
'postcode',
'jobTitle',
'country',
'locale',
'userRole',
'termsAccepted',
'consentOptIn',
'username',
'password',
'legacyUserId',
'authType'
]
populateModel(user, fillable, req.body)
try {
const result = await User.register(user, req.body.password)
return responseHelper.handleSuccess(res, {
user: result
})
} catch(err) {
next(err)
}
}
// login - would like to conver to async
module.exports.login = [
body('username', 'Email is required').not().isEmpty().isEmail(),
body('password', 'Password is required').not().isEmpty(),
(req, res) => {
const errors = validationResult(req)
if ( ! errors.isEmpty()) return responseHelper.handleValidationError(res, errors)
passport.authenticate('local', {}, (err, user, info) => {
if (err) return responseHelper.handleOperationError(res, err)
if ( ! user) return responseHelper.handleAuthError(res, 'Username/password not matched.')
req.logIn(user, (err) => {
if (err) return responseHelper.handleAuthError(res, err)
const token = jwt.sign({ _id: user._id }, process.env.AUTH_SECRET, { expiresIn: 1800 })
return res.json({
message: "Authentication successful.",
user: user,
token: token
})
})
})(req, res)
}
]
// token strategy - would like to conver to async
passport.use(new JwtStrategy(options, (payload, callback) => {
User.findById(payload, (err, user) => {
if (err) return callback(err, false)
if (user) return callback(null, user)
return callback(null, false)
})
}))
// get user - would like to conver to async
module.exports.user = [
(req, res, next) => {
passport.authenticate('jwt', { session: false }, (err, user) => {
if (err) return responseHelper.handleOperationError(res, err)
if ( ! user) return responseHelper.handleAuthError(res, 'User not authenticated.')
return res.json({
message: "Authentication successful.",
user: user
})
})(req, res, next)
}
]
In my app file I'm using the defaults for passport-local-mongoose:
passport.use(User.createStrategy())
passport.serializeUser(User.serializeUser())
passport.deserializeUser(User.deserializeUser())
Any my router calls my controller:
router.post('/auth/register', authController.register)
router.post('/auth/login', authController.login)
router.get('/auth/user', authController.user)
Finally I have an error handler set as middleware and a helper module for responses:
app.use((err, req, res, next) => responseHelper.handleUnexpectedError(res, err))
module.exports.handleSuccess = (res, data) => {
return res.json({
message: 'Success.',
data: data
})
}
module.exports.handleUnexpectedError = (res, err) => {
const validationErrors = [
'ValidationError',
'UserExistsError',
'MissingUsernameError',
'MissingPasswordError'
]
let code = err.status || 500
if (code === 500 && err.name && validationErrors.includes(err.name)) code = 422
return res.status(code).json({
message: err.message || 'Internal Server Error.',
error: err
})
}
in this piece of code whenever someone enters a username that already exists i get this error:
'Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client'
anybody know how to fix this? thanks.
const express = require('express')
const router = express.Router()
const bcrypt = require('bcryptjs')
const users= require('./users')
router.post('/reg', async (req, res, next) => {
let { firstname, lastname, username, email, password, confirm_password } = req.body
if (password !== confirm_password) {
res.status(400).json({
msg: 'passwords dont match'
})
return
}
await (users.findOne({username: username})).then(user => {
if(user) {
res.status(400).json({
msg: `username already exists`
})
return
}
})
await users.findOne({email: email}).then(user => {
if(user) {
res.status(400).json({
msg: 'email alraedy exists'
})
return
}
})
let newUser = new users({
firstname,
lastname,
username,
password,
email
})
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err
newUser.password = hash
newUser.save().then(user => {
return res.status(201).json({
success: true,
msg: 'you successfully signed up'
})
})
})
})
})```
I am implementing a reset password feature on for the users of my website. However, it seems to throw this error when the user confirms new password at the reset.ejs page.
The following steps are being performed correctly:
Token sent on mail
When clicked on token, new password page opens
When user enters new password and confirm new password and hits the button to save the new password, the function in users.js user.setPassword does not work as the error is mentioned below. It is a built in function of passport-local-mongoose.
ERROR: user.savePassword is not a function
Including all the code files below:
users.js:
var express = require('express');
var router = express.Router();
var multer = require('multer');
var upload = multer({dest: './uploads'});
var passport = require('passport');
var mongoose = require('mongoose');
var LocalStrategy = require('passport-local').Strategy;
var randomstring = require("randomstring");
var User = require('../models/user');
var data=require('../models/data');
var checklist=require("../models/checklist");
var async = require("async");
var crypto = require("crypto");
var passportLocalMongoose=require("passport-local-mongoose");
// "use strict";
const nodemailer = require("nodemailer");
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'dataseedltd#gmail.com',
pass: 'mypassword'
}
});
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
router.get('/register/:type', function(req, res, next) {
res.render('user/register.ejs',{title:'Register',type:req.params.type});
});
router.get('/login', function(req, res, next) {
res.render('user/login.ejs', {title:'Login'});
});
router.post('/login',
passport.authenticate('local',{failureRedirect:'/users/login', failureFlash: 'Invalid username or password'}),
function(req, res) {
var previouspath=req.cookies.previouspath;
if(req.cookies.checklist==undefined){
req.flash('success', 'You are now logged in');
if(req.user.as=='seller'){
if(previouspath!=undefined){
if(previouspath.length!=0)
res.redirect(previouspath);
else
res.redirect('/buyer');
}
else
res.redirect('/buyer');
}
else {
if(previouspath!=undefined){
if(previouspath.length!=0)
res.redirect(previouspath);
else
res.redirect('/buyer');
}
else
res.redirect('/buyer');
}
}
else{
var ids=req.cookies.checklist['data']
var checks=[];
for(var i=0;i<ids.length;i++){
checks.push({user:req.user,data:ids[i]});
}
checklist.insertMany(checks,function(err, d) {
if(err)console.log(err);
res.clearCookie('checklist');
req.flash('success', 'You are now logged in');
if(req.user.as=='seller'){
if(previouspath.length!=0)
res.redirect(previouspath);
else
res.redirect('/seller')
}
else {
if(previouspath.length!=0)
res.redirect(previouspath);
else
res.redirect('/buyer')
}
});
}
});
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.getUserById(id, function(err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy(function(username, password, done){
User.getUserByUsername(username, function(err, user){
if(err) throw err;
if(!user){
return done(null, false, {message: 'Unknown User'});
}
User.comparePassword(password, user.password, function(err, isMatch){
if(err) return done(err);
if(isMatch){
return done(null, user);
} else {
return done(null, false, {message:'Invalid Password'});
}
});
});
}));
router.post('/register/:type',function(req, res, next) {
var name = req.body.name;
var email = req.body.email;
var username = req.body.username;
var institution=req.body.institution;
var education=req.body.education;
var proffession=req.body.proffession;
var country=req.body.country;
//check if email does not already exists
//check if username does not already exists
User.find({ $or: [ { email: { $eq: req.body.email } }, { username: { $eq: req.body.username } } ] },function(err,data){
if(err)console.log(err);
if(data.length!=0){
console.log(data.length);
req.flash('success','email or username is repeated.');
res.redirect('/users/register/'+req.params.type);
}
else
{
var password = req.body.password;
var password2 = req.body.password2;
var as =req.params.type;
// Form Validator
req.checkBody('name','Name field is required').notEmpty();
req.checkBody('email','Email field is required').notEmpty();
req.checkBody('email','Email is not valid').isEmail();
req.checkBody('username','Username field is required').notEmpty();
req.checkBody('password','Password field is required').notEmpty();
// Check Errors
var errors = req.validationErrors();
if(errors){
res.render('user/register.ejs', {
errors: errors
});
} else{
var newUser = new User({
name: name,
email: email,
username: username,
password: password,
as :as,
institution:institution,
education:education,
proffession:proffession,
country:country
});
User.createUser(newUser, function(err, user){
if(err) throw err;
console.log(user);
});
// res.location('/');
var token=randomstring.generate(7);
var to=req.body.email;
var subject='Email Confirmation from dataSeed ';
var message='<h1>Use this code to register </h1><p>Your verification Code is: <h2>'+token+'</h2></p>';
var mailOptions=sendmail(to,subject,message);
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
}
else
{
res.render('user/emailconfirmation.ejs',{username:req.body.username,token:token});
}
});
}
}
});
function sendmail(to,subject,message){
var mailOptions = {
from: 'dataseedltd#gmail.com',
to: to,
subject: subject,
html: message
};
return mailOptions;
}
router.post('/emailconfirmation/:token',function(req, res) {
if(req.params.token==req.body.token){
req.flash('success', 'You are now registered and can login');
res.redirect('/');
}
else{
User.remove({username:req.body.username},function(err,data){
if(err)console.log(err);
res.redirect('/');
});
}
});
});
router.get('/logout', function(req, res){
req.logout();
req.flash('success', 'You are now logged out');
res.redirect('/users/login');
});
//Forgot Password
router.get('/forgot',function(req,res){
res.render("forgotpassword.ejs");
});
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('success', '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: 'dataseedltd#gmail.com',
pass: 'mypassword'
}
});
var mailOptions = {
to: user.email,
from: 'dataseedltd#gmail.com',
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 + '/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', '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('success', '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', '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;
user.save(function(err) {
req.logIn(user, function(err) {
done(err, user);
});
});
})
} else {
req.flash("error", "Passwords do not match.");
return res.redirect('back');
}
});
},
function(user, done) {
var smtpTransport = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'dataseedltd#gmail.com',
pass: 'mypassword'
}
});
var mailOptions = {
to: user.email,
from: 'dataseedltd#gmail.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', 'Success! Your password has been changed.');
done(err);
});
}
], function(err) {
res.redirect('/login');
});
});
module.exports = router;
users.js (model):
var mongoose = require('mongoose');
var bcrypt = require('bcryptjs');
var passportLocalMongoose=require("passport-local-mongoose");
mongoose.connect('mongodb://localhost/dataseed');
var db = mongoose.connection;
// User Schema
var UserSchema = mongoose.Schema({
username: {
type: String,
index: true
},
password: {
type: String
},
email: {
type: String
},
name: {
type: String
},
profileimage:{
type: String
},
as:{
type:String
},
institution:{
type:String
},
education:{
type:String
},
proffession:{
type:String
},
country:{
type:String
},
resetPasswordToken: String,
resetPasswordExpires: Date
});
var User = module.exports = mongoose.model('User', UserSchema);
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
}
module.exports.getUserByUsername = function(username, callback){
var query = {username: username};
User.findOne(query, callback);
}
module.exports.comparePassword = function(candidatePassword, hash, callback){
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
callback(null, isMatch);
});
}
module.exports.createUser = function(newUser, callback){
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(newUser.password, salt, function(err, hash) {
newUser.password = hash;
newUser.save(callback);
});
});
}
It seems like you don't have a method called setPassword defined under user model. This is mainly because you haven't plugin the passport-local-mongoose to UserSchema. Add UserSchema.plugin(passportLocalMongoose); before creating the model from UserSchema. like this
var mongoose = require('mongoose');
var bcrypt = require('bcryptjs');
var passportLocalMongoose=require("passport-local-mongoose");
mongoose.connect('mongodb://localhost/dataseed');
var db = mongoose.connection;
// User Schema
var UserSchema = mongoose.Schema({
username: {
type: String,
index: true
},
password: {
type: String
},
email: {
type: String
},
name: {
type: String
},
profileimage:{
type: String
},
as:{
type:String
},
institution:{
type:String
},
education:{
type:String
},
proffession:{
type:String
},
country:{
type:String
},
resetPasswordToken: String,
resetPasswordExpires: Date
});
UserSchema.plugin(passportLocalMongoose);
var User = module.exports = mongoose.model('User', UserSchema);