Skip to content

Commit

Permalink
Split out BCrypt hashing to make it reusable
Browse files Browse the repository at this point in the history
This logic is generic and reusable -- hash a secret; and take an
unhashed secret and compare it to a hashed secret. This breaks this out
to make it reusable in other places. Specifically, we use this in our
own token auth at Bonobos that we plan to split out as a Devise
extension. This will make that possible without copy & pasting this
code.
  • Loading branch information
magnusvk committed May 6, 2015
1 parent a29fee1 commit a876993
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 16 deletions.
22 changes: 22 additions & 0 deletions lib/devise/encryptor.rb
@@ -0,0 +1,22 @@
require 'bcrypt'

module Devise
module Encryptor
def self.digest(klass, password)
if klass.pepper.present?
password = "#{password}#{klass.pepper}"
end
::BCrypt::Password.create(password, cost: klass.stretches).to_s
end

def self.compare(klass, encrypted_password, password)

This comment has been minimized.

Copy link
@betesh

betesh May 8, 2015

Contributor

Why pass the klass instead of the pepper?

return false if encrypted_password.blank?
bcrypt = ::BCrypt::Password.new(encrypted_password)
if klass.pepper.present?
password = "#{password}#{klass.pepper}"
end
password = ::BCrypt::Engine.hash_secret(password, bcrypt.salt)
Devise.secure_compare(password, encrypted_password)
end
end
end
19 changes: 5 additions & 14 deletions lib/devise/models/database_authenticatable.rb
@@ -1,13 +1,10 @@
require 'devise/strategies/database_authenticatable'
require 'bcrypt'
require 'devise/encryptor'

module Devise
# Digests the password using bcrypt.
def self.bcrypt(klass, password)
if klass.pepper.present?
password = "#{password}#{klass.pepper}"
end
::BCrypt::Password.create(password, cost: klass.stretches).to_s
ActiveSupport::Deprecation.warn "Devise.bcrypt is deprecated; use Devise::Encryptor.digest instead"
Devise::Encryptor.digest(klass, password)
end

module Models
Expand Down Expand Up @@ -47,13 +44,7 @@ def password=(new_password)

# Verifies whether a password (ie from sign in) is the user password.
def valid_password?(password)
return false if encrypted_password.blank?
bcrypt = ::BCrypt::Password.new(encrypted_password)
if self.class.pepper.present?
password = "#{password}#{self.class.pepper}"
end
password = ::BCrypt::Engine.hash_secret(password, bcrypt.salt)
Devise.secure_compare(password, encrypted_password)
Devise::Encryptor.compare(self.class, encrypted_password, password)
end

# Set password and password confirmation to nil
Expand Down Expand Up @@ -151,7 +142,7 @@ def authenticatable_salt
# See https://github.com/plataformatec/devise-encryptable for examples
# of other encryption engines.
def password_digest(password)
Devise.bcrypt(self.class, password)
Devise::Encryptor.digest(self.class, password)
end

module ClassMethods
Expand Down
4 changes: 2 additions & 2 deletions test/devise_test.rb
Expand Up @@ -14,11 +14,11 @@ class DeviseTest < ActiveSupport::TestCase
test 'bcrypt on the class' do
password = "super secret"
klass = Struct.new(:pepper, :stretches).new("blahblah", 2)
hash = Devise.bcrypt(klass, password)
hash = Devise::Encryptor.digest(klass, password)
assert_equal ::BCrypt::Password.create(hash), hash

klass = Struct.new(:pepper, :stretches).new("bla", 2)
hash = Devise.bcrypt(klass, password)
hash = Devise::Encryptor.digest(klass, password)
assert_not_equal ::BCrypt::Password.new(hash), hash
end

Expand Down

0 comments on commit a876993

Please sign in to comment.