title: Bcrypt-NodeJS compare() returns false whatever the password
body: <p>I know that question has already been asked a few times (like <a href="https://stackoverflow.com/questions/34360898/bcrypt-compare-always-returns-false">here</a>, <a href="https://stackoverflow.com/questions/40558748/bcrypt-node-js-returns-false-on-compare">here</a> or <a href="https://stackoverflow.com/questions/30494856/bcrypt-nodejs-compare-method-returns-false-everytime">there</a>, or even on <a href="https://github.com/kelektiv/node.bcrypt.js/issues/466" rel="noreferrer">Github</a>, but none of the answers actually worked for me...</p>
  
  <p>I am trying to develop authentication for a NodeJS app using Mongoose and Passport, and using Bcrypt-NodeJS to hash the users' passwords.</p>
  
  <p>Everything was working without any problem before I decided to refactor the User schema and to use the async methods of bcrypt. The hashing still works while creating a new user but I am now unable to verify a password against its hash stored in MongoDB.</p>
  
  <h1>What do I know?</h1>
  
  <ol>
  <li><code>bcrypt.compare()</code> always returns <code>false</code> whatever the password is correct or not, and whatever the password (I tried several strings).</li>
  <li>The password is only hashed once (so the hash is not re-hashed) on user's creation.</li>
  <li>The password and the hash given to the compare method are the right ones, in the right order.</li>
  <li>The password and the hash are of type "String".</li>
  <li>The hash isn't truncated when stored in the database (60 characters long string).</li>
  <li>The hash fetched in the database is the same as the one stored on user's creation.</li>
  </ol>
  
  <h1>Code</h1>
  
  <h2>User schema</h2>
  
  <p>Some fields have been stripped to keep it clear, but I kept the relevant parts.</p>
  
  <pre><code>var userSchema = mongoose.Schema({
  
      // Local authentication
      password: {
          hash: {
              type: String,
              select: false
          },
          modified: {
              type: Date,
              default: Date.now
          }
      },
  
      // User data
      profile: {
          email: {
              type: String,
              required: true,
              unique: true
          }
      },
  
      // Dates
      lastSignedIn: {
          type: Date,
          default: Date.now
      }
  });
  </code></pre>
  
  <h2>Password hashing</h2>
  
  <pre><code>userSchema.statics.hashPassword = function(password, callback) {
      bcrypt.hash(password, bcrypt.genSaltSync(12), null, function(err, hash) {
          if (err) return callback(err);
          callback(null, hash);
      });
  }
  </code></pre>
  
  <h2>Password comparison</h2>
  
  <pre><code>userSchema.methods.comparePassword = function(password, callback) {
      // Here, `password` is the string entered in the login form
      // and `this.password.hash` is the hash stored in the database
      // No problem so far
      bcrypt.compare(password, this.password.hash, function(err, match) {
          // Here, `err == null` and `match == false` whatever the password
          if (err) return callback(err);
          callback(null, match);
      });
  }
  </code></pre>
  
  <h2>User authentication</h2>
  
  <pre><code>userSchema.statics.authenticate = function(email, password, callback) {
      this.findOne({ 'profile.email': email })
          .select('+password.hash')
          .exec(function(err, user) {
              if (err) return callback(err);
              if (!user) return callback(null, false);
  
              user.comparePassword(password, function(err, match) {
                  // Here, `err == null` and `match == false`
                  if (err) return callback(err);
                  if (!match) return callback(null, false);
  
                  // Update the user
                  user.lastSignedIn = Date.now();
                  user.save(function(err) {
                      if (err) return callback(err);
                      user.password.hash = undefined;
                      callback(null, user);
                  });
              });
          });
  }
  </code></pre>
  
  <p>It may be a "simple" mistake I made but I wasn't able to find anything wrong in a few hours... May you have any idea to make that method work, I would be glad to read it.</p>
  
  <p>Thank you guys.</p>
  
  <h1>Edit:</h1>
  
  <p>When running this bit of code, match is actually equal to <code>true</code>. So I know my methods are correct. I suspect this has something to do with the storage of the hash in the database, but I really have no idea of what can cause this error to occur.</p>
  
  <pre><code>var pwd = 'TestingPwd01!';
  mongoose.model('User').hashPassword(pwd, function(err, hash) {
      console.log('Password: ' + pwd);
      console.log('Hash: ' + hash);
      user.password.hash = hash;
      user.comparePassword(pwd, function(err, match) {
          console.log('Match: ' + match);
      });
  });
  </code></pre>
  
  <h1>Edit 2 (and solution) :</h1>
  
  <p>I put it there in case it could be helpful to someone one day...</p>
  
  <p>I found the error in my code, which was occurring during the user's registration (and actually the only piece of code I didn't post here). I was hashing the <code>user.password</code> object instead of <code>user.password.plaintext</code>...</p>
  
  <p>It's only by changing my dependencies from "brcypt-nodejs" to "bcryptjs" that I was able to find the error because bcryptjs throws an error when asked to hash an object, while brcypt-nodejs just hashes the object as if it were a string.</p>