Permalink
Browse files

Split out BCrypt hashing to make it reusable

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 Mar 25, 2014
1 parent a29fee1 commit a8769933d1842e8dff1a625d202f693403663e12
Showing with 29 additions and 16 deletions.
  1. +22 −0 lib/devise/encryptor.rb
  2. +5 −14 lib/devise/models/database_authenticatable.rb
  3. +2 −2 test/devise_test.rb
View
@@ -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.

Show comment
Hide comment
@betesh

betesh May 8, 2015

Contributor

Why pass the klass instead of the pepper?

@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
@@ -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
@@ -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
@@ -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
View
@@ -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

0 comments on commit a876993

Please sign in to comment.