Passport Authentication Fails with associated model - node.js

I made an addition to my database modeling where I wanted to attach my users to a specific company. I followed the Sequelize documentation to create a one to one relationship between user and company, but it appears that it has screwed up my authentication a bit as the introduction of the code has been sending every submission of a login to the failureRedirect portion of my authentication code. Has anyone run into a similar issue?
User Model:
var bcrypt = require('bcrypt-nodejs');
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('user', {
user_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
firstName: {
type: DataTypes.STRING,
field: 'first_name'
},
lastName: {
type: DataTypes.STRING,
field: 'last_name'
},
email: {
type: DataTypes.STRING,
isEmail: true,
unique: true
},
password: DataTypes.STRING,
organizationId: {
type: DataTypes.STRING,
unique: true
}
}, {
freezeTableName: true,
classMethods: {
associate: function(db) {
User.belongsTo(db.Organization);
}
}
});
return User;
}
Organization Model:
module.exports = function(sequelize, DataTypes) {
var Organization = sequelize.define('organization', {
organizationId: {
type: DataTypes.INTEGER,
field: 'organization_id',
autoIncrement: true,
primaryKey: true
},
organizationName: {
type: DataTypes.STRING,
field: 'organization_name'
},
admin: DataTypes.STRING,
members: DataTypes.STRING
},{
freezeTableName: true
});
return Organization;
}
Database Index:
var Sequelize = require('sequelize');
var path = require('path');
var config = require(path.resolve(__dirname, '..', '..','./config/config.js'));
var sequelize = new Sequelize(config.database, config.username, config.password, {
host:'localhost',
port:'3306',
dialect: 'mysql'
});
sequelize.authenticate().then(function(err) {
if (!!err) {
console.log('Unable to connect to the database:', err)
} else {
console.log('Connection has been established successfully.')
}
});
var db = {}
db.Organization = sequelize.import(__dirname + "/organization");
db.User = sequelize.import(__dirname + "/user");
db.FormApp = sequelize.import(__dirname + "/formapp");
db.User.associate(db);
db.FormApp.associate(db);
db.sequelize = sequelize;
db.Sequelize = Sequelize;
sequelize.sync();
module.exports = db;
Passport Authentication Routing:
var express = require('express');
var siteRoutes = express.Router();
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var models = require('../models/db-index');
/*==== Passport Configuration ====*/
// Serialize sessions
passport.serializeUser(function(user, done) {
console.log(user.user_id + "Serializing");
done(null, user.user_id);
});
passport.deserializeUser(function(user_id, done) {
models.User.find({where: {user_id: user_id}}).then(function(user){
done(null, user);
}).error(function(err){
done(err, null);
});
});
passport.use('local', new LocalStrategy({
passReqToCallback : true,
usernameField: 'email'
},
function(req, email, password, done) {
//Find user by email
models.User.findOne({
where: {
email: req.body.email,
password: req.body.password
}
}).then(function(user) {
return done(null, user);
}).catch(function(err) {
return done(null, false, req.flash('message', 'Email not found.'));
});
}
));
/*==== Index ====*/
siteRoutes.get('/', function(req, res){
res.render('pages/index.hbs');
});
siteRoutes.get('/flash', function(req, res){
req.flash('test', 'it worked');
res.redirect('/')
});
/*==== Login ====*/
siteRoutes.route('/login')
.get(function(req, res){
res.render('pages/login.hbs');
})
.post(passport.authenticate('local', {
successRedirect: '/app',
failureRedirect: '/login',
failureFlash: 'Invalid username or password.'
}));
siteRoutes.route('/sign-up')
.get(function(req, res){
res.render('pages/sign-up.hbs');
})
.post(function(req, res){
models.User.create({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: req.body.password
}).then(function() {
res.redirect('/app');
//res.redirect('/sign-up/create-organization');
}).catch(function(error){
res.send(error);
})
});
module.exports = siteRoutes;

Related

Signup with passport & saving with Sequelize doesn't work (Unexpected ' ')

I have some problem with passport and sequelize on my Node.js project.
In fact, when I want to signup, I'm using postman to call the /users/signup route and it show me : Unexpected '' and :
::1 - - [07/Aug/2018:09:20:19 +0000] "POST /users/signup HTTP/1.1" - - "-" "PostmanRuntime/7.2.0"
Executing (default): SELECT "user_id", "username", "name", "firstname", "email", "type", "location", "password", "createdAt", "updatedAt" FROM "users" AS "users" LIMIT 1;
There is the code :
/* CREATE an account */
app.post('/users/signup', (req, res) => {
db.users.find({ $or: [{ email: req.body.email }, { username: req.body.username }] }).then(user => {
if (err) {
return res.send(err);
}
if (user) {
if (user.email == req.body.email) {
return res.send("This email is already taken.")
}
return res.send("This username is already taken.")
}
else {
const data = {
username: req.body.username,
name: req.body.name,
firstname: req.body.firstname,
email: req.body.email,
location: req.body.location,
type: req.body.type,
password: req.body.password
};
db.users.create({
username: data.username,
name: data.name,
firstname: data.firstname,
email: data.email,
location: data.location,
type: data.type,
password: data.password
}).then(newUser => {
res.send("newUser saved to database")
// `req.user` contains the authenticated user.
//TODO : res.redirect('/profile/' + req.body.username);
})
.catch(err => {
console.log(err);
res.status(400).send("unable to save this newUser to database");
})
}
}).catch(err => {
console.log(err);
res.status(400).send("signup failed");
})
})
And my model (db.users) :
const bcrypt = require("bcrypt-nodejs");
module.exports = (sequelize, DataTypes) => {
// TABLE USERS
const Users = sequelize.define('users', {
user_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
name: {
type: DataTypes.STRING,
allowNull: false
},
firstname: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
type: {
type: DataTypes.STRING,
allowNull: false,
},
location: {
type: DataTypes.STRING
},
password: {
type: DataTypes.STRING,
allowNull: false
}
});
// methods ======================
// generating a hash
Users.generateHash = function (password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
// checking if password is valid
Users.validPassword = function (password) {
return bcrypt.compareSync(password, this.password);
};
//hashing a password before saving it to the database
Users.beforeCreate('save', function (next) {
var user = this;
bcrypt.hash(user.password, 10, function (err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
})
});
return Users;
};
My passport.js :
// load all the things we need
var LocalStrategy = require('passport-local').Strategy;
var db = require('../db')
// expose this function to our app using module.exports
module.exports = function (passport) {
var User = db.users;
// =========================================================================
// passport session setup ==================================================
// =========================================================================
// required for persistent login sessions
// passport needs ability to serialize and unserialize users out of session
// used to serialize the user for the session
passport.serializeUser(function (user, done) {
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser(function (id, done) {
User.find({
where: { user_id: user_id }
})
.then(function (user) {
done(err, user);
}).catch(function (err) {
return done(err);
})
});
// =========================================================================
// LOCAL LOGIN =============================================================
// =========================================================================
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called 'local'
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function (req, email, password, done) { // callback with email and password from our form
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ email : email }, function (err, user) {
// if there are any errors, return the error before anything else
if (err)
return done(err);
// if no user is found, return the message
if (!user)
return done(null, false, { message: 'User not found.' }); // req.flash is the way to set flashdata using connect-flash
// if the user is found but the password is wrong
if (!user.validPassword(password))
return done(null, false, { message: 'Incorrect password.' }); // create the loginMessage and save it to session as flashdata
// all is well, return successful user
return done(null, user);
});
}));
};
I can't find the source of the problem. I'm new with Node.js and I'm stuck since few hours. Is there someone here who can help me please?
After discussion, we found out that the first error comes from the beforeSave hook on the User db model, where a hash function was used missing a null parameter as third parameter, and thus bcrypt was throwing a "no callback passed" error. There is several other things going wrong, such as missuses of promises and callbacks, and I suggest going through a learning of promises, and double checking documentation and examples on how to use the libraries, such as Sequelize and bcrypt (how to generate and use a salt for example).
Previous answer
I think the problem comes from your 'local-login' strategy : passport uses callbacks, but sequelize uses promises, so your User.findOne callback is never called. Try something like that :
app.post("/users/signup", (req, res) => {
return db.users
.find({
$or: [
{
email: req.body.email
},
{
username: req.body.username
}
]
})
.then(user => {
if (user) {
if (user.email == req.body.email) {
return res.send("This email is already taken.");
}
return res.send("This username is already taken.");
} else {
return db.users
.create({
username: req.body.username,
name: req.body.name,
firstname: req.body.firstname,
email: req.body.email,
location: req.body.location,
type: req.body.type,
password: req.body.password
})
.then(newUser => {
res.send("newUser saved to database");
});
}
})
.catch(err => {
console.error(err);
res.status(400).send("unable to save this newUser to database");
});
});
Same thing for the route, and the deserialize :
app.post('/users/signup', (req, res) => {
db.users.find({ $or: [{ email: req.body.email }, { username: req.body.username }] })
.then(user=>{})
.catch(err=>{})

Why does sequelize return 500 [object SequelizeInstance:Users] when connecting to express session?

The Problem:
I'm trying to authenticate a user with passport-local strategy. I can successfully retrieve the user from the database, but when I try to redirect to ' / ' and initiate a new session, my server responds with 500 [object SequelizeInstance:Users]
Context:
I came across the 'connect-session-sequelize' node package and implemented it in my app.js:
const db = require('./models/db.js');
const userController = require('./controllers/user');
const myStore = new SequelizeStore({
db: db.sequelize,
table: 'Sessions'
});
app.use(cookieParser());
app.use(session({
secret: process.env.SESSION_SECRET,
store: myStore,
resave: false, // per the express-session docs this should be set to false
proxy: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.get('/login', userController.getLogin);
app.post('/login', userController.postLogin);
app.get('/signup', userController.getSignup);
app.post('/signup', userController.postSignup);
db.sequelize.sync({
force: false,
}).then(() => {
app.listen(app.get('port'), () => {
console.log('%s App is running at http://localhost:%d in %s mode', chalk.green('✓'), app.get('port'), app.get('env'));
});
});
My routes handling the requests:
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const db = require('../models/db.js');
const User = db.user;
passport.serializeUser((user, done) => {
console.log('serializing user: ', user);
done(null, user);
});
passport.deserializeUser((id, done) => {
User.findById(id).then((user) => {
done(user);
});
});
passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
User.findOne({
where: { email: email.toLowerCase() },
}).then((user) => {
if (!user) {
return done(null, false, { msg: `Email ${email} not found.` });
}
user.comparePassword(password, (err, isMatch) => {
if (err) { return done(err); }
if (isMatch) {
return done(null, user);
}
return done(null, false, { msg: 'Invalid email or password.' });
});
});
}));
exports.postSignup = (req, res, next) => {
const errors = req.validationErrors();
const user = new User({
username: req.body.name,
email: req.body.email,
password: req.body.password
});
User.findOne({
where: { email: req.body.email }
}).then((existingUser) => {
if (existingUser) {
req.flash('errors', { msg: 'Account with that email address already exists.' });
return res.redirect('/signup');
}
user.save((err) => {
req.logIn(user, (err) => {
req.session.save(() => res.redirect('/'));
});
});
});
};
'Users' DB model:
const bcrypt = require('bcrypt-nodejs');
module.exports = (sequelize, DataTypes) => {
const Users = sequelize.define('Users', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
allowNull: false,
unique: true
},
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password_hash: {
type: DataTypes.STRING
},
password: {
type: DataTypes.VIRTUAL,
allowNull: false,
unique: false,
set(value) {
const that = this;
bcrypt.genSalt(10, (err, salt) => {
if (err) { return console.log('BCRYPT GEN SALT ERR:', err); }
bcrypt.hash(value, salt, null, (error, hash) => {
if (error) { return console.log('BCRYPT HASH ERR:', err); }
console.log('--> SEQ: BCRYPT hash SET', hash);
that.setDataValue('password', value);
that.setDataValue('password_hash', hash);
});
});
}
}
});
Users.prototype.comparePassword = function comparePassword(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password_hash, (err, isMatch) => {
cb(err, isMatch);
});
};
return Users;
};
'Sessions' DB model:
module.exports = (sequelize, DataTypes) => sequelize
.define('Sessions', {
sid: {
type: DataTypes.STRING,
primaryKey: true
},
userId: DataTypes.STRING,
expires: DataTypes.DATE,
data: DataTypes.STRING(50000),
});
Server Response:
POST /login 302 234.474 ms - 46
Executing (default): SELECT "sid", "userId", "expires", "data", "createdAt",
"updatedAt" FROM "Sessions" AS "Sessions" WHERE "Sessions"."sid" =
'Jhmo9YA9MhwKEVa6zWxvvRQGdYoXmdSQ';
Executing (default): SELECT "id", "username", "email", "password_hash", "phone",
"age", "gender", "location", "createdAt", "updatedAt" FROM "Users" AS "Users"
WHERE "Users"."id" = 'c40d4cd6-4937-4a66-b785-d302e9fa6c40';
Executing (default): UPDATE "Sessions" SET "expires"='2018-05-10 06:31:42.797
+00:00',"updatedAt"='2018-05-09 06:31:42.797 +00:00' WHERE "sid" =
'Jhmo9YA9MhwKEVa6zWxvvRQGdYoXmdSQ'
[object SequelizeInstance:Users]
GET / 500 5.420 ms - -
The deserializeUser should call done with error first:
passport.serializeUser((user, done) => {
console.log('serializing user: ', user.id);
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then((user) => {
done(null, user);
}).catch(done);
});

Can't use facebook to login because user model setting in local passport

I am trying to build a login system with Facebook, Google and local passport, I am using Node.js, passport.js, and ORM to finish it, but I faced one problem now, my user model is like below
const User = connection.define('user', { googleID: {
type: Sequelize.STRING, }, facebookID: {
type: Sequelize.STRING, }, firstname: {
type: Sequelize.STRING,
notEmpty: true,
},
lastname: {
type: Sequelize.STRING,
notEmpty: true,
},
username: {
type: Sequelize.TEXT,
},
email: {
type: Sequelize.STRING,
validate: {
isEmail: true,
},
},
password: {
type: Sequelize.STRING,
allowNull: false,
},
last_login: {
type: Sequelize.DATE,
}, });
After this setting, whenever I want to log on my website through Facebook, it kept showing SEQUELIZE ERROR, password required! I know it the reason is I don't have password while signing in with Facebook, but what can I do with this issue? I saw this page on GitHub https://github.com/feathersjs/authentication/issues/168
but I still have no clue in using this feathers-joi tool, can anyone give me some advice? Thank you
Here is my Facebook.js code
const passport = require('passport');
const cookieSession = require('cookie-session');
const FacebookStrategy = require('passport-facebook').Strategy;
const User = require('../models/User');
const keys = require('../secret/keys.js');
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
done(null, user);
});
});
passport.use(new FacebookStrategy({
clientID: keys.facebookClientID,
clientSecret: keys.facebookClientSecret,
callbackURL: '/auth/facebook/callback',
}, (accessToken, refreshToken, profile, done) => {
User.findOne({ where: { facebookID: profile.id } }).then(existinguser => {
if (existinguser) {
//Nothing will happen, the ID already exists
done(null, existinguser);
}else {
User.create({ facebookID: profile.id }).then(user => done(null, user));
}
});
}));
My passport.js would be like below(Still need some fix)
var bCrypt = require('bcrypt-nodejs');
module.exports = function(passport, user) {
var User = user;
var LocalStrategy = require('passport-local').Strategy;
passport.use('local-signup', new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) {
var generateHash = function(password) {
return bCrypt.hashSync(password, bCrypt.genSaltSync(8), null);
};
User.findOne({
where: {
email: email
}
}).then(function(user) {
if (user)
{
return done(null, false, {
message: 'That email is already taken'
});
} else
{
var userPassword = generateHash(password);
var data =
{
email: email,
password: userPassword,
firstname: req.body.firstname,
lastname: req.body.lastname
};
User.create(data).then(function(newUser, created) {
if (!newUser) {
return done(null, false);
}
if (newUser) {
return done(null, newUser);
}
});
}
});
}
));
}
try this
passport.use(new FacebookStrategy({
clientID: keys.facebookClientID,
clientSecret: keys.facebookClientSecret,
callbackURL: '/auth/facebook/callback',
}, (accessToken, refreshToken, profile, done) => {
User.findOne({ where: { facebookID: profile.id } }).then(existinguser => {
if (existinguser) {
//Nothing will happen, the ID already exists
done(null, existinguser);
}else {
var object = {
facebookID: profile.id,
password: ""
}
User.create(object).then(user => done(null, user));
}
});
}));

Authentication issue in Nodejs: ValidationError: Path `password` is required

I am getting an error ValidationError: Path password is required, when trying to register a new User. I am new to Node, programming, and authentication.
Here is the code I have, and then I will walk through what I have done to try to trouble shoot it.
User model and schema
var mongoose = require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");
var bcrypt = require("bcrypt-nodejs");
var UserSchema = new mongoose.Schema({
username: {type: String, unique: true, required: true},
password: {type: String, unique: true, required: true},
// avatar: String,
firstName: String,
lastName: String,
email: {type: String, unique: true, required: true},
resetPasswordToken: String,
resetPasswordExpires: Date,
// isAdmin: {type: Boolean, default: false}
});
UserSchema.plugin(passportLocalMongoose);
//save function -- hashes and then saves the password
UserSchema.pre('save', function(next) {
var user = this;
var SALT_FACTOR = 5;
if (!user.isModified('password')) return next();
bcrypt.genSalt(SALT_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();
});
});
});
//compares and checks password
UserSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
module.exports = mongoose.model("User", UserSchema);
Here is my app.js file:
var express = require("express"),
app = express(),
bodyParser = require("body-parser"),
mongoose = require("mongoose"),
expressSanitizer = require("express-sanitizer"),
passport = require("passport"),
cookieParser = require("cookie-parser"),
LocalStrategy = require("passport-local"),
session = require("express-session"),
nodemailer = require("nodemailer"),
bcrypt = require("bcrypt-nodejs"),
async = require("async"),
crypto = require("crypto"),
flash = require("connect-flash"),
moment = require("moment"),
User = require("./models/user"),
// seedDB = require("./seeds"),
methodOverride = require("method-override");
// APP CONFIG
mongoose.connect("mongodb://localhost/blog", {useMongoClient: true});
//PRODUCTION CONFIG - LIVE URL GOES HERE!
app.set("view engine", "ejs");
app.use(express.static(__dirname + "/assets"));
app.use(bodyParser.urlencoded({extended: true}));
app.use(expressSanitizer());
app.use(methodOverride("_method"));
app.use(cookieParser('secret'));
//require moment
app.locals.moment = require('moment');
// seedDB(); //seed test data!
// PASSPORT CONFIGURATION
app.use(require("express-session")({
secret: "It's a secret to everyone!!",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
passport.use(new LocalStrategy(function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) return done(err);
if (!user) return done(null, false, { message: 'Incorrect username.' });
console.log(user);
User.comparePassword(password, function(err, isMatch) {
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Incorrect password.' });
}
});
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
app.use(function(req, res, next){
res.locals.currentUser = req.user;
res.locals.success = req.flash('success');
res.locals.error = req.flash('error');
next();
});
// REQUIRE ROUTES
var commentRoutes = require("./routes/comments"),
bpostRoutes = require("./routes/bposts"),
indexRoutes = require("./routes/index");
//USE ROUTES
app.use("/", indexRoutes);
app.use("/bposts", bpostRoutes);
app.use("/bposts/:id/comments", commentRoutes);
//RUN SERVER
app.listen(process.env.PORT, process.env.IP, function(){
console.log("The Server Has Started!");
});
Here is the index.js route file for posting new Users
// handle sign up logic
router.post("/register", function(req, res) {
var newUser = new User({
username: req.sanitize(req.body.username),
firstName:req.sanitize(req.body.firstName),
lastName: req.sanitize(req.body.lastName),
email: req.sanitize(req.body.email),
});
console.log(newUser);
// if (req.body.adminCode === 'secretcode123') {
// newUser.isAdmin = true;
// }
User.register(newUser, req.body.password, function(err, user) {
if (err) {
console.log(err);
return res.render("register", { error: err.message });
}
passport.authenticate("local")(req, res, function() {
req.flash("success", "Successfully Signed Up! Nice to meet you " + req.body.username);
res.redirect("/bposts");
});
});
});
As you can see in the index.js
I did a console.log(newUser) and it brings back the entire newUser object.
{ username: 'apple',
firstName: 'apple',
lastName: 'apple',
email: 'apple#gmail.com',
_id: 5a2eac41720dc60acffa02f4 }
Outside of this I am not sure what other testing I could do to locate whats happening behind the scenes. Any help or pointers on how to think about this kind of problem in the future so I can resolve it myself would be awesome. Thanks.
Here is the full StackTrace as well if it helps:
{ ValidationError: User validation failed: password: Path `password` is required.
at MongooseError.ValidationError.inspect (/home/ubuntu/workspace/node_modules/mongoose/lib/error/validation.js:57:23)
at formatValue (util.js:351:36)
at inspect (util.js:185:10)
at exports.format (util.js:71:24)
at Console.log (console.js:43:37)
at /home/ubuntu/workspace/routes/index.js:35:21
at /home/ubuntu/workspace/node_modules/passport-local-mongoose/index.js:213:33
at /home/ubuntu/workspace/node_modules/mongoose/lib/model.js:4038:16
at /home/ubuntu/workspace/node_modules/mongoose/lib/services/model/applyHooks.js:175:17
at _combinedTickCallback (internal/process/next_tick.js:73:7)
at process._tickCallback (internal/process/next_tick.js:104:9)
errors:
{ password:
{ ValidatorError: Path `password` is required.
at MongooseError.ValidatorError (/home/ubuntu/workspace/node_modules/mongoose/lib/error/validator.js:25:11)
at validate (/home/ubuntu/workspace/node_modules/mongoose/lib/schematype.js:782:13)
at /home/ubuntu/workspace/node_modules/mongoose/lib/schematype.js:829:11
at Array.forEach (native)
at SchemaString.SchemaType.doValidate (/home/ubuntu/workspace/node_modules/mongoose/lib/schematype.js:789:19)
at /home/ubuntu/workspace/node_modules/mongoose/lib/document.js:1528:9
at _combinedTickCallback (internal/process/next_tick.js:73:7)
at process._tickCallback (internal/process/next_tick.js:104:9)
message: 'Path `password` is required.',
name: 'ValidatorError',
properties: [Object],
kind: 'required',
path: 'password',
value: undefined,
reason: undefined,
'$isValidatorError': true } },
_message: 'User validation failed',
name: 'ValidationError' }
Basically you defined that you need a password in your models.
var UserSchema = new mongoose.Schema({
username: {type: String, unique: true, required: true},
password: {type: String, unique: true, required: true}, // <-- See here
firstName: String,
lastName: String,
email: {type: String, unique: true, required: true},
resetPasswordToken: String,
resetPasswordExpires: Date
});
But when you are saving you aren't passing it a password:
var newUser = new User({
username: req.sanitize(req.body.username),
firstName:req.sanitize(req.body.firstName),
lastName: req.sanitize(req.body.lastName),
email: req.sanitize(req.body.email),
/* password: req.sanitize(req.body.password) // <- Probably missing something like this.*/
});
I didn't write the following blog, but I think it might be helpful for you.
https://github.com/DDCreationStudios/Writing/blob/master/articles/AuthenticationIntro.md#user-registration
The way you did the passport authentication, it does not need to have the password required true in the user schema. Passport will automatically check the password if it's missing. If password is missing, passport will throw an error. So no need to make the password required true because passport will do the same job internally. This worked for me.
user.js
var userSchema=new mongoose.Schema({
username:{type:String,required:true},
email:{type:String,required:true},
password:String
});
server.js
app.post("/signUp",(req,res)=>{
User.register(new User({username:req.body.username,email:req.body.email}),req.body.password,(err,user)=>{
if(err){
console.log(err);
}else{
passport.authenticate("local")(req,res,()=>{
res.redirect("/");
});
}
});
});
var UserSchema = new mongoose.Schema({
username: {type: String, unique: true, required: true},
password: String ,//remove previous line and just add String to password field.
// avatar: String,
firstName: String,
lastName: String,
email: {type: String, unique: true, required: true},
resetPasswordToken: String,
resetPasswordExpires: Date,
// isAdmin: {type: Boolean, default: false}
});
You had this issue because you set the password field in your schema to
password: {type: String, required: true},
. You do not need that.
Use the info below and you will be fine.
password: {type: String}
See the working code below:
**user.js**
const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');
const userSchema = new mongoose.Schema({
username: {type: String, required: true},
password: {type: String} // no need to add required!
});
**server.js**
app.post('/register', (req, res) => {
const { username, password } = req.body;
User.register(new User({ username }), password, (err,user) => {
if (err) {
return res.render('register', { message: err.message, messageType:
'error', user: req.user });
}else{
passport.authenticate('local')(req, res, () => {
res.redirect('/dashboard');
});
}
});
});

PassportJS Not Recognizing Password Change

I created added some password reset functionality to my sign up flow that allows users to change their password, but when a user resets their password, they are unable to login with the new password and I receive a consoled Server Error. I'm wondering if this has to do with updating the user password outside of the passportjs logic or maybe because my password isn't hashed on the update?
Here is the console log:
GET /login 200 9.507 ms - 1793
GET /stylesheets/styles.css 304 1.093 ms - -
Database query triggered
Executing (default): SELECT `user_id`, `first_name` AS `firstName`, `last_name` AS `lastName`, `email`, `password`, `organization_id` AS `organizationId`, `reset_password_token` AS `resetPasswordToken`, `reset_password_expires` AS `resetPasswordExpires`, `createdAt`, `updatedAt` FROM `user` AS `user` WHERE `user`.`email` = 'test#gmail.com' LIMIT 1;
Server Error
POST /login 302 16.169 ms - 56
GET /login 200 10.926 ms - 1862
Here is my password update route:
var express = require('express');
var siteRoutes = express.Router();
var path = require('path');
var async = require('async');
var crypto = require('crypto');
var nodemailer = require('nodemailer');
var sgTransport = require('nodemailer-sendgrid-transport');
var moment = require('moment');
var url = require('url');
var passport = require(path.resolve(__dirname, '..', '..','./config/passport.js'));
var models = require('../models/db-index');
/*==== /RESET ====*/
siteRoutes.route('/reset/:token')
.get(function(req, res){
var urlPath = url.parse(req.url).pathname.split('/');
var urlToken = urlPath[2];
console.log(urlToken);
models.User.findOne({
where: {
resetPasswordToken: req.params.token,
resetPasswordExpires: {
$gt: moment().format('YYYY-MM-DD HH:mm:ss')
}
}
}).then(function(){
res.render('pages/app/reset-password.hbs',{
urlToken: urlToken
});
})
})
.post(function(req, res){
async.waterfall([
function(done){
models.User.update({
password: req.body.password,
resetPasswordToken: null,
resetPasswordExpires: null
}, { where: {
resetPasswordToken: req.body.token,
resetPasswordExpires: {
$gt: moment().format('YYYY-MM-DD HH:mm:ss')
}
}})
// Nodemailer
var transporter = nodemailer.createTransport(sgTransport(options));
var mailOptions = {
from: '"Tester" <test#test.com',
to: 'test#gmail.com', //Replace with Email
subject: 'Your password has been changed',
text: 'Hello,\n\n' +
'This is a confirmation that the password for your account ' + 'test#gmail.com' + ' has just been changed.\n'
};
transporter.sendMail(mailOptions, function(error, info){
if(error){
return console.log(error + 'During Post');
}
console.log('Message sent: ' + info.response);
})
}
])
res.redirect('/login');
});
Here is my login routing:
/*==== Login ====*/
siteRoutes.route('/login')
.get(function(req, res){
res.render('pages/site/login.hbs',{
error: req.flash('error')
});
})
.post(passport.authenticate('local', {
successRedirect: '/app',
failureRedirect: '/login',
failureFlash: 'Invalid email or password.'
}));
Here is the user model:
var bcrypt = require('bcrypt-nodejs');
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('user', {
user_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
firstName: {
type: DataTypes.STRING,
field: 'first_name'
},
lastName: {
type: DataTypes.STRING,
field: 'last_name'
},
email: {
type: DataTypes.STRING,
isEmail: true,
unique: true,
set: function(val) {
this.setDataValue('email', val.toLowerCase());
}
},
password: DataTypes.STRING,
organizationId: {
type: DataTypes.INTEGER,
field: 'organization_id',
allowNull: true
},
resetPasswordToken: {
type: DataTypes.STRING,
field: 'reset_password_token'
},
resetPasswordExpires: {
type: DataTypes.DATE,
field: 'reset_password_expires'
}
}, {
freezeTableName: true,
classMethods: {
associate: function(db) {
User.belongsToMany(db.Organization, { through: 'member', foreignKey: 'organizationId'})
},
generateHash: function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
},
instanceMethods: {
validPassword: function(password) {
return bcrypt.compareSync(password, this.password);
},
},
});
return User;
}
Here is the passportjs logic:
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var models = require('../app/models/db-index');
/*==== Passport Configuration ====*/
// Serialize sessions
passport.serializeUser(function(user, done) {
console.log("User ID: " + user.user_id + " is serializing");
done(null, user.user_id);
});
passport.deserializeUser(function(user_id, done) {
models.User.find({where: {user_id: user_id}}).then(function(user){
console.log("User ID: " + user.user_id + " is deserializing");
done(null, user);
}).error(function(err){
done(err, null);
});
});
//Login logic
passport.use('local', new LocalStrategy({
passReqToCallback: true,
usernameField: 'email'
}, function(req, email, password, done) {
console.log("Database query triggered");
//Find user by email
models.User.findOne({
where: {
email: req.body.email
}
}).then(function(user) {
if (!user) {
done(null, false, { message: 'The email you entered is incorrect' }, console.log("Unknown User"));
} else if (!user.validPassword(password)){
done(null, false, console.log("Incorrect Password"));
} else {
console.log("User match");
done(null, user);
}
}).catch(function(err) {
console.log("Server Error");
return done(null, false);
});
}));
//Sign Up Logic
passport.use('local-signup', new LocalStrategy({
passReqToCallback: true,
usernameField: 'email'
}, function(req, email, password, done){
models.User.findOne({
where: {
email: email
}
}).then(function(existingUser){
if (existingUser)
return done(null, false, req.flash('error', 'Email already exists.'));
if (req.user) {
var user = req.user;
user.firstName = firstName;
user.lastName = lastName;
user.email = email;
user.password = models.User.generateHash(password);
user.save().catch(function(err){
throw err;
}).then(function(){
done(null, user, req.flash('error', 'All fields need to be filled in'));
});
} else {
var newUser = models.User.build({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: models.User.generateHash(password)
});
newUser.save().then(function(){
done(null, newUser);
}).catch(function(err){
done(null, false, console.log(err));
});
}
}).catch(function(e){
done(null, false, req.flash('error', 'All fields need to be filled in'),console.log(e.email + e.message));
})
}));
module.exports = passport;
Looks like you forget to generate your password again.
.post(function(req, res){
async.waterfall([
function(done){
models.User.update({
password: models.User.generateHash(req.body.password)

Resources