Robert's Homepage

How to Add Usernames to Clearance

#ruby-on-rails #authentication

In this tutorial we will cover adding a username to Clearance User models in Ruby on Rails. This post assumes you already completed Clearance setup instructions for your application and generated the appropriate models, views and controllers.

Generate Username Migration

Generate a migration to add an additional column to the users table called username:

$ bundle exec rails generate migration AddUsernameToUsers username

Open your newly generated migration file to ensure column addition is correct. In the code listing below, uniqueness and non-null validations are added to the database column configuration. These are optional, but, recommended.

class AddUsernameToUsers < ActiveRecord::Migration[6.1]
  def change
    add_column :users, :username, :string, unique: true, null: false
  end
end

On verifying your migration appears correct run the migration via Rails command line tools to apply the schema changes:

$ bundle exec rails db:migrate

Optional: Add Regular Expression Validations to User Model

As an optional addition, you may consider adding a regular expression validation check to the User model to ensure that username are consistent and sanitized. For example:

class User < ApplicationRecord
 include Clearance::User
 
 # Regular expression to allow letters, numbers, and underscore characters
 VALID_USERNAME = /\A(\w|\.)+\z/.freeze
 
 validates :username, 
    presence: true, 
    format: { with: VALID_USERNAME, message: 'Username can only include numbers, letters or underscores.'  }
end

Override the UsersController

Generate a UsersController for your application that inherits from Clearance::UsersController in order to add additional behaviour:

class UsersController < Clearance::UsersController
  # TODO
end

By default, Clearance::UsersController defines create and new actions to create and render Users, respectively. In this child controller, will need override the strong parameters method user_from_params which is inherited from the parent class to include the username field form the migration. This strong parameters method is used by both the create and new actions in the parent class.

class UsersController < Clearance::UsersController
 # -- snipped --
 
 private 
 # Parameters used to create a new user record via +new+ and +create
  def user_from_params
    email = user_params.delete(:email)
    username = user_params.delete(:username)
    password = user_params.delete(:password)

    Clearance.configuration.user_model.new(user_params).tap do |user|
      user.username = username
      user.email = email
      user.password = password
    end
  end
end

The above snippet is a modified version of the user_from_params method in the parent class of Clearance::UsersController that includes the username parameter.

Update Clearance Views to Include Username

With the UsersController capable of accepting usernames in the payload sent by the view, we can update the user sign up view to include a username field during the sign up process. This assumes you are using stock views provided by Clearance via:

bundle exec rails generate clearance:views
<div id="clearance" class="sign-up">
  <h2>Sign Up</h2>

  <%= form_with model: @user do |form| %>

    <div class="text-field">
      <%= form.label :username %>
      <%= form.text_field :username %>
    </div>

    <%= render partial: '/users/form', object: form %>

    <div class="submit-field">
      <%= form.submit %>
    </div>

    <div class="other-links">
      <%= link_to "Sign In", sign_in_path %>
    </div>
  <% end %>
</div>

The primary modification is adding an addition field for the username above the partial template provided by Clearance. On addition, the sign up form will now include a username field to complete in order to sign up for the application:

A user sign up form containing a username field

Figure 1: The user sign up form containing the username field.