Failed to load user into session with - node.js

For some reason, testing my authentication, I created a user with the login 8===D, and password 123, and then passportjs says it fails to serialize that user when i try to login, however do it with any regular like username like bill#kentucky.com then it works fine and serializes the user on login. I can provide code as a example as well, but does it have do with it having strange characters(like '=')?
Also why can I serialize with just .id instead of ._id, why does that work? I use mongoDB and it always generates a _id instead.
exports.postLogin = function(req, res, next) {
// Do email and password validation for the server
passport.authenticate('local', function(err, user, info) {
if(err) return next(err);
if(!user) {
req.flash('errors', {msg: info.message});
console.log("ERROR BOYS");
}
// Passport exposes a login() function on req (also aliased as logIn()) that can be used to establish a login session
req.logIn(user, function(err) {
console.log("User: " +user + " has been logged in");
if(err) return next(err);
req.flash('success', { msg: 'Success! You are logged in'});
res.end('Success');
});
})(req, res, next);
};
/**
* GET /logout
*/
exports.getLogout = function(req, res, next) {
// Do email and password validation for the server
console.log("You have been logged out");
req.logout();
res.redirect('/');
};
/**
* POST /signup
* Create a new local account
*/
exports.postSignUp = function(req, res, next) {
var user = new User({
email: req.body.email,
password: req.body.password,
profile: {
firstName : req.body.firstName,
lastName : req.body.lastName,
section : req.body.section
}
});
User.findOne({email: req.body.email}, function(err, existingUser) {
if(existingUser) {
req.flash('errors', { msg: 'Account with that email address already exists' });
res.redirect('/sign');
}
user.save(function(err) {
if(err) return next(err);
req.logIn(user, function(err) {
if(err) return next(err);
console.log('Successfully created');
console.log('Printing user');
console.log(user);
console.log('Print our body from our request');
console.log(req.body);
res.redirect('/');
res.end();
});
});
});
};
And the serialize part:
/* Initializing passport.js */
var User = require('../models/user');
var local = require('./passport/local');
/*
* Expose
*/
module.exports = function(app, passport, config) {
// serialize sessions
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
//use the following strategies
passport.use(local);
};
Maybe it has to do with the login??::???
else {
renderedResult = (
<div>
<div className={styles['wrapper']}>
<div className={styles['container']}>
<h1 className={styles['welcomelogin']}>Welcome, If ya just signed up then go ahead and sign in</h1>
<fieldset className = {styles['loginSet']}>
<input type="text" className = {styles['form-control']} placeholder="Username" ref = "email" name = "email" />
<input type="password" className = {styles['form-control']} placeholder="Password" ref = "password" name = "password" />
<button type="submit" className={styles['login-button']} to = "dashboard" onClick={this._onLoginSubmit}>Login</button>
</fieldset>
<hr/>
<p>Need an account? <Link to="register">Signup</Link></p>
</div>
</div>
</div>
);
}}
return (
<div>
{renderedResult}
</div>
);
}
}
User Model:
/**
* Defining a User Model in mongoose
*
*/
var bcrypt = require('bcrypt-nodejs');
var mongoose = require('mongoose');
var crypto = require('crypto');
// Other oauthtypes to be added
/*
User Schema
*/
var UserSchema = new mongoose.Schema({
email: { type: String, unique: true},
password: String,
tokens: Array,
profile: {
firstName: { type: String, default: ''},
lastName: {type: String, default: ''},
gender: { type: String, default: ''},
location: { type: String, default: ''},
website: { type: String, default: ''},
picture: { type: String, default: ''},
section: { type: String, default: ''}
},
resetPasswordToken: String,
resetPasswordExpires: Date,
google: {},
isStaff : { type: Boolean, default: false}
});
/**
* Password hash middleware.
*/
UserSchema.pre('save', function(next) {
var user = this;
if (!user.isModified('password')) return next();
bcrypt.genSalt(5, 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();
});
});
});
/*
Defining our own custom document instance method
*/
UserSchema.methods = {
comparePassword: function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if(err) return cb(err);
cb(null, isMatch);
})
}
};
/**
* Statics
*/
UserSchema.statics = {}
module.exports = mongoose.model('User', UserSchema);

Serialize function determine what data from the user object should be stored in the session. The result of the serializeUser method is attached to the session as req.session.passport.user = {} here for instance it would be(as we provide id as key)
req.session.passport.user = {id:'xyz'}
Passport will serialize and deserialize user instances to and from the
session.
In this example, only the user ID is serialized to the session,
keeping the amount of data stored within the session small. When
subsequent requests are received, this ID is used to find the user,
which will be restored to req.user.
The serialization and deserialization logic is supplied by the
application, allowing the application to choose an appropriate
database and/or object mapper, without imposition by the
authentication layer.
http://passportjs.org/docs
Basically, you need to work around with this, transforming those =in html code or not allow these symbols. Just remember that the session is converted in strings with & and =. Like :
data=data&data2=data2

Related

Keep hashed password on update using NodeJs Mongo and Express

I am trying to build an application using MEAN. On register, everything works fine, user will be introduced into database with the fields password and verify hashed. But on update, the password and verify won't be hashed anymore and they will be added into database as a plain text. How can I resolve this? (I don't have the frontend code yet, I used Postman to send the request)
This is what I have by now:
model.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt');
var schema = new Schema({
firstname: { type: String, required: true },
lastname: { type: String, required: true },
email: { type: String, required: true },
password: { type: String, required: true },
verify: { type: String, required: true },
});
schema.pre('save', function (next) {
var user = this;
bcrypt.hash(user.password, 10, function (err, hash) {
if (err) {
return next(err);
}
user.password = hash;
user.verify = hash;
next();
});
});
module.exports = mongoose.model('User', schema);
controller.js
var router = express.Router();
// register user
router.post('/register', function (req, res, next) {
addToDB(req, res);
});
async function addToDB(req, res) {
var user = new User({
firstname: req.body.firstname,
lastname: req.body.lastname,
email: req.body.email,
password: req.body.password,
verify: req.body.verify
});
try {
doc = await user.save();
return res.status(201).json(doc);
}
catch (err) {
return res.status(501).json(err);
}
}
// update user
router.put('/:id', function (req, res, next) {
User.findByIdAndUpdate(req.params.id, req.body, function (err, post) {
if (err) {
console.log('Error in user update: ' + JSON.stringify(err, undefined, 2));
return next(err);
}
res.json(post);
});
});
Update your Mongoose middleware to only hash the password if it has been modified (or is new) e.g.
schema.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(10, function(err, salt) {
if (err) return next(err);
// hash the password along with our new salt
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
user.verify = hash
next();
});
});
});
Because findByIdAndUpdate is a wrapper around findOneAndUpdate, better to use save so that the pre save hook is invoked
var _ = require('lodash');
// update user
router.put('/:id', function (req, res, next) {
// fetch user
User.findById(req.params.id, function(err, post) {
if (err) return next(err);
_.assign(post, req.body); // update user
post.save(function(err) {
if (err) return next(err);
return res.json(200, post);
})
});
});

Node JS Authentications with passport-jwt unauthorized

Im trying to setup my Node JS API.
I have a User model :
// Dependencies
var restful = require('node-restful');
var mongoose = restful.mongoose;
var bcrypt = require('bcrypt');
// Schema
var userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true},
firstname: {
type: String,
required: true
},
lastname: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true,
lowercase: true
},
password: {
type: String,
required: true},
},
{
timestamps: true
});
// Saves the user's password hashed
userSchema.pre('save', function (next) {
var user = this;
if (this.isModified('password') || this.isNew) {
bcrypt.genSalt(10, function (err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
} else {
return next();
}
});
// Use bcrypt to compare passwords
userSchema.methods.comparePassword = function(pw, cb) {
bcrypt.compare(pw, this.password, function(err, isMatch) {
if (err) {
return cb(err);
}
cb(null, isMatch);
});
};
module.exports = restful.model('Users', userSchema);
I want to use passport with jwt for authentication :
// Dependencies
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var config = require('../config/database');
// Load models
var User = require('../models/user');
// Logique d'authentification JWT
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('JWT');
opts.secretOrKey = config.secret;
opts.audience = 'localhost';
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findById(jwt_payload._id, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
Company.findById(jwt_payload._id, function(err, company) {
if (err) {
return done(err, false);
}
if (company) {
done(null, company);
} else {
done(null, false)
}
});
}));
};
And my route for authentication :
// User
router.post('/users/login', (req, res) => {
User.findOne({
email: req.body.email
}, (err, user) => {
if (err) throw err;
if (!user) {
res.json({success: false, message: 'Authentication failed. User not found.'});
} else {
// Check if passwords matches
user.comparePassword(req.body.password, (err, isMatch) => {
if (isMatch && !err) {
// Create token if the password matched and no error was thrown
var token = jwt.sign(user, config.secret, {
expiresIn: 10080 // in seconds
});
res.json({success: true, token: 'JWT ' + token, user: {
id: user._id,
username: user.username,
email: user.email
}});
} else {
res.json({success: false, message: 'Authentication failed. Passwords did not match.'});
}
});
}
});
});
Everything work great on postman.
The token is correctly generated and signed with user's informations.
But i have a problem with the authentication on a protected route :
router.get('/users/profile', passport.authenticate('jwt', { session: false }), function(req, res) {
res.send('It worked! User id is: ' + req.user._id + '.');
});
Everytime, it gives me an "Unauthorized 401" Error.
I really dont know where is the problem, i think the problem is around jwtFromRequest, i also tried with Bearer but it also doesn't work...
I think a good option to avoid this kind of problems is to start from a base project that uses this authentication strategy, and after you have that working, modify it with your functionality.
Here you have an example with jwt authentication strategy and Refresh token implementation: https://solidgeargroup.com/refresh-token-autenticacion-jwt-implementacion-nodejs?lang=es

bcrypt login is not working in production while works on localhost

I used the user model like below:
the usermodel uses bcrypt authentication and is working fine over the localhost.
In the production, the registration works fine and while registering the user gets logged in. However, in the login it gives the error "Either Username or Password is wrong".
var mongoose = require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");
var bcrypt = require('bcrypt-nodejs');
var userSchema = new mongoose.Schema({
username: {type: String, required: true, unique: true},
email : {type: String, required: true, unique: true},
password : {type: String},
twitterId : String,
twitterToken: String,
profileUserName: String,
displayName : String,
coachingUser: Boolean,
coaching_name: String,
resetPasswordToken: String,
resetPasswordExpires: Date
});
userSchema.plugin(passportLocalMongoose);
userSchema.pre('save', function(next) {
var user = this;
var SALT_FACTOR = 5;
console.log("in the presave method");
if (!user.isModified('password'))
{
//console.log("in the presave method");
return next();
}
bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, null, function(err, hash) {
console.log("in the presave method");
if (err) return next(err);
user.password = hash;
next();
});});});
userSchema.methods.comparePassword = function(candidatePassword, cb) {
console.log("candidatePassword: " + candidatePassword);
console.log(this.password);
var passlen = this.password;
console.log("password length: " + passlen.length);
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
console.log("isMatch: " + isMatch);
if (err) return cb(err);
cb(null, isMatch);
});
};`
module.exports = mongoose.model("User",userSchema);
in the localhost, the bcrypt login is working fine. However in the production over https://mlab.com/home and digitalocean, the login always gives Email or password not correct.
router.post('/login', function(req, res, next) {
console.log(req);
passport.authenticate('local', function(err, user, info) {
if (err) return next(err)
if (!user) {
//console.log(user);
req.flash("error", "Either Username or Password is wrong");
return res.redirect('/login');
}
req.logIn(user, function(err) {
if (err) return next(err);
{
//console.log(user);
req.flash("success","Successfully logged in as " + user.username);
res.redirect('/coaching');
}
});
})(req, res, next);
});
Kindly provide help as to why in production the particular logic not work

NodeJS, Passport: how to change user's password by admin

I start to make admin panel for modify users. I want to have ability to change users passwords by admin. For authentication I use passport. The I change user's password - the data is saving to mongo, but after that, user can't logIn to site.
Please, help.
My Passport configuration:
module.exports = function(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.use('local-login', new LocalStrategy({
usernameField : 'email',
passwordField : 'password_first',
passReqToCallback : true
},
function(req, email, password, done) {
User.findOne({ 'email' : email }, function(err, user) {
if (err)
return done(err);
if (!user)
return done(null, false, function(req, res){
res.send({error: true})
});
if (!user.isAuthenticated )
return done(null, false, function(req, res){
res.send({error: true})
});
if (!user.validPassword(password))
return done(null, false, function(req, res){
res.send({error: true})
});
if(user){
return done(null, user);
}
});
}));
User model:
var userSchema = mongoose.Schema({
date : Date,
role : String,
newRole : { type: mongoose.Schema.Types.ObjectId, ref: 'newRole'},
authToken : { type: String, required : true, unique:true },
isAuthenticated : { type: Boolean, required : true },
username : String,
email : String,
password_first : String,
firstName : String,
lastName : String,
address : String,
phone : String,
resetPasswordToken : String,
resetPasswordExpires: Date });
Methods:
userSchema.methods.generateHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
userSchema.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.password_first);
};
var User = mongoose.model('User', userSchema);
module.exports = User;
And finally:
exports.changeByAdmin = function (req, res, next) {
User.findById({ _id : req.params.id }, function(err, updateUser) {
if(err) return next(err);
if (!updateUser) return next();
updateUser.email = req.body.email;
updateUser.firstName = req.body.firstName;
updateUser.lastName = req.body.lastName;
updateUser.address = req.body.address;
updateUser.phone = req.body.phone;
updateUser.password_first = updateUser.generateHash(req.body.password_first);
updateUser.save(function(err, updateUser) {
if(err) return next(err);
if(!updateUser) return next();
res.send({ success: true });
});
});
};
If I change only email of user, the user can login with new email and old password, but if I change password - user can't login. Please, advise the solution of this problem. Thank you.

How to use methods on schema in mongoose + express

I'm getting the following error when I try to run user.comparePassword from exports.index.post (see below) -- I pasted all code to help narrow down the problem. The UserSchema.pre('save') method works fine, but not the one in ./routes/account.js (I'm using mongoose 3)
Here is the error I get.
Caught exception: [TypeError: Object { username: 'test4',
email: 'test4#test.com',
password: '$2a$10$Ix5vCuVYGIU7AmXglmfIxOyYnF6CiPJfw9HLSAGcRDxMJEttud/F6',
_id: 505fee7ce28f10711e000002,
__v: 0 } has no method 'comparePassword']
## ./app.js
app.post('/account', routes.account.index.post);
## ./models/user.js
var mongoose = require('mongoose')
, bcrypt = require('bcrypt')
, Schema = mongoose.Schema
, db = mongoose.createConnection('localhost', 'mydb');
var UserSchema = new Schema({
username : { type: String, required: true, index: { unique: true }, trim: true }
, email : { type: String, required: true, index: { unique: true }, trim: true, lowercase: true }
, password : { type: String, required: true, trim: true }
});
UserSchema.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(function(err, salt) {
if (err) return next(err);
// hash the password along with our new salt
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
next();
});
});
});
//compare supplied 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 = db.model('User', UserSchema);
##./routes/account.js
/*
* GET account home page.
*/
exports.index = {};
exports.index.get = function(req, res){
var d = { title: 'Edit account' };
res.render('account', { d: d } );
};
exports.index.post = function(req, res){
req.assert('email', 'Enter email').notEmpty().isEmail();
req.assert('password', 'Enter password').notEmpty().isAlphanumeric().len(5,20);
//user must confirm password
if ( req.body.password_new ) {
req.assert('password_new', 'Enter password').notEmpty().isAlphanumeric().len(5,20);
req.assert('password_new_confirm', 'Passwords must match').equals(req.body.password);
}
res.locals.err = req.validationErrors(true);
if ( res.locals.err ) {
var d = { title: 'Edit account' };
res.render('account', { d: d } );
return;
}
var User = require('../models/user')
, mongoose = require('mongoose')
, db = mongoose.createConnection('localhost', 'mydb');
var user = db.model('User', User.UserSchema);
user.find({username: req.session.user.username }, function(err, user){
if ( err ) return next(err);
/*********** THIS IS WHERE THE ERROR OCCURS **************/
user.comparePassword(req.body.password, function(err, isMatch) {
console.log("isMatch", isMatch);
if (err) next(err);
if (!isMatch) {
req.flash('error', 'Woops, looks like you mistyped your password.');
req.session.user = user;
res.locals.user = user;
res.redirect('/account');
return;
}
//user is authenticated
//session length
console.log(req.body);
});
});
};
user.find queries for 0 or more docs, so the second parameter to its callback is an array of docs, not a single doc. user.findOne queries for 0 or 1 docs, so the second parameter to its callback is either null or that single doc. So you're trying to call your schema's method on a JavaScript Array which of course won't work. Change that find call to a findOne and it should work.

Resources