Passportjs custom callback - 'Incorrect password' message - node.js

For a regular authentication, the 'Incorrect password' message is available via failureFlash
app.post('/login',
passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login',
failureFlash: true })
);
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);
});
}
));
But if I use a custom callback, how can I access that 'Incorrect password' message and show it to the user ? Because the custom callback only seems to check for (!user). I need custom callback as I am doing login via ajax and I cannot have redirects.
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.status(401).send({"ok": false}); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return return res.send({"ok": true});
});
})(req, res, next);
});

The message is in the info parameter in your custom callback handler making it very easy to access and send to the user.
On a side not, I wouldn't specify if the username or the password was the cause of the login failure. Using that kind of response it is very easy to test which usernames exist, and then just focus on the password.

Related

Node passport flow with Express

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) {
// ...
}
));

Passport.js multiple local strategies and req.user

The client for my web-app wants to totally separate regular users and admins, hence I'm trying to implement two local strategies for passport.js:
passport.use('local', new LocalStrategy({
usernameField: 'email'
}, function(email, password, done) {
User.findOne({ email: email }, function(err, user) {
if (err) return done(err);
if (!user) return done(null, false, { message: 'Wrong email or password.' });
if (!user.validPassword(password)) return done(null, false, { message: 'Wrong email or password.' });
done(null, user);
});
}));
passport.use('admin', new LocalStrategy({
usernameField: 'email'
}, function(email, password, done) {
Admin.findOne({ email: email }, function(err, admin) {
if (err) return done(err);
if (!admin) return done(null, false, { message: 'Wrong email or password.' });
if (!admin.validPassword(password)) return done(null, false, { message: 'Wrong email or password.' });
done(null, admin);
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
Admin.findById(id, function(err, admin) {
if (err) return done(err);
if (admin) return done(null, admin);
User.findById(id, function(err, user) {
done(err, user);
});
});
});
And then in my API admin router:
router.post('/', function(req, res, next) {
if (typeof req.body.email === 'undefined') return res.json({ success: false, message: 'Email not supplied.' });
if (!validator.isEmail(req.body.email)) return res.json({ success: false, message: 'Wrong email format.' });
if (typeof req.body.password === 'undefined') return res.json({ success: false, message: 'Password not supplied.' });
if (req.body.password.length === 0) return res.json({ success: false, message: 'Password not supplied.' });
passport.authenticate('admin', function(err, admin, info) {
if (!admin) return res.json({ success: false, message: info.message });
req.logIn(admin, function(err) {
if (err) return res.json({ success: false, message: 'Server error.', reason: err });
console.log('user:');
console.log(req.user); // both users and admins go here
console.log('admin:');
console.log(req.admin); // undefined
res.json({ success: true });
});
})(req, res, next);
});
In fact, I was following the answer here: https://stackoverflow.com/a/21898892/1830420, and Matthew Payne gets two session variables: req.user and req.sponsor. In my case, even if an admin authenticates, it gets written to req.user. Now, trying to implement my client's desire of totally separating users and admins, I want to get req.user and req.admin, but every time it gets written to req.user. I thought changing LocalStrategy name would help, but passport.js seems to ignore both the strategy name and model name.
Any help is very much appreciated.
P.S. I know I can have everyone in User model and write some middleware to protect certain routes based on roles, but then again, unfortunately, I don't get to choose here.
Unfortunately you can't do that with Passport. You will need to create express middleware to handle admin authentication.
And like Hya answered passport dont are good for authorization ...
You need to use middlewares for set admin context and admin roles:
// add this middlewares after your routes...
// set admin context and others things like admin templates
app.use('/admin/*', function adminContext(req, res, next) {
// set admin context
req.isAdmin = true;
next();
});
// then get roles for authenticated user in your passport stategy:
app.use(function getUserRoles(req, res, next) {
req.userRoleNames = [];
if (req.isAuthenticated()) {
req.userRoleNames.push('authenticated');
} else {
req.userRoleNames.push('unAuthenticated');
return next(); // skip role load if dont are authenticated
}
// get user roles, you may get roles from DB ...
// and if are admin add its role
req.userRoleNames.push('administrator');
next();
});
// and check roles in admin page
app.get('/admin/admin-page/', function (req, res, next) {
if (req.userRoleNames.indexOf('administrator') == -1) return res.status(403).send('forbidden');
console.log('roles:', req.userRoleNames);
res.status(200).send('Hey you are the administrator!');
});
See we.js core for see one advanced roles and permissions implementation: https://github.com/wejs/we-core

how to specify multiple strategy in passport

I am using passport local for authenticating the application .I understood to perform login
Now i wanted to implement registration for the same
I understood we can provide a name to distinguish both local strategy
but i don't know how to invoke them
Login:
app.post('/login',
passport.authenticate('local', {
successRedirect: '/loginSuccess',
failureRedirect: '/loginFailure',
failureFlash: true
}));
passport.use('login',new LocalStrategy(
function(username, password, done) {
process.nextTick(function () {
});
UserDetails.findOne({'username':username},
function(err, user) {
if (!user) { return done(null, false , { message: 'Incorrect username.' }); }
if (err) { return done(err); }
if (user.password != password) { return done(null, false , { message: 'Incorrect password.' }); }
return done(null, user);
});
}
));
app.post('/register',
passport.authenticate('local', {
successRedirect: '/loginSuccess',
failureRedirect: '/loginFailure',
failureFlash: true
}));
passport.use('register',new LocalStrategy(
function(username, password, done) {
process.nextTick(function () {
});
UserDetails.findOne({'username':username},
function(err, user) {
if (user) { return done(null, false , { message: 'EmailId Already exits' }); }
if (err) { return done(err); }
return done(null, user);
});
}
));
where should i configure login and register to pick up corresponding strategy accordingly i am a new bee please revert if someone not understood my question
Updating as cant put code in comment:
Please go through with this article https://scotch.io/tutorials/easy-node-authentication-setup-and-local
and let us know if you face any issue.
You can define it in your end points ..see below.. here /login is your end point where you will receive your requests and in function do your coding for checking user, etc and send res.json with response to user.
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
});

Passport.js - req.session.passport.user is not showing up

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.

passport with express: serialization issue with custom authentication method

#set up passport
LocalStrategy = require('passport-local').Strategy
passport.use(new LocalStrategy({usernameField: 'email'}, (email, password, done) ->
User.findOne({ email: email }, (err, user) ->
console.log("findone")
if err?
console.log "error"
return done(err)
if not user
console.log("incorrect username")
return done(null, false, { message: 'Incorrect username.' })
if password isnt user.password
console.log "NOT"
return done(null, false, {message: "Incorrect password" })
console.log "BUT YESS"
done(null,user)
)
))
passport.serializeUser((user, done) ->
done(null, user._id)
)
passport.deserializeUser((id, done) ->
User.findById(id, (err, user) ->
done(err, user)
)
)
I am using the LocalStrategy example as found at http://passportjs.org/guide/username-password/
Now, my problem is that if I enter a wrong password, or an incorrect username, I get an error Error: failed to serialize user into session.
In fact, in my serializeUser function, user is false. This probably is because the code literally returns false as the second parameter above:
return done(null, false, {message: "Incorrect password" })
But this is from the docs! So what am I doing wrong? Why do I have a serialization problem when parameters are incorrect? When parameters are ok (username and password correct), I can login without problems
EDIT: Upon comment by #robertklep here is how I use the authentication function. Indeed, because I need to route differenly depending on role, I use the passport custom method (this detail added to title of question):
app.post '/login', (req, res, next) ->
passport.authenticate('local', (err, user, info) ->
return next(err) if err?
return res.redirect('/login', { message: req.flash('Access denied')}) if not user?
req.logIn user, (err) ->
if err?
console.log "err! " + err
res.redirect("/", { message: req.flash(err)})
return
if user.role is "admin" or user.role is "auditor"
res.redirect("/dashboard")
else
res.redirect("/start")
)(req, res, next)
passport.use(new LocalStrategy( function(username, password, done) {
user.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
//check for incorrect username
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
//incorrect password for the selected username
if (user.password != password) {
return done(null, false, { message: 'Invalid password' });
}
return done(null, user);
});
}
));
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user,info) {
if (err) {
//console.log(info);
return next(err); }
if (!user) {
console.log(info);
return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err);
}
return res.redirect('https://www.google.co.in' );
});
})(req, res, next);
});
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(login, done) {
user.findById(login._id, function(err, user) {
done(err, user);
});
});
app.post '/login', (req, res, next) ->
passport.authenticate('local', (err, user, info) ->
console.log "authenticate callback"
if err?
console.log "err in authenticate callback"
return next(err)
if not user
console.log "User NOT in auth callback"
req.flash("Accesso no otorgado", info.message)
return res.redirect('/login')
I rewrote a bit the authenticate function.
Instead of if not user?, the solution is to use if not user (no question mark!), as the "user" returned is just a boolean set to false when things went wrong.

Resources