17

I just looking for solution which makes verification email with token for my local autentification in passport.js Is there some plugin or component for node which can make me verification easyer? Or I have to do it myself?

My controller

exports.postSignup = function(req, res, next) {
  req.assert('email', 'Email is not valid').isEmail();
  req.assert('password', 'Password must be at least 4 characters long').len(4);
  req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);

  var errors = req.validationErrors();

  if (errors) {
    req.flash('errors', errors);
    return res.redirect('/signup');
  }

  var user = User.build({
    email: req.body.email,
    password: req.body.password,
  });

  User
  .find({ where: { email: req.body.email } })
  .then(function(existingUser){
    if (existingUser) {
      req.flash('errors', { msg: 'Account with that email address already exists.' });
      return res.redirect('/signup');
    }

    user
    .save()
    .complete(function(err){
      if (err) return next(err);
      req.logIn(user, function(err){
        if (err) return next(err);
        res.redirect('/');
      });
    });
  }).catch(function(err){
    return next(err);
  });
};

Thanks for any opinion!

Makromat
  • 1,540
  • 5
  • 26
  • 47
  • Take a look at the Drywall project -- this includes the functionality you mention, and is built with passport: https://github.com/jedireza/drywall – Ben Mar 04 '15 at 11:57
  • The [hackathon boilerplate](https://github.com/sahat/hackathon-starter) has some password reset logic built-in. You can use it in new projects, or as a guide if you write your own. – ki9 Aug 21 '16 at 23:50

1 Answers1

27

Implementing this yourself is pretty straightforward.

The pseudocode:

//A user registers
    //User is stored along with a random token string and a variable set to false
    //User is sent a verification email

//Verification email has a link with the random token and a unique ID for that user

//Link goes to a route that takes the token as a parameter


//Match the user and the random token

//If they match - change a variable to verified

The package I use to generage the random string is: https://www.npmjs.com/package/randomstring

Local signup strategy

passport.use('local-signup', new LocalStrategy({
        // by default, local strategy uses username and password, we will override with email
        usernameField: 'email',
        passwordField: 'password',
        passReqToCallback: true // allows us to pass back the entire request to the callback
    },

    function (req, email, password, done) {
        // asynchronous
        // User.findOne wont fire unless data is sent back
        process.nextTick(function () {
            // find a user whose email is the same as the forms email
            // we are checking to see if the user trying to login already exists
            User.findOne({'local.email': email}, function (err, user) {
                // if there are any errors, return the error
                if (err) {
                    return done(err);
                }

                // check to see if theres already a user with that email
                if (user) {
                    console.log('that email exists');
                    return done(null, false, req.flash('signupMessage', email + ' is already in use. '));

                } else {
                    User.findOne({'local.username': req.body.username}, function (err, user) {
                        if (user) {
                            console.log('That username exists');
                            return done(null, false, req.flash('signupMessage', 'That username is already taken.'));
                        }

                        if (req.body.password != req.body.confirm_password) {
                            console.log('Passwords do not match');
                            return done(null, false, req.flash('signupMessage', 'Your passwords do not match'));
                        }

                        else {
                            // create the user
                            var newUser = new User();

                            var permalink = req.body.username.toLowerCase().replace(' ', '').replace(/[^\w\s]/gi, '').trim();

                            var verification_token = randomstring.generate({
                                length: 64
                            });


                            newUser.local.email = email;

                            newUser.local.password = newUser.generateHash(password);

                            newUser.local.permalink = permalink;

                            //Verified will get turned to true when they verify email address
                            newUser.local.verified = false;
                            newUser.local.verify_token = verification_token;

                            try {
                                newUser.save(function (err) {
                                    if (err) {

                                        throw err;
                                    } else {
                                        VerifyEmail.sendverification(email, verification_token, permalink);
                                        return done(null, newUser);
                                    }
                                });
                            } catch (err) {

                            }
                        }
                    });
                }
            });
        });
    }));

I use a combination of /permalink/random-token for the verification URL

The route should look like this:

app.get('/verify/:permaink/:token', function (req, res) {
        var permalink = req.params.permaink;
        var token = req.params.token;

        User.findOne({'local.permalink': permalink}, function (err, user) {
            if (user.local.verify_token == token) {
                console.log('that token is correct! Verify the user');

                User.findOneAndUpdate({'local.permalink': permalink}, {'local.verified': true}, function (err, resp) {
                    console.log('The user has been verified!');
                });

                res.redirect('/login');
            } else {
                console.log('The token is wrong! Reject the user. token should be: ' + user.local.verify_token);
            }
        });
    });
Tom
  • 2,543
  • 3
  • 21
  • 42
  • As great as this answer is, I believe the original question was what is the proper way to handle this through passport, if any? I'm currently opting for the approach you mentioned but would be curious to know if this logic can be run through passport instead. – GroomedGorilla Mar 12 '18 at 11:33
  • Thanks for your code Tom. I see that you have this 'local.' in front of the field names. I think that you implemented other login strategies as well as i Intend to. Could you please detail your mysql/sequelize/user.js implementation? (About the answer @GroomedGorilla, I don't think that passport do the e-mail confirmation. ) – LeoPucciBr Oct 05 '18 at 14:51
  • Nevermind found similiar content (https://medium.com/@bmshamsnahid/node-js-authentication-using-passport-js-78386be1f518) tks tks – LeoPucciBr Oct 05 '18 at 18:34
  • Nowadays you can generate the random string with `crypto.randomBytes(64).toString('hex')`. – Ciro Santilli OurBigBook.com Dec 10 '21 at 10:50