Be a Zeek

Spree Uname Login/Signup

Spree default provides login/signup with just email.

But now a days many E-Coms are taking a trend to introduce username or phone number as alternate login options.

This article is all about how to use alternate options (other than email) for login/signup in a Spree app. I am targeting username here but using other options will be much similar to that.

Start with adding DB migration to add uname attribute to User model.

$ rails g migration add_uname_to_spree_users
class AddUnameToSpreeUsers < ActiveRecord::Migration
  def up
    add_column :spree_users, :uname, :string
    add_index :spree_users, :uname

    Spree::User.where(uname: nil).each do |user|

  def down
    remove_index :spree_users, :uname
    remove_column :spree_users, :uname

In migration, you will notice a loop to set_uname for existing users.

# In User model (user_decorator.rb)
def set_uname
  if self.uname.blank? &&
    # Fetch string prior to @ from emailID
    username =[0]
    # remove chars other than numbers and alphabets
    username = username.titleize.gsub(/[^a-zA-Z0-9]/, '')
    count = 0
    # add counter with username if some other user already has it
    while Spree::User.find_by('lower(uname) = :value', value: username.downcase)
      username += (count += 1).to_s

    self.uname = username

This set_uname ensures username for each user to be extracted out from its email and only allowing alphabets and numbers to be a part of uname. You can change logic here to suit your need.

If you want to auto assign username to user, you can

# In User model (user_decorator.rb)
before_validation :set_uname, on: :create

We are set now to code/customise Spree login/signup process.

If you want user to set uname of their choice while signing up set

# In config/initializers/spree.rb

Update signup form accordingly to have uname text field.

Change login form email text_field to login text field,

<%= f.email_field :email %>
# TO
<%= f.text_field :login %>

Add login authentication key to devise,

# In config/initializers/devise.rb
Devise.setup do |config|
  config.authentication_keys = [ :login ]

Change the way devise finds user,

# In User model (user_decorator.rb)
def self.find_for_database_authentication(warden_conditions)
  conditions = warden_conditions.dup
  if login = conditions.delete(:login)
    where(conditions.to_hash).where('lower(uname) = :value OR lower(email) = :value', value: login.downcase).first
    conditions[:email].downcase! if conditions[:email]

And you should be good to go.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: