How to Add Username Authentication to Your Rails App with Devise: A Step-by-Step Guide
August 31, 2024
How to Add Username Authentication to Your Rails App with Devise: A Step-by-Step Guide
In today's digital landscape, offering flexible sign-up and sign-in options can significantly improve user experience and engagement on your web application. If you're using Ruby on Rails and Devise for authentication, you might want to allow users to register and log in using either their username or email address. In this article, we'll guide you through adding a username field to your Devise-powered Rails application and configuring it to accept both usernames and emails for authentication.
Why Use Username-Based Authentication?
Usernames provide a more personalized touch to user accounts and can be easier to remember than email addresses. Allowing users to sign up and sign in using either their username or email offers flexibility and caters to a broader range of user preferences.
Step 1: Add a Username Field to Your Devise Users Table
To get started, we need to add a username
field to the users
table in your database. If you haven't already done so, modify the Devise migration file to include the username
column.
-
Update the Devise Migration File
Open the Devise migration file (
db/migrate/[timestamp]_devise_create_users.rb
) and add the following line:t.string :username, null: false, default: ""
Also, add an index to ensure that each username is unique:
add_index :users, :username, unique: true
The final code will be like this.
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[7.1]
def change
create_table :users do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
# Username
t.string :username, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
# t.integer :sign_in_count, default: 0, null: false
# t.datetime :current_sign_in_at
# t.datetime :last_sign_in_at
# t.string :current_sign_in_ip
# t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
add_index :users, :username, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
-
Run the Migration
Apply the changes to your database by running:
rails db:migrate
Step 2: Configure Devise to Support Username Authentication
Next, we need to update Devise's configuration to allow sign-in using both the username
and email
.
-
Modify the Devise Configuration File
Open
config/initializers/devise.rb
and set the authentication key to:login
, a virtual attribute we'll define next:Devise.setup do |config| config.authentication_keys = [:login] end
-
Add the Virtual Attribute
login
to the User ModelUpdate your
User
model (app/models/user.rb
) to include a virtual attributelogin
and override the default Devise method for finding users:class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable # Virtual attribute for authenticating by either username or email attr_accessor :login validates :username, presence: true, uniqueness: { case_sensitive: false } # Override Devise's find_for_database_authentication method def self.find_for_database_authentication(warden_conditions) conditions = warden_conditions.dup if (login = conditions.delete(:login)) where(conditions.to_h).where( ["lower(username) = :value OR lower(email) = :value", { value: login.downcase }] ).first else where(conditions.to_h).first end end end
Step 3: Permit All Necessary Parameters in Devise Controller
To ensure that all the required parameters (username
, email
, password
, etc.) are allowed during sign-up and sign-in, update the ApplicationController
:
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username, :email, :password, :password_confirmation])
devise_parameter_sanitizer.permit(:account_update, keys: [:username, :email, :password, :password_confirmation, :current_password])
devise_parameter_sanitizer.permit(:sign_in, keys: [:login, :password, :remember_me])
end
end
Step 4: Update Your Devise Views to Include Username and Login Fields
Finally, modify the Devise views to support the new username
and login
fields.
-
Sign-in View: Open
app/views/devise/sessions/new.html.erb
and add thelogin
field:<div class="field"> <%= f.label :login, "Username or Email" %><br /> <%= f.text_field :login, autofocus: true, autocomplete: "username" %> </div>
-
Sign-up View: Make sure the sign-up view (
app/views/devise/registrations/new.html.erb
) includes bothusername
andemail
fields.
<div class="field">
<%= f.label :username, class: 'block text-sm font-medium text-gray-700' %>
<%= f.text_field :username, autofocus: true, autocomplete: "username", class: 'mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm' %>
</div>
Step 5: Test Your Changes
Restart your Rails server and test the following:
- Sign up: Ensure users can register with both
username
andemail
. - Sign in: Verify that users can log in using either
username
oremail
along with their password.
Conclusion
By following these steps, you have successfully added a username
field to your Devise-powered Rails application and configured it to allow users to sign up and sign in with either their username or email. This enhancement offers flexibility and improves the overall user experience.
For more details on Devise and its capabilities, check out the Devise GitHub Repository.
Further Reading
By providing multiple authentication options, you're making your app more accessible and user-friendly — an important factor in retaining users and boosting engagement!
Happy coding! 😊💻