web-dev-qa-db-fra.com

Réinitialisation du mot de passe dans NodeJS

J'ai configuré la mise à jour du mot de passe d'un utilisateur à l'aide de NodeJS/Passport. J'ai suivi ce guide formidable: http://sahatyalkabov.com/how-to-implement-password-reset-in-nodejs/ .

99% de cela fonctionne. J'ai dû le modifier un peu pour inclure des fonctionnalités de bande. J'ai peur cependant d'avoir une erreur critique quelque part et je ne la trouve pas. L’utilisateur est actuellement en mesure de suivre le processus d’envoi d’un courrier électronique, de saisir un nouveau mot de passe et d’être connecté. Un autre courrier électronique suit, indiquant que son mot de passe a été mis à jour avec succès. Tout est parfait. Toutefois. Pour certaines raisons. Le nouveau mot de passe n'est pas enregistré. l'utilisateur ne peut se connecter qu'avec son ancien mot de passe. J'ai tout essayé pour résoudre ce problème.

Plusieurs autres programmeurs se sont penchés sur la question et aucun d’entre eux n’a pu comprendre comment, dans le monde, cela ne fonctionnait pas. 

On pense actuellement que la session ne se termine peut-être pas correctement, mais nous avons essayé de la détruire et cela ne fonctionnait toujours pas. 

Toute aide grandement appréciée. 

Mise en place complète:

Modèle d'utilisateur:

var UserSchema = new mongoose.Schema({
    username:   { type: String, required: true, unique: true },
    password:  String,
    datapoint:  String,
    email:  { type: String, required: true, unique: true },
    resetPasswordToken: String,
    resetPasswordExpires: Date
});


UserSchema.pre('save', function(next) {
  var user = this;
  var SALT_FACTOR = 5;

  if (!user.isModified('password')) return next();

  bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
    if (err) return next(err);

    bcrypt.hash(user.password, salt, null, function(err, hash) {
      if (err) return next(err);
      user.password = hash;
      next();
    });
  });
});

Enregistrer un nouveau compte(Ceci contient également des informations de bande non liées mais pourrait causer le problème.)}

var newUser = new User({username: req.body.username, email: req.body.email, datapoint: req.body.datapoint});
      User.register(newUser, req.body.password, function(err, user){


            if(err){
            console.log('Looks like there was an error:' + ' ' + err)
             res.redirect('/login')

          } else {
          passport.authenticate("local")(req, res, function(){

      var user = new User({
      username: req.body.username,
      email: req.body.email,
      password: req.body.password

      })


console.log('creating new account')
console.log('prepping charge')
var token = req.body.stripeToken; // Using Express
var charge = stripe.charges.create({
  amount: 749,
  currency: "usd",
  description: "Example charge",
  source: token,

}, function(err, charge) {
  // asynchronously called
  console.log('charged')
});
            res.redirect('/jobquiz')
             console.log(req.body.datapoint)
             console.log(req.body.email)

          });
          }
      });
});

Configurer la publication du mot de passe oublié

app.post('/forgot', function(req, res, next) {
  async.waterfall([
    function(done) {
      crypto.randomBytes(20, function(err, buf) {
        var token = buf.toString('hex');
        done(err, token);
      });
    },
    function(token, done) {
      User.findOne({ email: req.body.email }, function(err, user) {
        if (!user) {
        //   console.log('error', 'No account with that email address exists.');
        req.flash('error', 'No account with that email address exists.');
          return res.redirect('/forgot');
        }
console.log('step 1')
        user.resetPasswordToken = token;
        user.resetPasswordExpires = Date.now() + 3600000; // 1 hour

        user.save(function(err) {
          done(err, token, user);
        });
      });
    },
    function(token, user, done) {
        console.log('step 2')


      var smtpTrans = nodemailer.createTransport({
         service: 'Gmail', 
         auth: {
          user: 'myemail',
          pass: 'mypassword'
        }
      });
      var mailOptions = {

        to: user.email,
        from: 'myemail',
        subject: 'Node.js Password Reset',
        text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
          'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
          'http://' + req.headers.Host + '/reset/' + token + '\n\n' +
          'If you did not request this, please ignore this email and your password will remain unchanged.\n'

      };
      console.log('step 3')

        smtpTrans.sendMail(mailOptions, function(err) {
        req.flash('success', 'An e-mail has been sent to ' + user.email + ' with further instructions.');
        console.log('sent')
        res.redirect('/forgot');
});
}
  ], function(err) {
    console.log('this err' + ' ' + err)
    res.redirect('/');
  });
});

app.get('/forgot', function(req, res) {
  res.render('forgot', {
    User: req.user
  });
});

Configurer le changement de mot de passe post

app.get('/reset/:token', function(req, res) {
  User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
      console.log(user);
    if (!user) {
      req.flash('error', 'Password reset token is invalid or has expired.');
      return res.redirect('/forgot');
    }
    res.render('reset', {
     User: req.user
    });
  });
});




app.post('/reset/:token', function(req, res) {
  async.waterfall([
    function(done) {
      User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user, next) {
        if (!user) {
          req.flash('error', 'Password reset token is invalid or has expired.');
          return res.redirect('back');
        }


        user.password = req.body.password;
        user.resetPasswordToken = undefined;
        user.resetPasswordExpires = undefined;
        console.log('password' + user.password  + 'and the user is' + user)

user.save(function(err) {
  if (err) {
      console.log('here')
       return res.redirect('back');
  } else { 
      console.log('here2')
    req.logIn(user, function(err) {
      done(err, user);
    });

  }
        });
      });
    },





    function(user, done) {
        // console.log('got this far 4')
      var smtpTrans = nodemailer.createTransport({
        service: 'Gmail',
        auth: {
          user: 'myemail',
          pass: 'mypass'
        }
      });
      var mailOptions = {
        to: user.email,
        from: 'myemail',
        subject: 'Your password has been changed',
        text: 'Hello,\n\n' +
          ' - This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
      };
      smtpTrans.sendMail(mailOptions, function(err) {
        // req.flash('success', 'Success! Your password has been changed.');
        done(err);
      });
    }
  ], function(err) {
    res.redirect('/');
  });
});
13
AndrewLeonardi

Je n'ai pas (ou n'ai pas) trouvé de problème avec votre code, mais j'ai une suggestion pour tracer le bogue.

Ce bloc de code est risqué. Vous pouvez accidentellement mettre à jour le champ du mot de passe et déclencher le processus de mot de passe rehash.

UserSchema.pre('save', function(next) {
   var user = this;
   var SALT_FACTOR = 12; // 12 or more for better security

   if (!user.isModified('password')) return next();

   console.log(user.password) // Check accident password update

   bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
      if (err) return next(err);

      bcrypt.hash(user.password, salt, null, function(err, hash) {
         if (err) return next(err);
         user.password = hash;
         next();
      });
   });
});

Placez un console.log juste après la if (!user.isModified('password')) pour vérifier la mise à jour inattendue du mot de passe. Maintenant, essayez de nouveau d'oublier le mot de passe et de voir s'il y a un bogue là-dedans.

* TD; LR Séparez le mot de passe de mise à jour dans une nouvelle méthode au lieu de le mettre dans la pré-sauvegarde car vous pourriez accidentellement mettre à jour un nouveau mot de passe avec d'autres champs.

* Mise à jour: Merci #imns pour avoir suggéré un meilleur numéro SALT_FACTOR.

6
Anh Cao

Je pense que le problème pourrait être dans la fonction de hachage. J'ai essayé de dupliquer votre code en une expérience plus simple mais similaire sur mon ordinateur.

Comme indiqué dans la documentation bcrypt ici https://www.npmjs.com/package/bcrypt#to-hash-a-password

La fonction de hachage ne prend que 3 arguments, vous en envoyez 4. Le troisième argument dans votre cas est null.

Voici du code pour illustrer le problème et, espérons-le, la solution

Dans le rappel de salage

bcrypt.hash(user.password, salt, null, function(err, hash) {
  if (err) return next(err);
  user.password = hash;
  next();
});

Mais changez le troisième argument pour qu'il soit la fonction de rappel. 

bcrypt.hash(user.password, salt, function(err, hash) {
  if (err) return next(err);
  user.password = hash;
  next();
});
5
mertje

J'ai eu le même problème, il suffit de supprimer le paramètre null de cette ligne:

bcrypt.hash (user.password, salt, null, function (err, hash) {

1
user9348495

J'ai déjà utilisé ce code dans mon projet actuel et il fonctionne bien. J'ai constaté une petite erreur dans votre code dans la fonction UserSchema.pre('save', function(next). quand vous hachez le mot de passe avec bcrypt.hash, alors il a fallu quatre arguments mais il n'y a que trois arguments dans mon code, comme 

schema.pre('save', function(next) {
    var user = this;
    var SALT_FACTOR = 5;

    if(!user.isModified('password')){
        return next();
    }

    bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
        if(err){
            return next(err);
        }
        bcrypt.hash(user.password, salt, function(err, hash) {
            if(err){
                return next(err);
            }
            user.password = hash;
            next();
        });
    });
});

Le troisième argument doit être une fonction de rappel voir le document pour bcrypt

0
Gaurav