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!
I had a similar issue with Passport
and failed login responses. I was building an API, and wanted all responses to be returned as JSON
. Passport responds to an invalid password with status: 401
and body: Unauthorized
. That's just a text string in the body, not JSON, so it broke my client which expected all JSON.
As it turns out, there is a way to make Passport just return the error to the framework instead of trying to send a response itself.
The answer is to set failWithError
in the options passed to authenticate: https://github.com/jaredhanson/passport/issues/126#issuecomment-32333163
From jaredhanson's comment in the issue:
app.post('/login', passport.authenticate('local', { failWithError: true }), function(req, res, next) { // handle success if (req.xhr) { return res.json({ id: req.user.id }); } return res.redirect('/'); }, function(err, req, res, next) { // handle error if (req.xhr) { return res.json(err); } return res.redirect('/login'); } );
This will invoke the error handler after Passport calls next(err)
. For my app, I wrote a generic error handler specific to my use case of just providing a JSON error:
// Middleware error handler for json response function handleError(err,req,res,next){ var output = { error: { name: err.name, message: err.message, text: err.toString() } }; var statusCode = err.status || 500; res.status(statusCode).json(output); }
Then I used it for all api routes:
var api = express.Router(); ... //set up some routes here, attached to api ... // error handling middleware last api.use( [ handleError ] );
I didn't find the failWithError
option in the documentation. I stumbled upon it while tracing through the code in the debugger.
Also, before I figured this out, I tried the "custom callback" mentioned in the @Kevin_Dente answer, but it didn't work for me. I'm not sure if that was for an older version of Passport or if I was just doing it wrong.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With