Where developers come to connect, share, build and be inspired.

2

Thoughts on login using username or e-mail address with Devise

1533 views


Devise is one of the more polarising libraries out there in the Rails ecosystem — you either love it or hate it. I'm a fan; having used it for multiple projects throughout it's evolution and giving a few alternatives a try (including rolling my own), I can't really fault it.

One thing about it that does irk me, however, is the use of the #devise_controller? method and the pattern of customising it's behaviour by putting code in ApplicationController.

If you follow the official how-to on allowing users to login using either username or password, you'll end up with something like this in your ApplicationController:

before_filter :configure_permitted_parameters,
              :if => :devise_controller?

protected

def configure_permitted_parameters
  # ...
end

This logic is needed for only a handful of actions on one controller (DeviseController), yet we end up with a before_filter that checks #devise_controller? before every action in your application. Admittedly the consequences of this are minimal, but it's not a good pattern to repeat and is generally bad object-oriented design.

Thanks to the power of Ruby, and the syntactic sugar provided by ActiveSupport::Concern, we have a nice alternative:

# config/initializers/devise.rb

module MyPermittedParameters
  extend ActiveSupport::Concern

  included do
    before_filter :configure_permitted_parameters
  end

  protected

  def configure_permitted_parameters
    ...
  end
end

DeviseController.send :include, MyPermittedParameters

While we're picking on the above-linked guide, why not use Arel instead of raw SQL when overriding #find_first_by_auth_conditions:

def self.find_first_by_auth_conditions(warden_conditions)
  conditions = warden_conditions.dup

  if login = conditions.delete(:login)
    where(conditions). \
    where(arel_table[:username].lower.eq(login.downcase).or(
          arel_table[:email].lower.eq(login.downcase))).first
  else
    where(conditions).first
  end
end

Comments

  • Profile_square

    Nice direction. I've introduced username's for accounts on a side-project but haven't switched over to allowing login using them because it was a hassle I didn't want to deal with yet. Your approach seems cleaner and I'll probably refer back here when I'm ready to implement username login. Thanks. :)

Add a comment