Devise: manually encrypt password and store directly

jmccartie picture jmccartie · Nov 4, 2011 · Viewed 35.8k times · Source

I'm trying to migrate a ton of users from an old database. To do this, I'm using activerecord-import and trying to save all my user data directly to DB (bypassing the User model).

My issue: I need to take the old user's plain-text password, encrypt it, and store directly to the DB. I know how to generate a password using Devise, but am wondering if there's a way to get a hashed password that I can store directly to the database.

Hoping to do:

new_hashed_password = Devise.awesome_encrypting_method(old_user.password)

Then store "new_hashed_password" directly into the DB without going through the model. I dug around in Devise and found this:

def password_digest(password)
  ::BCrypt::Password.create("#{password}#{self.class.pepper}", :cost => self.class.stretches).to_s
end

@@stretches defaults to 10 (lib/devise.rb:71) and isn't overridden by my initializer

@@pepper defaults to nil (lib/devise.rb:148) and isn't overridden by my initializer

I thought I could manually re-create password_digest() but I think I'm missing something fundamental about Bcrypt because even with setting password and stretches, the resulting hash is different every time.

Any ideas? Thanks for your help!

Answer

Robert Kajic picture Robert Kajic · Jul 2, 2013

You should do it like this:

password = 'the secret password'
new_hashed_password = User.new(:password => password).encrypted_password

This is much better than using BCrypt directly as it abstracts away how passwords are generated from your code, making it easier to understand, and also immune to changes in how devise constructs encrypted passwords. Your code should not, and has no reason to know anything about that.