Sending back a JSON response when failing Passport.js authentication

authentication, javascript, node.js, passport-local, passport.js

Solution

I believe the callback function that your 'authenticate' static calls (called 'callback' in your code) accepts a 3rd parameter - "info" - which your code can provide. Then, instead of passing in the { failureRedirect: ...} object, pass in a function which takes 3 arguments - err, user, and info. The "info" you provided in your authenticate method will be passed to this callback.

Passport calls this scenario "custom callback". See the docs here: http://passportjs.org/guide/authenticate/

Problem

I'm using `Node.js` as a backend API server for an iPhone client. I'm using `Passport.js` to authenticate with a `local strategy`. The relevant code is below: ``` // This is in user.js, my user model UserSchema.static('authenticate', function(username, password, callback) { this.findOne({ username: username }, function(err, user) { if (err){ console.log('findOne error occurred'); return callback(err); } if (!user){ return callback(null, false); } user.verifyPassword(password, function(err, passwordCorrect){ if (err){ console.log('verifyPassword error occurred'); return callback(err); } if (!passwordCorrect){ console.log('Wrong password'); return callback(err, false); } console.log('User Found, returning user'); return callback(null, user); }); }); }); ``` and ``` // This is in app.js app.get('/loginfail', function(req, res){ res.json(403, {message: 'Invalid username/password'}); }); app.post('/login', passport.authenticate('local', { failureRedirect: '/loginfail', failureFlash: false }), function(req, res) { res.redirect('/'); }); ``` Right now, I have managed to redirect a failed login to /loginfail, where I send back some JSON to the iPhone client. However, this doesn't have enough granularity. I want to be able to send back the appropriate errors to the iPhone client, such as: "No user found" or "Password is wrong". With my existing code, I don't see how this can be accomplished. I tried to follow the examples for a custom callback on the passport.js site, but I just can't get it to work due to lack of node understanding. How could I modify my code so that I'd be able to send back a res.json with an appropriate error code/message? I am trying something like this now: ``` // In app.js app.post('/login', function(req, res, next) { passport.authenticate('local', function(err, user, info) { if (err) { return next(err) } if (!user) { console.log(info); // *** Display message without using flash option // re-render the login form with a message return res.redirect('/login'); } console.log('got user'); return res.json(200, {user_id: user._id}); })(req, res, next); }); // In user.js UserSchema.static('authenticate', function(username, password, callback) { this.findOne({ username: username }, function(err, user) { if (err){ console.log('findOne error occurred'); return callback(err); } if (!user){ return callback(null, false); } user.verifyPassword(password, function(err, passwordCorrect){ if (err){ return callback(err); } if (!passwordCorrect){ return callback(err, false, {message: 'bad password'}); } console.log('User Found, returning user'); return callback(null, user); }); }); }); ``` But back when I try to console.log(info), it just says undefined. I don't know how to get this custom callback working...Any help would be appreciated!

Original source