4

I'm trying to implement a passport-local strategy using passport.js

I would like it so that when a user tries to sign up with an email that is already taken, it flashes a message 'That email is already taken'.

I've added the line failureFlash: true but how do I get access to the flash message so I can actually get it to appear on the screen?

I'm quite new to this all so any help would be awesome.

passport.js:

const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const User = mongoose.model('users');

passport.serializeUser((user, done) => {
  console.log('serialize:', user.id);
  done(null, user.id);
});

passport.deserializeUser((id, done) => {
  User.findById(id).then(user => {
    console.log('deserialize:', user);
    done(null, user);
  });
});

// Local signup strategy
passport.use('local-signup', new LocalStrategy(
  {
    usernameField: 'email',
    passwordField: 'password',
    passReqToCallback: true
  },
  (req, email, password, done) => {
    User.findOne({ 'local.email': email }, (err, user) => {
      if (err) { return done(err); }

      if (user) {
        console.log('user error:', user);
        return done(null, false, req.flash('signUpMessage', 'That email is already taken.'));
      } else {
        new User({
          'local.email': email,
          'local.password': password
        })
        .save()
        .then(user => {
          done(null, user)
        })
        .catch(err => {
          console.log('error:', err);
          done(err);
        });
      }
    });

  }
));

authRoutes.js:

const passport = require('passport');

module.exports = app => {
  app.post(
    '/api/signup',
    passport.authenticate('local-signup', {
      failureFlash: true
    }),
    (req, res) => {
      console.log('request:', req);
      console.log('flash msg:', req.flash('signUpMessage'));
      res.send(req.user);
    }
  );

  app.get('/api/current_user', (req, res) => {
    console.log('current_user:', req.user);
    res.send(req.user);
  });

  app.get('/api/logout', (req, res) => {
    req.logout();
    res.redirect('/');
  });
};

Github: https://github.com/drhectapus/voting-app

VC.One
  • 14,790
  • 4
  • 25
  • 57
doctopus
  • 5,349
  • 8
  • 53
  • 105

4 Answers4

2

I think you could do

console.log('flash msg:', req.flash('error'));

or

failureFlash: req.flash('error');
Jesus Mendoza
  • 323
  • 2
  • 17
  • 1
    `failueFlash: true` should automatically stuff the request with an appropriate flash message, right? Do we need to write it ourselves? – fsljfke Jul 21 '20 at 18:44
2

In the done callback, you can pass on the error message like this - return done(null, false, { message: 'User exists!' }) and make sure you have failureFlash set to true in your router e.g.

router.get('/google/callback', passport.authenticate('google',
{
        failureRedirect: '/',
        failureFlash: true,
        session: false,
}),

You'll have the error message available in your views as messages.error

CodeWalker
  • 2,281
  • 4
  • 23
  • 50
1

I know this is like damn late, but here is what works for me

First, you need to require and set up flash:

var flash = require('connect-flash');

Then, use it through express:

app.use(flash());

Finally, display the message by using ejs as your view engine

app.use('view engine', 'ejs');

app.get('/login', function(req,res) {
res.render('login', {message: req.flash('message')});
});

Display in your template:

<%= message %>

This is not entirely the code (duh!) but it will give you some idea on fixing yours! Cheers!

iamlanphan
  • 11
  • 2
0

In your example,

return done(null, false, req.flash('signUpMessage', 'That email is already taken.'));

should be replaced with

return done(null, false, {type:'signUpMessage', message: 'That email is already taken'});

It’s passport itself that will call req.flash() for you.

Assuming that you already have setup connect-flash and you verified that you can already show flashes other than passport flashes then you have 3 options:

failureFlash: true

In this case when you call done(null, false, {type: 'info', message: 'my message'}) passport will take the type and message and use it to call req.flash(type, message), if you don't specify a type then passport will default to the error flash.

In the example below, there are two possible messages that can be flashed username not found! and incorrect password, note that we don't pass a type in the done(null, false, {...} so all flashes will "inherit" the error flash type:

 passport.use('login', new LocalStrategy(function(username, password, done) {
        // we need to call done(null, user) if authenticated
        // or done(null, false, {message: "invalid username/password"}) if can't authenticate
        User.findOne({username: username}, function(err, user) {
            if (err) { done(err); }
            if (!user) { 
                return done(null, false, {message: "username not found!"});
            }
            user.checkPassword(password, function(err, result) {
                if (err) { return done(err);}
                if (result) { 
                    return done(null, user);
                }
                return (null, false, {message: 'incorrect password'});
            } )
        })
    }));

failureFlash: "some generic message"

This will ignore whatever {type: xxx, message: yyy} that you pass in the done(null, false, {...} and it's equivalent to use {type: "error", "message": "some generic message"} in all done(null, false, ...)

failureFlash: { "type": "info" }

This overwrite the flash type of all messages to info. So regardless of what type is specified by the call to done(null, false, {type: "myflashtype", message: "my message"} that will be effectively overwritten to req.flash('info', 'my message')

I don't think this is particularly well documented in passport.js docs but you can find the details by looking at this section of passport source code

RubenLaguna
  • 21,435
  • 13
  • 113
  • 151