Argon2 with Devise

bcrypt has been a great choice for safely storing passwords. However, as time has passed, a better alternative has emerged: Argon2. OWASP now recommends Argon2 for new applications. With a little bit of code, you can use Argon2 with Devise.

Devise supports custom encryptors. However, it requires a separate column to store a salt, which isn’t needed as Argon2 stores the salt in the password hash (like bcrypt).

Instead, add argon2 to your Gemfile:

gem 'argon2', '>= 2'

And create config/initializers/devise_argon2.rb with:

module Argon2Encryptor
  def digest(klass, password)
    if klass.pepper.present?
      password = "#{password}#{klass.pepper}"
    end
    ::Argon2::Password.create(password)
  end

  def compare(klass, hashed_password, password)
    return false if hashed_password.blank?

    if hashed_password.start_with?("$argon2")
      if klass.pepper.present?
        password = "#{password}#{klass.pepper}"
      end
      ::Argon2::Password.verify_password(password, hashed_password)
    else
      super
    end
  end
end

Devise::Encryptor.singleton_class.prepend(Argon2Encryptor)

All new passwords will be hashed with Argon2. For existing passwords, rotate to Argon2 when a user signs in. Add to your model:

class User < ApplicationRecord
  def valid_password?(password)
    valid = super
    if valid && !encrypted_password.start_with?("$argon2")
      self.password = password
      save(validate: false)
    end
    valid
  end
end

You can also rehash all passwords at once, but it’s a bit more complicated.

Congrats, your password storage is even stronger!

Published March 23, 2019 · Tweet


You might also enjoy

Hardening Devise

Google OAuth with Devise

Modern Encryption for Mongoid


All code examples are public domain.
Use them however you’d like (licensed under CC0).