Take me home

Converting from Sorcery (Rails plugin) to plain BCrypt

Written by August Lilleaas, published February 21, 2013

I recently split a Rails app into one Ruby app and a set of Clojure apps. The authentication in the Rails app was implemented using the Sorcery plugin, which defaults to BCrypt, so porting to Clojure is relatively easy, since BCrypt is a standard crypto method with implementations available on most platforms.

But as always with magic Rails plugins, there was a twist :)

The solution

This is the Clojure code I ended up with:

(BCrypt/checkpw (str password old-rails-salt) old-rails-password-hash)

old-rails-salt is the contents of the User#salt from the Rails app. Also, old-rails-password-hash is the contents of the User#crypted_password.

Normally, all you need to do is to pass the password and the BCrypt password hash. The BCrypt hash already includes a salt as part of its contents, so there's no need for an additional salt. For some reason, Sorcery also salts the password by appending User#salt to the password before hashing it.

My theory is that this is an artifact of the modularity of Sorcery. When Sorcery uses SHA1, it needs to salt, and Sorcery isn't coded so that salting the password is optional. And it doesn't hurt usability on the Rails end, since sorcery is very automagic. As we can see, it's less than ideal when we want to use Sorcery crypto outside of Sorcery, so it's not very portable. But I'm not gonna complain, I guess it's rare to move from Sorcery to another auth system, or even another platform altogether, so they just haven't fixed it. I should probably stop complaining and submit a patch.

The full code:

(defn valid-password?
  [user password]
  (if-let [old-rails-salt (:old_rails_salt user)]
    (BCrypt/checkpw (str password old-rails-salt) (:password_hash user))
    (BCrypt/checkpw password (:password_hash user))))

Only users imported from the Rails database will have old_rails_salt set on them, newly created users will not.


Questions or comments?

Feel free to contact me on Twitter, @augustl, or e-mail me at august@augustl.com.