4
votes

I'm using node-bcrypt along with PostgreSQL (via Sequelizejs to hash and save passwords.

The user's password is hashed in a beforeValidate hook like so:

beforeValidate: function(user, model, cb) {
        bcrypt.hash(user.password, 10, function(err, hash) {
          if ( err ) { throw err; }
          user.password = hash;
          cb(null, user);
        });
      }

The column on the User model in which the hash is stored is defined as:

password: { type: DataTypes.STRING, allowNull: false }

When a user is logging in (I'm using Passport for authentication), the function looks like this:

passport.use(new LocalStrategy(function(username, password, done) {
    models.User.find({ username: username }).then(function(retrievedUser) {
      if ( !_.isEmpty(retrievedUser) ) {
        retrievedUser.verifyPassword(password, function(err, result) {
          if ( err || !result ) {
            return done(null, false, { message: 'Incorrect password.' });
          } else {
            return done(null, retrievedUser);
          }
        });
      } else {
        return done(null, false, { message: 'User could not be found at that username.' });
      }
    }).catch(function(err) {
      return done(err);
    });
  }));

Which retrieves the user correctly.

And the comparison, defined here on the User model:

instanceMethods: {
      verifyPassword: function(password, cb) {
        bcrypt.compare(password, this.password, cb);
      }
    }

The verification passes just fine if the password only contains letters and/or symbols. However, any passwords with numbers never pass the comparison. Any ideas?

2
Are you sure the hash in the db is correct? Maybe you accidentally lowercased the password before creating the hash? - loganfsmyth
@loganfsmyth just double-checked to make sure this wasn't the problem, and it looks like uppercase letters actually work fine. The problem is just with symbols and numbers (edited original post) - Jakemmarsh
Which module are you using? There are a number of bcrypt implementations. - Aaron Dufour
@AaronDufour I'm using the implementation linked in my original post, here. I believe it's the official node implementation? - Jakemmarsh
I do not think this is bcrypt related. I assume that your passwords for letters only are matching because of automatic type conversion in JS. It would be helpful if you could provide exact input you are giving to hash call in both cases. - Damian Krawczyk

2 Answers

3
votes

In your beforeValidate function you are passing in 10 as the salt you need to generate your salt, change that function to this

beforeValidate: function(user, model, cb) {
    var salt = bcrypt.genSalt(10);
    bcrypt.hash(user.password, salt, function(err, hash) {
      if ( err ) { throw err; }
      user.password = hash;
      cb(null, user);
    });
  }
2
votes

Well, embarrassingly (and as some people suspected), this was completely unrelated to Bcrypt.

In my authentication function, I was attempting to fetch the appropriate user with the following:

models.User.find({ username: username })

However, the query should have been specified in a where field, like so:

models.User.find({
   where: { username: username }
})

This was causing the authentication method to always be comparing to the password for the first user in the database (returned by default when no where field is specified). Thus, it SEEMED to work because the first user's letters-only password always worked but no following passwords worked (in reality, even a letters-only password would have failed for other users).