I've been searching stackoverflow for reasons why my node.js (express) app hangs on some posts with passport.js.
I've been looking at theese two questions:
More Passport.js woes - hangs on form submission
Passport (local) is hanging on Authentication
My code for creating a user works fine, and looks like this:
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 in the req from our route (lets us check if a user is logged in or not)
},
function(req, email, password, done) {
if (email)
email = email.toLowerCase(); // Use lower-case e-mails to avoid case-sensitive e-mail matching
// asynchronous
process.nextTick(function() {
// if the user is not already logged in:
if (!req.user) {
User.findOne({
where: {
local_email: email
}
}).then(function(user) {
if (user){
return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
}else{
// create the user
User
.build({
local_email: email,
local_password: generateHash(password),
id: 1 //Normal activated user
})
.save()
.then(newUser => {
//Our newly crated user
return done(null, newUser);
})
.catch(error =>{
//Woops something went wrong
return done(error);
})
//var newUser = new User();
//newUser.localemail = email;
//newUser.localpassword = generateHash(password);
//User.create(newUser).then(function(newUser, created) {
// if (!newUser) {
// return done(err);
// }
// if (newUser) {
// return done(null, newUser);
// }
//});
}
});
// if the user is logged in but has no local account...
} else if ( !req.user.local.email ) {
// ...presumably they're trying to connect a local account
// BUT let's check if the email used to connect a local account is being used by another user
User.findOne({
where: {
localemail: email
}
}).then(function(user) {
if (err)
return done(err);
if (user){
return done(null, false, req.flash('loginMessage', 'That email is already taken.'));
// Using 'loginMessage instead of signupMessage because it's used by /connect/local'
} else {
// create the user
var newUser = new User();
newUser.local.email = email;
newUser.local.password = generateHash(password);
User.create(newUser).then(function(newUser, created) {
if (!newUser) {
return done(err);
}
if (newUser) {
return done(null, newUser);
}
});
}
});
} 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);
}
});
}));
In my routs file the post to that function looks like this:
// process the signup form
app.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/profile', // redirect to the secure profile section
failureRedirect : '/signup', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
My code for signing in however dosen't work that well. The function in passport.js looks like this:
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 in the req from our route (lets us check if a user is logged in or not)
},
function(req, email, password, done) {
if (email)
email = email.toLowerCase(); // Use lower-case e-mails to avoid case-sensitive e-mail matching
// asynchronous
process.nextTick(function() {
User.findOne({
where: {
local_email: email
}
}).then(function(user){
console.log("TEST: "+user);
// if no user is found, return the message
if (!user){
console.log("no user with the following email: " +email);
return done(null, false, req.flash('loginMessage', 'No user found.'));
}else if(!validPassword(password,user.local_password)){
console.log("wrong password");
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.'));
// all is well, return user
}else{
return done(null, user);
return;
}
}).catch(function(err){
console.log("woops der skete en fejl: "+err);
console.log("email is: "+email);
console.log("password is: "+password);
return done(err);
});
});
}));
The corresponding route looks like this:
// process the login form
app.post('/login', passport.authenticate('local-login', {
successRedirect : '/', // redirect to the secure profile section
failureRedirect : '/login', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
The above code hangs when the form is being submitted. If I changes the code to:
app.post('/login', function(req, res, next) {
passport.authenticate('local-login', function(err, user, info) {
if (err) { return next(err); }
if (!user) {
return res.render('/login', {
pageTitle: 'Sign in',
form: req.body
});
}
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/');
});
})(req, res, next);
});
I receive an 500.
Any ideas why this isen't working?
Updated with console output:
Executing (default): SELECT `id`, `role`, `local_email`, `local_password`, `facebook_id`, `facebook_token`, `facebook_email`, `facebook_name`, `twitter_id`, `twitter_token`, `twitter_displayname`, `twitter_username`, `google_id`, `google_token`, `google_email`, `google_name`, `createdAt`, `updatedAt` FROM `users` AS `users` WHERE `users`.`local_email` = 'test#local' LIMIT 1;
TEST: [object SequelizeInstance:users]
POST /login 302 87.021 ms - 46
Executing (default): SELECT `id`, `role`, `local_email`, `local_password`, `facebook_id`, `facebook_token`, `facebook_email`, `facebook_name`, `twitter_id`, `twitter_token`, `twitter_displayname`, `twitter_username`, `google_id`, `google_token`, `google_email`, `google_name`, `createdAt`, `updatedAt` FROM `users` AS `users` WHERE `users`.`id` = 1;
Updated with changed passport.deserializeUser
passport.deserializeUser(function(id, done) {
/*User.findById(id, function(err, user) {
done(err, user);
});*/
User.findById(id).then(user => {
if (!user){
done(null);
}else{
done(user);
}
})
});
Your passport.deserializeUser isn't correct. As with all Node.js callbacks, the first argument is reserved for errors, and should be null (or another falsy value) if there aren't any errors. In your case, you're passing user as first argument, which makes Passport think there was an error.
Try this:
passport.deserializeUser(function(id, done) {
User.findById(id)
.then(user => done(null, user))
.catch(done);
});
Related
I'm trying to add a conditional to passport.js that also checks if the account is confirmed or not by inserting an 'if statement' to the bottom of the code after user email and password have been checked.
I tried several ways to add the if statement to the last part of the code in the 'LocalStrategy' function and tried reworking the whole thing with elif, and else statements. I'm getting 'Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client' and other errors as it is below.
I'm afraid I can't provide all the code for you to reproduce this.
Unless you have a mock sign page and mongodb running with passport.js sign in and an account with a true/false like user.confirmed is for my account.
const LocalStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
// Load User model
const User = require('../models/User');
module.exports = function(passport) {
passport.use(
new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
// Match user
User.findOne({
email: email
}).then(user => {
if (!user) {
return done(null, false, { message: 'That email is not registered' });
}
// Match password
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) {
console.log(err);
res.sendStatus(500);
return;
} if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Password incorrect' });
}
});
// LINES I'M TRYING TO ADD HERE BELOW
if (!user.confirmed) {
return done(null, false, { message: 'This account is not confirmed' });
// LINES I'M TRYING TO ADD HERE ABOVE
}
});
})
);
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
};
In my setup the above code is in the config file as passport.js and used by a js file in the 'routes' folder that logs the user in with
router.post('/login', (req, res, next) => {
passport.authenticate('local', {
successRedirect: '/dashboard',
failureRedirect: '/users/login',
failureFlash: true
})(req, res, next);
The field 'confirmed' is either 'true' or 'false'. Read with !user.confirmed. (The process of confirmed or !confirmed is handled with a link sent to the user's email. This is outside the scope of my issue but sharing in case this helps frame the problem.)
I would like for passport.js to check the account with the email exists, that the password is correct, THEN let the user login ONLY if the account is confirmed 'true' and post the message that the account is not confirmed and deny entry if it is not confirmed.
I can't seem to add the confirmed check without errors and losing the other checks for things. With this current setup it tells you if the email is NOT registered. It tells you if the user is NOT confirmed, but it crashes if you enter the wrong password with a registered email.
So unless I am missing something
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) {
console.log(err);
res.sendStatus(500);
return;
}
if (isMatch && user.confirmed) {
return done(null, user);
} else if (isMatch) {
return done(null, false, { message: 'This account is not confirmed' });
} else {
return done(null, false, { message: 'Password incorrect' });
}
});
Other permutations are available.
I am building an API using the MEAN stack and am working on the login and signup functions.
And I want to respond with a json string as follows
{
success: 0,
message: ""
}
success:1 for successful login and 0 otherwise.
My authenticate.js is as follows
module.exports = function(passport){
//log in
router.post('/login', passport.authenticate('login', {
//success
//failure
}));
//sign up
router.post('/signup', passport.authenticate('signup', {
//success
//failure
}));
//log out
router.get('/signout', function(req, res) {
req.logout();
res.redirect('/');
});
return router;
}
My passport.init.js middleware is as follows
var mongoose = require('mongoose');
var User = mongoose.model('User');
var LocalStrategy = require('passport-local').Strategy;
var bCrypt = require('bcrypt-nodejs');
module.exports = function(passport){
// Passport needs to be able to serialize and deserialize users to support persistent login sessions
passport.serializeUser(function(user, done) {
console.log('serializing user:',user.username);
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
console.log('deserializing user:',user.username);
done(err, user);
});
});
passport.use('login', new LocalStrategy({
passReqToCallback : true
},
function(req, username, password, done) {
// check in mongo if a user with username exists or not
User.findOne({ 'username' : username },
function(err, user) {
// In case of any error, return using the done method
if (err)
return done(err);
// Username does not exist, log the error and redirect back
if (!user){
console.log('User Not Found with username '+username);
return done(null, false);
}
// User exists but wrong password, log the error
if (!isValidPassword(user, password)){
console.log('Invalid Password');
return done(null, false); // redirect back to login page
}
// User and password both match, return user from done method
// which will be treated like success
return done(null, user);
}
);
}
));
passport.use('signup', new LocalStrategy({
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, username, password, done) {
// find a user in mongo with provided username
User.findOne({ 'username' : username }, function(err, user) {
// In case of any error, return using the done method
if (err){
console.log('Error in SignUp: '+err);
return done(err);
}
// already exists
if (user) {
console.log('User already exists with username: '+username);
return done(null, false);
} else {
// if there is no user, create the user
var newUser = new User();
// set the user's local credentials
newUser.username = username;
newUser.password = createHash(password);
// save the user
newUser.save(function(err) {
if (err){
console.log('Error in Saving user: '+err);
throw err;
}
console.log(newUser.username + ' Registration succesful');
return done(null, newUser);
});
}
});
})
);
var isValidPassword = function(user, password){
return bCrypt.compareSync(password, user.password);
};
// Generates hash using bCrypt
var createHash = function(password){
return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
};
};
Please help me out in passing the JSON string accordingly
Using Express, all you have to do is to execute res.json inside any controller, passing it any JavaScript object. Express will automatically convert it to JSON and return it to the user.
return res.json({ success: 0, message: '' }
I have a user table in which i have an Admin account and some other normal users accounts.
I want to do all activities which for a particular user. It should act like that activity done by same user.
Can somebody tell me how can i switch to another account from Admin account
without login to that account.
Currently i use passport authentication.(passport-local)
Here is my code
app.get('/secure/group/login', function(req,res,next) {
passport.authenticate('local',function(err,user,info) {
console.log("error is "+err);
req.logIn('tessAccount',function(err) {
console.log("Weer" +err);
});
console.log("dd");
})(req,res,next);
});
});
and passport code
var LocalStrategy = require('passport-local').Strategy;
module.exports = function(passport) {
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user.token);
});
passport.use(new BearerStrategy(
function(token, done) {
user.tokenExist(token, function(err, user) {
if (err) {
return done(err);
}
else {
return done(null, user, { scope: 'all' });
}
});
}
));
// used to deserialize the user
passport.deserializeUser(function(accessToken, done) {
user.getUserByAccessToken(accessToken, function(err, dbUser) {
if (err) {
done(err);
} else {
done(null, dbUser[0]);
}
});
});
// 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-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) {
user.emailExists(email, function(err, exists) {
if (err)
return done(err);
else if (exists) {
return done(null, false, {
message: req.flash('loginMessage')
});
} else {
req.userDetails.status = 0;
req.userDetails.token = md5.digest_s(req.userDetails.email + new Date());
req.userDetails.userImage = config.user.image;
user.register(req.userDetails, function(err, newUser) {
if (err)
return done(err);
else {
/*Get user Shared article if exist start*/
getSharedArticlesOfnewlyuserIfExist(email, newUser.insertId);
/*Get user Shared article if exist end*/
req.userDetails.id = newUser.insertId;
return done(err, req.userDetails);
}
});
}
});
}));
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.matchPassword({
email: email,
password: password
}, function(err, newUser) {
// 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 (newUser.length > 0) {
var user = {
id: newUser[0].user_id,
email: newUser[0].email_id,
token: newUser[0].user_access_token
};
return done(null, user);
} else {
return done(null, false, {
message: 'Incorrect username or password.'
}); // req.flash is the way to set flashdata using connect-flash
}
});
}));
User.findOne({ username: 'Myusername' }, function(err, user) {
req.logIn(user, function(err){});
});
This worked for me to log into an account without using password, it switches from my admin account to an user account.
Actually I'm using passport.js and mongoose
I am trying to redirect a user once he signs up or if the username is already present in the db, the function works well but I can not redirect and I am totally confused on what to do next.
Here is my passport file
var LocalStrategy = require('passport-local').Strategy;
var User = require('./models/user');
module.exports = function(passport) {
passport.serializeUser(function(user, done) {
done(null, user);
});
// used to deserialize the user
passport.deserializeUser(function(user, done) {
done(null, user);
});
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({ 'email' : email }, function(err, user) {
// if there are any errors, return the error
if (err) {
return done(err);
}
if(user) {
if(user.validPassword(password)) {
return done(null, user);
} else {
return done(null, true, req.flash('signupMessage', 'That email is already in DB.'));
}
} else {
var newUser = new User();
newUser.email = email;
newUser.password = newUser.generateHash(password);
newUser.save(function(err) {
if (err)
return done(err);
return done(null, newUser);
});
}
});
});
}));
}
and my route
app.post('/signup',passport.authenticate('local-signup', {
successRedirect : '/timeslot', // redirect to the secure profile section
failureRedirect : '/', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
I have also tried doing
app.post('/signup',function(req, res, next) {
passport.authenticate('local-signup', function(err, user, info) {
if (user) {
res.redirect('/timeslot');
}
else res.redirect('/');
})(req, res, next);
});
but it is not working at all. I am desperate to fix this but dont know how.
I met the same problems with passport redirects. So my code began work just as
app.get("/auth/facebook/callback", passport.authenticate("facebook", { failureRedirect: config.loginPage }), function(req,res) {
res.redirect(config.redirectAfterLogin);
});
I'm new to node express and passport so I'm not sure what I'm missing. I'm trying to use a custom callback as all I want is a json response from my registration strategy. Currently I keep getting the following error "Missing credentials". I've been at it for a while now and I'm stuck.
Here is my controller:
app.post('/services/authentication/registration', function(req, res, next) {
console.log('before authentication')
passport.authenticate('local-registration', function(err, user, info) {
console.log('authentication callback');
if (err) { return res.send({'status':'err','message':err.message});}
if (!user) { return res.send({'status':'err','message':info.message});}
if (user!=false) { return res.send({'message':'registration successful'});}
})(req, res, next);
},
function(err, req, res, next) {
return res.send({'status':'err','message':err.message});
});
And my passport strategy:
passport.use('local-registration', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
console.log('credentials passed to passport' + email + '' + password)
// 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) {
return done(null, false, {message: 'User already exists'});
} else {
// if there is no user with that email
// create the user
var newUser = new User();
// set the user's local credentials
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
// save the user
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
});
});
}));
I dont think you should send "req" as a parameter to the authentication function. take a look at this example from the passport docs:
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.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));