passport with express: serialization issue with custom authentication method - node.js

#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.

Related

Why Callback not working with mongoose findOne method?

Using Expressjs and mongoose in a node application. I am trying to implement passportjs authentication. When I call the new LocalStrategy and passing the username and a callback function to get the username if exist, the callback function is not executing. To verify the I printed a console message is callback but even message is not showing. Why this is happening and how I can resolve it?
This is the router( index.js)
router.post('/login', (req, res, next) => {
console.log("Login POst called")
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: 'true'
})(req, res, next);
});
passport.use(new LocalStrategy(
(username, password, done)=>{
console.log("Local Strategy called")
User.getUserByUsername(username, (err, user)=>{
console.log("this message should display in callback"); // this message is not displaying
if(err) throw err;
if(!user){
return done(null, false, {message: "No user found"});
}
User.comparePassword(password, user.password, (err, isMatch)=>{
if(err) throw err;
if(isMatch){
return done(null, user);
}
else{
return done(null, false, {message: "Wrong Password"});
}
});
});
}
));
this is the model (User.js)
module.exports.getUserByUsername = (username, callback)=>{
console.log("GetUsername called")
const query = { username: username }
console.log(query);
User.findOne(query).exec(callback);
}
module.exports.getUserById = (id, callback)=>{
console.log("GetId called")
User.findId(id).exec(callback);
}

I keep getting 401 Unauthorized whenever I try to login an existing user with JWT, Passport, and Express

Whenever I try to login a user through a post request in Postman, I keep getting this error:
Here is the localStrategy I made for checking if a user's email and password can be verified:
passport.use(new LocalStrategy({usernameField: 'email', passwordField: 'password'}, function(email, password, done){
User.findOne({email: email}, (err, user) => {
if (err) return done(err);
if (!user){
return done(null, false, {message: "User is not registered"});
}
else {
const realPassword = String(user.password);
bcrypt.compare(password, realPassword, (err, result) => {
if (err) throw err;
if (result){
console.log('result is...' + result);
done(null, user)
}
else {
console.log('result is...' + result);
return done(null, false, {message: 'Invalid Password'});
}
}
});
}));
And here is the post request method in my router file:
router.post('/login', passport.authenticate('local', {session: false}), (req, res, next) => {
function generateUserToken(user){
return jwt.sign({sub: user._id, creationDate: user.creationDate}, config.secretKey);
}
if (err) throw err;
res.send({token: generateUserToken(user)})
});
UPDATE: I changed some of my code now to address issues raised. However, now instead of getting unauthorized, I keep getting a 404 not found error
In my case there where dismatch in findOne(), User schema has local.email, so I needed to search for findOne({'local.email': email}, ...).
Seems the value of doesMatch is always false.
I believe you're missing the bcrypt.compare doesn't return anything.
It is asynchronous, so, you can't get the result using the return statement.
You have to put everygint inside of the callback function from bcrypt.compare
bcrypt.compare(password, realPassword, (err, result) => {
if (err) throw err;
// NOW YOU HAVE THE VALUE FORM THE COMPARISON
if (result){
done(null, user)
}
else {
return done(null, false, {message: 'Invalid Password'});
}
});
Let me know if it works.
Hope it helps you.

Passport.js positive result

I am attempting to authenticate a user in a Node app using Sequelize and Passport. I am able to hit my database but can't seem to get a positive result. Basically, I have a simple frontend form that accepts a username and password (using the respective names "username" and "password") and then the following Passport definition:
passport.use(new localStrategy(
{usernameField: 'email'},
function(req, email, password, done) {
models.TeacherX.findOne({ email: email }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.verifyPassword(password)) { return done(null, false); }
return done(null, user);
});
}
));
Then I call it with:
router.post('/login',
passport.authenticate('local', { failureRedirect: '/login?msg=failure'}),
function(req, res) {
res.redirect("/?msg=positive");
});
Since you are passing the request to the verify callback, you need to set the property passReqToCallback to true or remove the req param from the verify callback. Try this:
passport.use(new localStrategy(
{passReqToCallback: true ,
usernameField: 'email'},
function(req, email, password, done) {
models.TeacherX.findOne({ email: email }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.verifyPassword(password)) { return done(null, false); }
return done(null, user);
});
}
));

Passportjs custom callback - 'Incorrect password' message

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.

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.

Resources