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

Why and How to Keep Your Decryption Keys Off Web Servers


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