Having your own authentication engine can be fun. You get to know how things work, why you should use salt, pepper, SHA2 instead of MD5 and much more. It also allows you to work with many old systems built before anyone heard about Devise. Still, I must say, that in old, maintained systems, sometimes it is worth throwing your own solution in favour of something that is already out there. Thanks to this, you won’t have to support the whole dedicated authentication engine stack (code, tests, docs). I decided to do exactly this: move from my own engine that was maintained for last 6 years to the Devise based authentication.

Moving whole controllers logic is quite simple: you just drop whatever you have and you use Devise stuff ;) but what about your custom, self-build encryption engine? The easiest approach would be to reset all the passwords and ask users to provide a new one again. Unfortunately it is not as user-friendly as we would want it to be. Users might feel afraid that we ask them for their password again not in the sign in process.

Luckily there’s a much easier approach: you can just use your current custom encryptor with Devise (as long as it is safe).

To do this, you need to do following things:

  1. Adding devise-encryptable to your Gemfile
  2. Moving your encryption logic to a Devise proper namespace
  3. Setting your encryption engine as a default one for Devise

After that, you should be able to use Devise with any encryption engine you used to.

Adding devise-encryptable to your Gemfile

This is definitely the easiest part. In your Gemfile file just:

gem 'devise'
gem 'devise-encryptable'

and run bundle install.

Moving your encryption logic to a Devise proper namespace

This is the hardest part. Create a file in your initializers (or add it to /config/initializers/devise.rb). It should contain your encryptor inside following modules:

module Devise
  module Encryptable
    module Encryptors
      # Here you should but encryption class that inherits from Base
    end
  end
end

Inside of it, you need to create a class that will correspond to your encrypion engine. It must inherit from Encryptors Base class and should contain one method called digest:

module Devise
  module Encryptable
    module Encryptors
      class CustomAuthentication < Base
        def self.digest(password, stretches, salt, pepper)
        end
      end
    end
  end
end

This method accepts following parameters:

  1. password – password provided by user
  2. stretches – cost for hashing the password (default 10)
  3. salt – salt that should be used for hashing
  4. pepper – pepper that should be used for hashing

Now, once you have all of this, you should just implement your logic in the digest method and return a password hash. For example like this one:

require "digest/sha2"

module Devise
  module Encryptable
    module Encryptors
      class CustomAuthentication < Base
        def self.digest(password, stretches, salt, pepper)
          string_to_hash = "#{pepper}#{salt}#{password.reverse}"
          Digest::SHA2.hexdigest(string_to_hash)
        end
      end
    end
  end
end

Setting your encryption engine as a default one for Devise

Now in your devise.rb config file set:

# Require the `devise-encryptable` gem when using anything other than bcrypt
config.encryptor = :custom_authentication

also keep in mind, that your devise using models should include encryptable options as well:

devise :database_authenticatable,
  :trackable, :encryptable, :confirmable, :recoverable,
  :registerable, :validatable, :lockable, :rememberable,
  :omniauthable, omniauth_providers: [:facebook]