The current front-end stack for the application I develop at my full-time job is built on Meteor. We've been struggling with limitations of the platform for well over a year, and are finally making strides to move off of Meteor.

Ultimately we want to keep this migration process as seamless and uneventful as possible for our users, so in an effort to minimize the disturbance of this process to our user base, I wanted to determine whether we could continue to utilize the existing password hashes outside of Meteor.

A quick look at a hashed password in the database reveals that passwords are hashed with bcrypt, which is no surprise as that's pretty standard fare.

So, the first thing I did was attempt to check a plaintext password against the hashed one:

const bcrypt = require('bcrypt');

// NOT an actual value from our database.. ;)
const hashed = '$2a$10$XHecfl2Yfn3hzlV8VhugKOn4SmplQ2qA9jspq4Z8sSLkGphtWVgsO';

console.log(bcrypt.compareSync('password', hashed));

Sadly, the result was false

So of course I immediately assumed they must be using a custom salt on the plaintext password, or something to that effect...

Not exactly...

It turns out that they're actually pre-hashing the plaintext password with sha256 and then running that hash through bcrypt.

So, in order to continue to allow users to login on our new system, without forcing them to reset their passwords, all we'll need to do is something like this:

const bcrypt = require('bcrypt');
const crypto = require('crypto');

// NOT an actual value from our database.. ;)
const stored = '$2a$10$XHecfl2Yfn3hzlV8VhugKOn4SmplQ2qA9jspq4Z8sSLkGphtWVgsO';

const userInput = crypto
  .Hash('sha256')
  .update('usersplaintextpassword')
  .digest('hex')

console.log(bcrypt.compareSync(userInput, stored));

This will result in true if the password is correct, so we can all get on with our lives, and the users ideally will be none the wiser that meteor has been completely ripped out from behind our UI (not that they necessarily knew it was there in the first place).