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 a simple login form that I created. It seems to authenticate any existing user as long as the password field has something in it. Obviously, it is a huge security flaw. I'm new to mean stack and using passport to authenticate users seemed easy but not sure if I did it wrong.
This is my backend code using passportjs:
app.js
const passport = require('passport');
require('./config/passport');
app.use(passport.initialize());
routes/index.js
const ctrlAuth = require('../controllers/authentication');
router.post('/login', ctrlAuth.login);
controllers/authentication.js
module.exports.login = function(req, res) {
passport.authenticate('local', function(err, user, info){
let token;
// If Passport throws/catches an error
if (err) {
res.status(404).json(err);
return;
}
// If a user is found
if(user){
token = user.generateJwt();
res.status(200);
res.json({
"token" : token
});
} else {
// If user is not found
res.status(401).json(info);
}
})(req, res);
};
And finally, my config file
config/passport.js
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const User = mongoose.model('User');
passport.use(new LocalStrategy({
usernameField: 'email'
},
function(username, password, done) {
User.findOne({
email: username
}, function(err, user) {
if (err) {
return done(err);
}
//Return if user not found in database
if (!user) {
return done(null, false, {
message: 'User not found'
});
}
//Return if password is wrong
if (!user.validPassword(password)) {
return done(null, false, {
message: 'Password is wrong'
});
}
//If credentials are correct, return the user object
return done(null, user);
});
}
));
I believe I've narrowed the bug down to my validPassword function where I might be using bcrypt incorrectly.
userSchema.methods.validPassword = function(password){
return bcrypt.compare(password, this.hash);
};
I narrowed my issue down to my validPassword method and found that I was using bcrypt incorrectly. Changed it to
userSchema.methods.validPassword = function(password){
return bcrypt.compareSync(password, this.hash);
};
Makes more sense after looking at the docs for bcrypt https://github.com/kelektiv/node.bcrypt.js#readme
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);
I got stuck on hashed password validation with bcrypt-nodejs, nodeJS (expressJS) and mongoose. User can register and code generates hashed password but when I try to validate that password with comparePassword function in login page it does not work and gives me error user.comparePassword is not a function
Here is the code:
Database:
UserSchema.pre('save', async function(next){
var user = this;
if(!user.isModified('password')) return next();
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt){
if(err) return next(err)
bcrypt.hash(user.password, salt,null, function(err,hash){
if(err) return next(err)
user.password = hash
next()
})
})
})
UserSchema.methods.comparePassword = async function(candidatePassword, cb){
bcrypt.compare(candidatePassword, this.password, function(err, isMatch){
if(err) return cb(err);
cb(null, isMatch)
})
}
Route:
router.post('/', async (req, res) => {
try {
const {username, password} = req.body;
const user = await User.findOne({username}).lean();
if (!user) {
return res.status(404).send({
message: 'user is not registered'
});
}
if(username.trim().length < 1 && password.trim().length < 1){
return res.status(409).send({message: 'username & password is required'})
}
// if (user.password !== password) {
// return res.status(403).send({
// message: 'user password invalid'
//});
//}
user.comparePassword(password, function(err, isMatch){
if(err){
return res.status(500).send({message: err.message})
}
if(!isMatch){
return res.status(403).send({
message: 'user password invali'
});
}
req.session.user = user;
const redirectTo = '/dashboard';
if (
req.is('application/json') // request content type is json
|| // or
req.xhr // is ajax
) {
// respond with json response
return res.status(200).send({redirectTo});
}
// not ajax request
// then respond redirect header
res.redirect(redirectTo);
})
const mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
SALT_WORK_FACTOR = 10;
const userDataModal = mongoose.Schema({
username: {
type: String,
required : true,
unique:true
},
password: {
type: String,
required : true
}
});
userDataModal.pre('save', function(next) {
var user = this;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
// hash the password using our new salt
bcrypt.hash(user.password, salt, null, function(err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
next();
});
});
});
userDataModal.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
// Users.index({ emaiId: "emaiId", fname : "fname", lname: "lname" });
const userDatamodal = module.exports = mongoose.model("usertemplates" , userDataModal)
//inserting document
userDataModel.findOne({ username: reqData.username }).then(doc => {
console.log(doc)
if (doc == null) {
let userDataMode = new userDataModel(reqData);
// userDataMode.password = userDataMode.generateHash(reqData.password);
userDataMode.save({new:true}).then(data=>{
let obj={
success:true,
message: "New user registered successfully",
data:data
}
resolve(obj)
}).catch(err=>{
reject(err)
})
}
else {
resolve({
success: true,
docExists: true,
message: "already user registered",
data: doc
}
)
}
}).catch(err => {
console.log(err)
reject(err)
})
//retriving and checking
// test a matching password
user.comparePassword(requestData.password, function(err, isMatch) {
if (err){
reject({
'status': 'Error',
'data': err
});
throw err;
} else {
if(isMatch){
resolve({
'status': true,
'data': user,
'loginStatus' : "successfully Login"
});
console.log('Password123:', isMatch); // -> Password123: true
}
Sequelize + Bcrypt not storing passwords in DB as hash
As the title says, whenever I attempt to store a user into my SQLite DB the console outputs the password as a hash but when I look into the DB with DBbrowser I can see the plaintext password.
Model
// const Promise = require('bluebird')
const bcrypt = require('bcrypt')
async function hashPassword (user, options) {
if (!user.changed('password')) {
return 0
}
const SALT_FACTOR = 8
await bcrypt.hash(user.password, SALT_FACTOR, (err, hash) => {
if (err) {
console.log(err)
}
// user.setDataValue('password', hash)
user.password = hash
console.log(user)
})
}
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
email: {
type: DataTypes.STRING,
unique: true
},
password: DataTypes.STRING
}, {
hooks: {
beforeSave: hashPassword,
beforeCreate: hashPassword
}
})
User.prototype.comparePassword = function (password) {
bcrypt.compare(password, this.password, function (res, err) {
if (res) {
console.log(res)
} else {
console.log(err)
}
})
return bcrypt.compare(password, this.password)
}
return User
}
Controllers
module.exports = {
async register (req, res) {
try {
const user = await User.create(req.body)
const userJson = user.toJSON()
res.send({
user: userJson,
token: jwtSignUser(userJson)
})
} catch (err) {
// e-mail already exists or such
res.status(400).send({
error: 'This email address is already in use'
})
}
},
async login (req, res) {
try {
// Grab user input
const { email, password } = req.body
const user = await User.findOne({
where: {
email: email
}
})
// Check to see if user is in db
if (!user) {
res.status(403).send({
error: 'the login information was incorrect / Not Found'
})
}
// Check to see if password is valid
const isPasswordValid = await user.comparePassword(password)
if (!isPasswordValid) {
return res.status(403).send({
error: 'The login information was incorrect'
})
}
// return user using toJSON()
const userJson = user.toJSON()
res.send({
user: userJson,
token: jwtSignUser(userJson)
})
} catch (e) {
res.status(500).send({ error: 'An error occured attempting to login' })
console.log(e)
}
}
}
To elaborate a little more, whenever I create a user, I receive the following:
{
"user": {
"id": 1,
"email": 'test#test.com",
"password": "$2b$08$SYYXU/GDSCFsp3MVeuqrduI0lOLHeeub7whXiaMMoVxO53YJry.1i",
"updatedAt": "2018-09-07T22:44:12.944Z",
"createdAt": "2018-09-07T22:44:12.944Z"
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJTVVBCUkhVQGxvbC5jb20iLCJwYXNzd29yZCI6IiQyYiQwOCRTWVlYVS9HRFNDRnNwM01WZXVxcmR1STBsT0xIZWV1Yjd3aFhpYU1Nb1Z4TzUzWUpyeS4xaSIsInVwZGF0ZWRBdCI6IjIwMTgtMDktMDdUMjI6NDQ6MTIuOTQ0WiIsImNyZWF0ZWRBdCI6IjIwMTgtMDktMDdUMjI6NDQ6MTIuOTQ0WiIsImlhdCI6MTUzNjM2MDI1MywiZXhwIjoxNTM2OTY1MDUzfQ.mDaeIikzUcV_AGTuklnLucx9mVyeScGpMym1y0kJnsg"
}
Which to me says the DB successfully hashed my password, and stored it. The overhanging issue for me with this is the fact that I believe it's causing the bcrypt.compare function to spit out 'false'. As always, any insight or help would be greatly appreciated!
I'm pretty sure that this answer is too late for you, but might help others landing on this same question.
The main issue I can see is how you are using the async/await pattern. Changing this:
async function hashPassword (user, options) {
if (!user.changed('password')) {
return 0
}
const SALT_FACTOR = 8
await bcrypt.hash(user.password, SALT_FACTOR, (err, hash) => {
if (err) {
console.log(err)
}
// user.setDataValue('password', hash)
user.password = hash
console.log(user)
})
}
to this, worked for me:
async function hashPassword(user, options) {
if (!user.changed("password")) {
return 0;
}
user.password = await bcrypt.hash(user.password, SALT_FACTOR);
}
Can you please try to add only one hook
hooks: {
beforeSave: hashPassword,
}
Because I think your password is getting hashed two times. as beforeSave and beforeCreate both hooks get executed.
Hope it helps