Node.js Passport failed to deserilize user - javascript

My node.js app has two passport local strategies. One for back office users and another for guests. Both strategies query two different tables for authenticating their respective users.
Below is the passport.js code
var isOfficeUser = false;
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
if(isOfficeUser == true){
done(null, user.idSystemUser);
} else {
done(null, user.idGuests);
}
});
// used to deserialize the user
passport.deserializeUser(function(id, done) {
var userQuery;
if(isOfficeUser == true) {
userQuery = 'SELECT idSystemUser, Name, Email, Phone, (select IsGuest from SystemProfiles where idSystemProfile = ProfileId) as GuestFlag FROM SystemUsers WHERE idSystemUser = ? ';
} else {
userQuery = 'SELECT idGuests, Name, Email, RoomId, GuestsCount, Phone, RoomNo, ' +
'(select DATE_FORMAT(CheckInDT, \'%b %d %Y %h:%i %p\')) as CheckInDTString, ' +
'(select DATE_FORMAT(CheckOutDT, \'%b %d %Y %h:%i %p\')) as CheckOutDTString, ' +
'(select IsGuest from SystemProfiles where idSystemProfile = ProfileId) as GuestFlag ' +
'FROM Guests WHERE idGuests = ? ';
}
connectionPool.getConnection(function(err, connection){
if (err) {
winston.log('debug', '------------------------------------ Error: ' + err);
return;
}
connection.query(userQuery,[id],function(err, rows){
if(err) {
winston.info('info', '----------------------- ERROR: ' + err);
}
done(err, rows[0]);
connection.release();
});
});
});
//this stretegy is for guests
passport.use(
'guest',
new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'room',
passwordField : 'accesskey',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, room, accesskey, done) {
isOfficeUser = false;
connectionPool.getConnection(function(err, connection){
if(err) {
winston.log('debug', '------------------------------------ Error: ' + err);
return;
}
connection.query('SELECT idGuests, Name, Email, RoomId, RoomNo, GuestsCount, Phone FROM Guests WHERE RoomNo = ? and AccessKey = ?',[room, accesskey], function(err, rows){
connection.release();
if(err) {
return done(err);
}
if(!rows.length) {
return done(null, false, {message : {active : true, text : 'Incorrect Credentials'}});
}
// all is well, return successful user
return done(null, rows[0]);
});
});
})
);
//this stretegy is for office user
passport.use(
'office',
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) {
isOfficeUser = true;
connectionPool.getConnection(function(err, connection){
if(err) {
winston.log('debug', '------------------------------------ Error: ' + err);
return;
}
connection.query('SELECT idSystemUser, Name, Active, Email, ProfileId, Phone, Password FROM SystemUsers WHERE Email = ?',[email], function(err, rows){
connection.release();
if(err) {
return done(err);
}
if(!rows.length) {
return done(null, false, {message : {active : true, text : 'Incorrect Credentials'}});
}
//if the user access is revoked
if(rows[0].Active == 0) {
return done(null, false, {message : {active : true, text : 'Your administrator has revoked your access to portal. Please contact your Administrator to get this resolved.'}});
}
// if the user is found but the password is wrong
if(!bcrypt.compareSync(password, rows[0].Password)) {
return done(null, false, {message : {active : true, text : 'Incorrect Email or Password'}});
}
// all is well, return successful user
return done(null, rows[0]);
});
});
})
);
However, for some reason I get signed out of my session and it shows the error:
"failed to deserialize user out of session"
I have to refresh the browser and relogin but then after sometime it logs me off again.
What am I doing wrong here?

Related

PassportJS: serializeUser() Can't set headers after they are sent

I am simply trying to serialise the user in passportjs following the "normal" flow. However I keep getting the following error:
Error: Can't set headers after they are sent.
I don't understand where I could send the res since I am just using the middleware to send it. Here is how I call the authentication route:
routes.post('/signup', function(req, res, next) {
passport.authenticate('local-signup-sme', {
successRedirect : req.get('origin') + '/dashboard', // redirect to the secure profile section
failureRedirect : req.get('origin') + '/signupSME', // redirect back to the signup page if there is an error
failureFlash : true,
successFlash : 'User created !'
})(req, res, next)
});
and here is my local-signup-sme, i only call done() and I only call it once, or in a callback, I have checked if there was any if without an else but I can't find any...
What am I doing wrong ?
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.use('local-signup-sme', 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 in the req from our route (lets us check if a user is logged in or not)
},
function(req, _email, _password, done) {
if (typeof _password === 'undefined' ){
return done(null, false, {message: 'Password field is missing'})
} else if( typeof _email === 'undefined'){
return done(null, false, {message: 'Email field is missing'})
} else {
var password = bcrypt.hashSync(_password, bcrypt.genSaltSync(10)); //encrypt password and store the hash...
var companySelected = JSON.parse(req.body.companySelected);
if (!req.user) {
checkCompanyRegistrationStatus(companySelected.company_number,
function(){
checkUserRegistrationStatus(_email,
function(){
checkPasswordStrength(_password,
function(){
maindb.MainDBInsert("INSERT INTO Uncomplete_Users_Registrations (first_name, last_name, password, email, company_registrat_number, company_name) OUTPUT Inserted.id VALUES (?, ?, ?, ?, ?, ?);",
[req.body.firstName, req.body.lastName, password, _email, companySelected.company_number, companySelected.title],
function(user, rowsCount){
return done(null, user[0]);
},
function(code, err){
return done(null, false, {message: err});
}
);
}, function (err) {
return done(null, false, {message: err});
})
}, function(err){
return done(null, false, {message: err})
}
)
}, function(err){
return done(null, false, {message: err})
}
)
}, function(err){
return done(null, false, {message: err})
}
)
} else {
// user is logged in and already has a local account. Ignore signup. (You should log out before trying to create a new account, user!)
return done(null, req.user);
}
}
}));

Verify Email Before Logging In (Nodejs, Mongoose+mongodb, Passportjs)

I'm having a problem about verifying first my email address before i can login to my system . Here's my code so far.
This line of code is where i verify my email address when he clicks the link on his/her email address it will redirect to my login form.
router.get('/verify_email', function(req, res){
console.log('verify_email token: ', req.query.token);
User.findOne({authToken: req.query.token}, function(err, user){
if(err){
console.log(err);
} else {
console.log(user);
user.IsAuthenticated = true;
user.save(function(err){
if(err){
console.log(err);
} else {
console.log('Successfully updated user');
console.log(user);
sendgrid({
to: user.email,
from: 'pektospioneers.com',
subject:'Email COnfirmed!',
html: 'Thank you'
}, function(err, json){
if(err){
console.log(err);
} else {
console.log(json);
}
});
res.send(user);
}
});
}
});
req.flash("success_msg",'Email has been confirmed!');
res.redirect('/users/login');
});
and here's my usermodel
//new user in the model(user.js)
var newUser = new User({
name: name,
email: email,
authToken: authToken,
IsAuthenticated: false,
username: username,
password: password,
field: field,
e_money: e_money //temporary emoney
});
now the problem is here . I can still login directly after my registration even without confirming my email . so what should be the problem here sir/madame .
i tried the passportjs like this
passport.use(new LocalStrategy(
function(username, password, IsAuthenticated, done){
User.IfIsAuthenticated(IsAuthenticated, function(err, authenticated)}
if(err) throw err;
if(!authenticated){
return done(null, false, {message: 'Please confirm your email first'});
}
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) throw err;
if(isMatch){
return done(null, user);
}
else{
return done(null, false, {message: "Invalid password"});
}
});
});
});
}));
but its not working it always says return done is not a function
and on my user.js it's just like this
module.exports.getUserByUsername = function(username, callback){
var query = {username: username};
User.findOne(query, callback);
}
/*
*need to know if authenticated or not
*so that if it is not yet authenticated
*the newly registered user cannot logged in
*/
module.exports.ifIsAuthenticated = function(IsAuthenticated, callback){
var query = {IsAuthenticated:IsAuthenticated};
User.findOne(query, callback);
}
By adding here this line of code i achieved what i want
User.comparePassword(password, user.password, function(err, isMatch){
if(err) throw err;
if(isMatch && !User.email){
return done(null, false, {message: "Please confirm your email first!"});
}else if(isMatch && User.email){
return done(null, user);
}
else{
return done(null, false, {message: "Invalid password"});
}
});
Just need to answer this for the future reference of the others . Happy coding

Verification email with token in passport.js

I just looking for solution which makes verification email with token for my local autentification in passport.js
Is there some plugin or component for node which can make me verification easyer? Or I have to do it myself?
My controller
exports.postSignup = function(req, res, next) {
req.assert('email', 'Email is not valid').isEmail();
req.assert('password', 'Password must be at least 4 characters long').len(4);
req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
var errors = req.validationErrors();
if (errors) {
req.flash('errors', errors);
return res.redirect('/signup');
}
var user = User.build({
email: req.body.email,
password: req.body.password,
});
User
.find({ where: { email: req.body.email } })
.then(function(existingUser){
if (existingUser) {
req.flash('errors', { msg: 'Account with that email address already exists.' });
return res.redirect('/signup');
}
user
.save()
.complete(function(err){
if (err) return next(err);
req.logIn(user, function(err){
if (err) return next(err);
res.redirect('/');
});
});
}).catch(function(err){
return next(err);
});
};
Thanks for any opinion!
Implementing this yourself is pretty straightforward.
The pseudocode:
//A user registers
//User is stored along with a random token string and a variable set to false
//User is sent a verification email
//Verification email has a link with the random token and a unique ID for that user
//Link goes to a route that takes the token as a parameter
//Match the user and the random token
//If they match - change a variable to verified
The package I use to generage the random string is:
https://www.npmjs.com/package/randomstring
Local signup strategy
passport.use('local-signup', 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) {
// asynchronous
// User.findOne wont fire unless data is sent back
process.nextTick(function () {
// 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({'local.email': email}, function (err, user) {
// if there are any errors, return the error
if (err) {
return done(err);
}
// check to see if theres already a user with that email
if (user) {
console.log('that email exists');
return done(null, false, req.flash('signupMessage', email + ' is already in use. '));
} else {
User.findOne({'local.username': req.body.username}, function (err, user) {
if (user) {
console.log('That username exists');
return done(null, false, req.flash('signupMessage', 'That username is already taken.'));
}
if (req.body.password != req.body.confirm_password) {
console.log('Passwords do not match');
return done(null, false, req.flash('signupMessage', 'Your passwords do not match'));
}
else {
// create the user
var newUser = new User();
var permalink = req.body.username.toLowerCase().replace(' ', '').replace(/[^\w\s]/gi, '').trim();
var verification_token = randomstring.generate({
length: 64
});
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
newUser.local.permalink = permalink;
//Verified will get turned to true when they verify email address
newUser.local.verified = false;
newUser.local.verify_token = verification_token;
try {
newUser.save(function (err) {
if (err) {
throw err;
} else {
VerifyEmail.sendverification(email, verification_token, permalink);
return done(null, newUser);
}
});
} catch (err) {
}
}
});
}
});
});
}));
I use a combination of /permalink/random-token for the verification URL
The route should look like this:
app.get('/verify/:permaink/:token', function (req, res) {
var permalink = req.params.permaink;
var token = req.params.token;
User.findOne({'local.permalink': permalink}, function (err, user) {
if (user.local.verify_token == token) {
console.log('that token is correct! Verify the user');
User.findOneAndUpdate({'local.permalink': permalink}, {'local.verified': true}, function (err, resp) {
console.log('The user has been verified!');
});
res.redirect('/login');
} else {
console.log('The token is wrong! Reject the user. token should be: ' + user.local.verify_token);
}
});
});

passport js check for error in req

I am logging in a user with passport local:
// route to log in
app.post('/login', passport.authenticate('local-login'), function(req, res) {
res.send(req.user);
});
This will only work in the case of success, i.e. when req.user is defined. How do I check for an error in the request? Essentially I want to take the error that was pulled from my passport authentication code in the done() call and send that back to the client to display the proper error message:
passport.use('local-login', new LocalStrategy({
username : 'email',
password : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
console.log('logging in user: ' + email);
// 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
Landlord.findOne({ 'local.email' : email }, function(err, user) {
if (err) return done(err);
if (!user) return done(null, false, { message: 'Incorrect username.' });
if (!user.validPassword(password)) {
console.log('wrong pw');
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}));

Node.js Passport strategy login with either Email or Username

I am using Passport on my node.js app and I am currently using an Username to login.
On my user register page, I have allow the user to register their unique username and email.
I want a login page with "Sign in Using username/email:" ________
Where the script can detect if there is a "#" in the field and look up the email instead of username.
I have tried for a few hours with no avail.
here is my passport.js
var mongoose = require('mongoose')
var LocalStrategy = require('passport-local').Strategy
var User = mongoose.model('User');
module.exports = function(passport, config){
passport.serializeUser(function(user, done){
done(null, user.id);
})
passport.deserializeUser(function(id, done) {
User.findOne({ _id: id }, function (err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
}, function(username, password, done) {
User.isValidUserPassword(username, password, done);
}));
}
EDIT: below is the user.js as requested
var mongoose = require('mongoose');
var hash = require('../util/hash.js');
UserSchema = mongoose.Schema({
username: String,
email: String,
salt: String,
hash: String
})
UserSchema.statics.signup = function(username, email, password, done){
var User = this;
hash(password, function(err, salt, hash){
if(err) throw err;
// if (err) return done(err);
User.create({
username : username,
email: email,
salt : salt,
hash : hash
}, function(err, user){
if(err) throw err;
// if (err) return done(err);
done(null, user);
});
});
}
UserSchema.statics.isValidUserPassword = function(username, password, done) {
this.findOne({username : username}, function(err, user){
// if(err) throw err;
if(err) return done(err);
if(!user) return done(null, false, { message : 'Incorrect username.' });
hash(password, user.salt, function(err, hash){
if(err) return done(err);
if(hash == user.hash) return done(null, user);
done(null, false, {
message : 'Incorrect password'
});
});
});
};
var User = mongoose.model("User", UserSchema);
module.exports = User;
Ok, you should have something like this in your Mongoose model:
UserSchema.statics.isValidUserPassword = function(username, password, done) {
var criteria = (username.indexOf('#') === -1) ? {username: username} : {email: username};
this.findOne(criteria, function(err, user){
// All the same...
});
};
its a mongoose thing rather than a passport thing, and if you can try out this, using bredikhin's answer ^_^ :
var criteria = {$or: [{username: username}, {email: username}, {mobile: username}, {anything: username}]};
The key is to find a user through a query from mongodb!!!
In this way, you can have whatever query you want to find a user.
For sign ins with username OR email, you could also use passport-local-mongoose's findByUsername option modify the queryParameters
// add passport functions
// schema.plugin(passportLocalMongoose);
schema.plugin(passportLocalMongoose, {
// allow sign in with username OR email
findByUsername: function(model, queryParameters) {
// start
// // queryParameters => { '$or' : [ { 'username' : 'searchString' } ] }
// iterate through queryParameters
for( let param of queryParameters.$or ){
// if there is a username
if( typeof param == "object" && param.hasOwnProperty("username") ){
// add it as an email parameter
queryParameters.$or.push( { email : param.username } );
}
}
// expected outcome
// queryParameters => { '$or' : [ { 'username' : 'searchString' }, { 'email' : 'searchString' } ] }
return model.findOne(queryParameters);
}
});
its very simple you have to redefine the passport strategy your self, just like in the code below,
serialize with username
passport.serializeUser(function(user, done) {
done(null, user.username);});
deserialize with username
passport.deserializeUser(function(username, done) {
User.findOne({username:username},function(err, user){
done(err,user);
}); });
Passport Strategy
//passport strategy
passport.use(new LocalStrategy(function(username, password, done) {
console.log(username.includes("#"));
User.findOne((username.includes("#"))?{email:username}:{username:username}, function(err, user) {
if (err) {return done(err); }
if (!user) {console.log("i am ERROR"); return done(null, false, { message: 'Incorrect username.' });}
if (user.password===password) {return done(null, user); }
return done(null, false);
});
}
));
NOTE: Here user.password===password means that the password in database is stored as plaintext.. And you have to insert password manually to database such as password : req.body.password also you have to apply encryption and decryption user self before adding or in comparing.
Or you just make a middle-ware to handle the situation like so:
const User = require("../models/user");
var middlewareObj = {};
middlewareObj.checkUsername = function (req, res, next) {
if (req.body.username.indexOf('#') !== -1) {
User.findOne({ email: req.body.username }, (err, foundUser) => {
if (err || !foundUser) {
req.flash('error', 'Please check your username or password');
return res.redirect('/login');
} else {
req.body.username = foundUser.username;
next();
}
});
} else {
next();
}
}
module.exports = middlewareObj;
And simply add it to the login route:
app.post('/login', middleware.checkUsername, function (req, res, next) {
//Login logic goes here
}

Categories

Resources