Jason Burgett
Jason Burgett

Reputation: 147

Using BCrypt in Rails to manually authenticate

I'm trying to use BCrypt in my rails app to save user's passwords securely. I'm able to save the encrypted password just fine and even compare it against the original plain text string to successfully verify it. The problem seems to be that every time I encrypt the password I get a different output (I'm assuming due to some salt or something) and the verification doesn't hold up after being saved to the database (I'm saving in an sqlite3 database if it makes a difference).

For example (in a Rails console):

2.1.2 :001 > u = User.new
 => #<User id: nil, created_at: nil, updated_at: nil, username: nil, password: nil> 
2.1.2 :002 > u.username = "jdoe"
 => "jdoe"
2.1.2 :002 > u.password = BCrypt::Password.create("snoopy")
 => "$2a$10$jJpHrgUmAh.YULY9CJUDjOSb9audpeD3Hx.66uVhix.WEDDB0HO.y" 
2.1.2 :003 > u.password == "snoopy"
 => true
2.1.2 :004 > u.save
 => true
u2 = User.find_by_username("jdoe")
 => [user "jdoe" is found here]
2.1.2 :006 > u2.password == "snoopy"
 => false

I know there are ready made solutions like has_secure_password, but I'd like to implement this manually to really understand what's going on.

Upvotes: 1

Views: 3396

Answers (1)

Salem
Salem

Reputation: 12986

When you set the password, you are setting it to a BCrypt::Password object. I guess that when you load it from the database it is loaded as a simple string, so == will not encrypt the given string with the original salt.

Try the following:

(...)
u.save
u2 = User.find_by_username("jdoe")
BCrypt::Password.new(u2.password) == "snoopy"

Edit

It seems Rails provides a after_find callback so if you define something like this

class User < ActiveRecord::Base
  after_find do |user|
    user.password = BCrypt::Password.new(user.password)
  end
end

it should work as you intended.

Upvotes: 1

Related Questions