How to show custom error messages using passport and express

You are not saying what output you want in your front end, but I'm guessing you want to have data to be the message that you set in your LocalStrategy.

Here is one way to do that:

authRouter.post('/signup', function(req, res, next) {
  passport.authenticate('local-register', function(err, user, info) {
    if (err) { return next(err); }
    if (!user) { 
        res.status(401);
        res.end(info.message);
        return;
    }
    createSendToken(req.user, res);
  })(req, res, next);
});

You can take advantage of passReqToCallback: true in passport.

With this option enabled, req will be passed as the first argument to the verify callback.

Also by taking advantage of flash messages.

Here is basic example of how you use it,

// your strategy
passport.use(new LocalStrategy({
    passReqToCallback: true
  },
    (req, username, password, done) => {
      User.findOne({ username: username }, (err, user) => {
        if (err) done(err)
        if (!user) {
          console.log('user does not exists')
          return done(null, false, req.flash('message', 'User does not exist' ))
        } else {
          console.log('user exists')
          return done(null, user, req.flash('message', 'User exist'))
        }
      })
    }
  ))

// your GET login
router.get("/login", (req, res) => {
  var message = req.flash('message')
  res.render("pages/login", { message })
})

As mentioned in other answers you can manually check the info parameter and then return your own message based on that:

// ❌ MESSY ❌
authRouter.post('/signup', function(req, res, next) {
    passport.authenticate('local-register', function(err, user, info) {
        if(info.name === 'UserExistsError' ) {
            return done(null, false, {
                message: "Email already exists"
            });
        } else if (info.name === 'IncorrectUsernameError') {
          return done(null, false, {
              message: "Email does not exist"
          });
        } else if(....

But A MUCH cleaner way is to just specify custom error messages when you create the Account Mongoose model:

var Account = new Schema({
    ...
});

var options = {
    errorMessages: {
        UserExistsError: 'Email already exists',
        IncorrectUsernameError: 'Email does not exist',
        IncorrectPasswordError: ...
    }
};

Account.plugin(passportLocalMongoose, options);

Then in your signup route you can simply return the info.message to the user.

// ✨ CLEAN ✨
authRouter.post('/signup', function(req, res, next) {
    passport.authenticate('local-register', function(err, user, info) {
        return done(null, false, {
            message: info.message
        });
    });
});