I am using PassportJS with Local Strategy and passport-local-mongoose. Here's my login script:
// Configure Passport (server.js)
// ---------------------------------------------------------------
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
// ---------------------------------------------------------------
.
// POST to /login (authenticate.js)
// ---------------------------------------------------------------
router.post('/login', (req, res) => {
// server-side validation
const errors = {
username: Validator.validateusername(req.body.username),
password: Validator.validatepassword(req.body.password),
};
if (!isEmpty(errors)) return res.send(JSON.stringify({ error: errors }));
passport.authenticate('local')(req, res, () => {
// If logged in, we should have user info to send back
if (req.user) {
const userdata = JSON.stringify(req.user);
const token = jwt.sign({
username: req.user.username,
firstName: req.user.firstName,
lastName: req.user.lastName,
email: req.user.email,
img: req.user.img,
}, process.env.JWT_SECRET);
res.cookie('token', token);
return res.send(userdata);
}
// Otherwise return an error
return res.send(JSON.stringify({ error: 'There was an error logging in' }));
});
});
This works fine except when there's a login error. If the login fails for whatever reason (401 or 500), shouldn't this script return the There was an error logging in
message? Instead, it just returns a 401 Unauthorized
?
The schema this authentication looks up is:
const { mongoose } = require('../config/dbconfig');
const Schema = mongoose.Schema;
const passportLocalMongoose = require('passport-local-mongoose');
const User = new Schema({
username: {
type: String,
lowercase: true,
required: true,
unique: true,
},
password: {
type: String,
select: false,
required: true,
},
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
email: {
type: String,
lowercase: true,
required: true,
unique: true,
},
img: {
type: String,
},
}, { timestamps: true });
User.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', User);
You are calling the strategy a little wrong. You should use it as a middleware, or use the custom callback way. Yours is a kind of mix between these - you call the strategy like it was a middleware but provide your own next
middleware function. Since authenticate is called as middleware but no failureRedirect
option is provided, Passport will return 401 by default. The next
function is called when successfully authenticated, and in your case it is not the next middleware but a callback function.
To use the custom callback you should write the route handler like this:
app.post('/login', (req, res, next) => {
// ...
passport.authenticate('local', (err, user, info) => {
if (err) { return next(err); }
if (user) {
// ...
req.login(user, (err) => {
if (err) { return next(err); }
return res.send(userdata);
});
} else {
return res.status(401).send({ error: 'There was an error logging in' });
}
})(req, res, next);
});