I am using Passport.js with Express - for some reason the req.session.passport.user value is not present, when it should be, as you can see:
here is my passport configuration:
passport.serializeUser(function (user, done) {
console.log(colors.bgRed('serialize user called, user:', user));
done(null, user._id);
});
passport.deserializeUser(function (id, done) {
console.log(colors.bgRed('deserialize user called, user_id:', id));
UserModel.getNewUser().findById(id, function (err, user) {
console.log(colors.bgRed('deserialize find user called, user:', user));
done(err, user);
});
});
passport.use('local', new LocalStrategy(function (username, password, done) {
UserModel.getNewUser().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.'
});
}
// req.user = user;
return done(null, user);
});
}));
does anyone know what could be going wrong? My only guess is that I might need to deserialize using the Session object, instead of the user id, but that I believe is incorrect. I do know for a fact that deserializeUser and serializeUser are not being called at all.
It was hard to find an answer for this online, so I think one should go here.
The problem was because instead of using this:
app.post('/login', passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login' }));
...I decided to use this style of call with Passport:
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
Originally my code was like so:
passport.authenticate('local', function (err, user, info) {
if (err) {
return next(err);
}
if (!user) {
registerUser(req,res,next);
}
else{
res.locals.loggedInUser = user._doc;
res.json({alreadyRegistered: !justRegistered,msg:user._doc});
}
})(req, res, next);
but then I updated it with req.logIn like so:
passport.authenticate('local', function (err, user, info) {
if (err) {
return next(err);
}
if (!user) {
console.log('no account found, so we will register user as expected...');
registerUser(req,res,next);
}
else{
req.logIn(user, function (err) { //this is crucial
if(err){
return next(err);
}
res.locals.loggedInUser = user._doc;
res.json({alreadyRegistered: !justRegistered,msg:user._doc});
});
}
})(req, res, next);
and now it works - should have followed the docs better. I recommend that Jared Hansson puts in "this is crucial" in the docs where I have just labeled it above. Otherwise, yes I should have followed the docs more closely.
Related
I'm getting that error, and besides asking where it is on my code I would like to know if there is a better way to log errors in order to get to know where it is being generated on my code, since I can't figure it out by reading the error log. Here are the code and the error. Thanks! StackOF is making me add more details, I don't know what to write
/////// app.js
//Function one : setting up the LocalStrategy
passport.use(
new LocalStrategy((username, password, done) => {
User.findOne({ username: username }, (err, user) => {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, { message: "Incorrect username" });
}
if (bcrypt.compare(password, user.password, (err, res) => {
if (res) {
// passwords match! log user in
return done(null, user)
} else {
// passwords do not match!
return done(null, false, { message: "Incorrect password" })
}
})) {
return done(null, false, { message: "Incorrect password" });
}
return done(null, user);
});
})
);
//Functions two and three: Sessions and serialization
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
//Route for logging in
app.post(
"/log-in",
passport.authenticate("local", {
successRedirect: "/",
failureRedirect: "/"
})
);
app.use(function(req, res, next) {
res.locals.currentUser = req.user;
next();
});
app.get("/", (req, res) => {
res.render("index", { user: req.user });
});
app.get("/sign-up", (req, res) => res.render("sign-up-form"));
app.post("/sign-up", (req, res, next) => {
bcrypt.hash(req.body.password, 10, (err, hashedPassword) => {
// if err, do something
if (err) {
return next(err);
}
// otherwise, store hashedPassword in DB
// eslint-disable-next-line no-unused-vars
const user = new User({
username: req.body.username,
password: hashedPassword
}).save(err => {
if (err) {
return next(err);
}
return res.redirect("/");
});
});
});
app.get("/log-out", (req, res) => {
req.logout();
res.redirect("/");
});
app.listen(3000, () => console.log("app listening on port 3000!"));
And the error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:558:11)
at ServerResponse.header (C:\Users\Atom\Desktop\passportJSOdin\node_modules\express\lib\response.js:771:10)
at ServerResponse.location (C:\Users\Atom\Desktop\passportJSOdin\node_modules\express\lib\response.js:888:15)
at ServerResponse.redirect (C:\Users\Atom\Desktop\passportJSOdin\node_modules\express\lib\response.js:926:18)
at complete (C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\middleware\authenticate.js:266:26)
at C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\middleware\authenticate.js:275:15
at pass (C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\authenticator.js:431:14)
at Authenticator.transformAuthInfo (C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\authenticator.js:453:5)
at C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\middleware\authenticate.js:272:22
at C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\http\request.js:52:7
at C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\sessionmanager.js:26:5
at pass (C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\authenticator.js:277:43)
at serialized (C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\authenticator.js:286:7)
at C:\Users\Atom\Desktop\passportJSOdin\app.js:63:5
at pass (C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\authenticator.js:294:9)
at Authenticator.serializeUser (C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\authenticator.js:299:5)
There is a glaring issue in the logic of your code here:
if (bcrypt.compare(password, user.password, (err, res) => {
if (res) {
return done(null, user)
} else {
return done(null, false, { message: "Incorrect password" })
}
})) {
return done(null, false, { message: "Incorrect password" });
}
return done(null, user);
bcrypt.compare() is an asynchronous function, which means it does not return the result it produces to its calling code, but instead passes it to its callback function once available. So the continuation of the code - ie everything that should happen once the result is available - should be wrapped in its callback function.
You got this right. But then you are duplicating the logic of what happens in this callback function in the calling code based on the returned value of bcrypt.compare() - which is irrelevant.
The end result is that the done() function will end up being called twice.
You should just do:
bcrypt.compare(password, user.password, (err, res) => {
if (res) {
return done(null, user)
} else {
return done(null, false, { message: "Incorrect password" })
}
})
As far as logging errors, a stack trace is as good as it gets to help pinpoint the issue.
This is my passport Login Handler.
Now I want to send JSON Data under every condition so that API can access the response and on behalf of this display anything on frontend.
//Login Handler
passport.use('local-user', new LocalStrategy(
function(username, password, done) {
User.getUserByUsername(username, function(err, user){
if(err) {
console.log('error')
logger.log('error', 'Error Generates on User.getUserByUsername Query on users.js file');
throw err;
}
if(!user){
//res.send('unknown user');
console.log('Unknown User');
return done(null, false, {message: 'Unknown User'});
}
User.comparePassword(password, user.password, function(err, isMatch){
if(err) {
logger.log('error', 'Error Generates on User.comparePassword Query on users.js file');
throw err;
}
if(isMatch){
return done(null, user);
}else{
return done(null, false, {message: 'Invalid Credential, please check carefully...!'})
}
});
});
}
));
Anyone have any idea for this? Thanks in advance
The local strategy will pass the user or error with done(), then you receive that with a callback and pack it with res.json()
Here is my implementation. May help?
passport.use(
new LocalStrategy(
{
usernameField: "email"
},
function(username, password, done) {
User.findOne({ email: username }, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {
email: "Email not found"
});
}
if (!user.validPassword(password)) {
return done(null, false, {
password: "Password is wrong"
});
}
return done(null, user);
});
}
)
);
router.post("/login", function(req, res) {
passport.authenticate("local", function(err, user, info) {
if (err) {
res.status(404).json(err);
return;
}
if (user) {
const token = user.generateJwt();
res.status(200);
res.json({
userInfo: user,
token: token
});
} else {
res.status(401).json(info);
}
})(req, res);
});
I' learning Node.s and Express, and I'm following this example from https://github.com/EvanHahn/Express.js-in-Action-code/tree/master/Chapter_08/learn-about-me. Can you explain the following question?
In the "/login" post route, if I need to access the request and response objects, how should I do it?
What is the "done" function inside LocalStrategy(), and how I know what parameter to pass? Looks like it take 3 arguments, and the 2nd argument is the user object, and the 3rd argument is the message. What is the 1st argument?
How do the username and password get passed from the "/login" post route into LocalStrategy? What magic is behind the scene?
router.post("/login", passport.authenticate("login", {
successRedirect: "/",
failureRedirect: "/login",
failureFlash: true
}));
passport.use("login", 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: "No user has that username!" });
}
user.checkPassword(password, function(err, isMatch) {
if (err) { return done(err); }
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: "Invalid password." });
}
});
});
}));
The answer for question #1 and #2 is at http://passportjs.org/docs
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
3 is at the same docs page.
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'passwd'
},
function(username, password, done) {
// ...
}
));
How do I run two or more passport strategies sequentially, meaning if one strategy comes up empty then run another one?
I tried doing this:
app.post('/', function (req,res,next){
passport.authenticate('strategy1', function (err, result1) {
if (err) { return next(err); }
if (!result1) {
passport.authenticate('strategy2', function (err,result2){
if (err) { return next(err); }
if(!result2){
return res.redirect('/');}
req.login(result2, function (err){
if(err){return next(err)}
res.render('result2');
})
});
}
req.login(result1, function (err){
if (err){return next(err)}
console.log('admin login found');
res.render('result');
});
})(req, res, next);
});
But am getting this error:
Error: Failed to serialize user into session
I have implemented:
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
I suspect the user was serialised in the first passport.authentication call and then it tried to serialise it again with the second one, and what I need to do is deserialise it again before running the second strategy.
Appreciate the help!
I ended up sticking to just one strategy but added logic to allow it to check several collections, as recommended in this answer.
passport.use('local', new LocalStrategy({
passReqToCallback : true
}, function(req, username, password, done) {
process.nextTick(function() {
collection1.findOne({'username': username}, function(err, collectionresult) {
if (err) {
return done(err);
}
if (!collectionresult) {
collection2.findOne({'username': username}, function(err, collection2result){
if (err) {
return done(err);
}
if (!collection2result) {
return done(null, false,req.flash('adminmessage','Invalid username or password'));
}
if (!collection2.validPassword(password)) {
return done(null, false,req.flash('adminmessage','Invalid username or password'));
}
console.log('local strategy has authenticated employee username and password! Returning employee');
return done(null, employee);
})
}
if (collection2result){
if (collection2result.password!=password) {
return done(null, false, req.flash('adminmessage','Invalid username or password' ));
}
else{
console.log('Local strategy has found an admin. Returning admin');
return done(null, collection2result)
}
}
});
}
);
}));
I am using express 4 with passport js to handle authentication of users.
The front end is Angular JS.
I am basically facing two problems:
1- sign in is lengthy, it takes up to 15 seconds to sign in.
2- once logged in, if i restart the node js server and refresh the page I am back to the signin page even though a cookie is set in the browser.
This is what i have in the backend
passport.serializeUser(function (user, done) {
done(null, user._id);
});
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
user.password = undefined;
done(err, user);
});
});
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: messages['116']});
}
user.comparePassword(password, function (err, isMatch) {
if (err) {
return done(err);
}
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: messages['116']});
}
});
});
}));
app.use(favicon());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(express.static(path.join(__dirname, 'public')));
app.use(cookieParser('a very hard to guess string'));
app.use(session({
secret: 'a very hard to guess string'
}
));
app.use(passport.initialize());
app.use(passport.session());
The following is the login route:
router.post('/signin', function (req, res, next) {
var result = _.cloneDeep(SwissArmyKnife.resultObjSkel);
var username = req.body.username;
var password = req.body.password;
if (_.isEmpty(username)) {
result.error.reasons.push(SwissArmyKnife.messages['118']);
}
if (_.isEmpty(password)) {
result.error.reasons.push(SwissArmyKnife.messages['119']);
}
if (!_.isEmpty(result.error.reasons)) {
return res.json(200, result);
}
passport.authenticate('local', function (err, user, info) {
if (err) {
return next(err);
}
if (user) {
req.login(user, function (err) {
if (err) {
return next(err);
}
result.result = true;
return res.json(200, result);
})
}
if (info) {
result.error.reasons.push(info.message);
return res.json(200, result);
}
})(req, res, next);
});
when debugging the above code i notice a huge delay when the code reaches
passport.authenticate('local', function (err, user, info) {
what could be the problem???
what am i doing wrong?
Thanks in advance.
The reason you're seeing the sign-in page after restarting your server is because you are using the memory store for your sessions. As you've noticed this only has so much usefulness and you should use some persistent store instead (e.g. redis).