Authentication Using Session Rails

Tejal Panjwani
Simform Engineering
7 min readMar 27, 2023

Explore session-based authentication into the rails application.

User authentication is a key component of online application security.

It helps you to verify a user’s identity.

There are two significant ways to authenticate an app in Rails:

1. Authentication based on Sessions: When a user registers or logs in, session data is stored in memory, and a session ID is provided to the browser. This is known as session-based authentication.

2. Authentication using Tokens: For token-based authentication to function, each request to a server must be followed by a signed token, which the server verifies for authenticity before fulfilling the request. Checkout the blog for token-based authentication.

Out of these two primary authentication techniques, we will focus on session-based authentication in this tutorial. So, let’s dive deep into the thick of things.

Authentication based on Sessions

Session:

Each user’s session in your application can be used to save a limited amount of data that will be kept on the file between requests. It’s only accessible in the controller and view. The session can utilize a number of various storing techniques, but by default, it utilizes CookieStore, which stores everything on the client.

We have two options for authenticating: manually or with a gem. Device gem is the most used ruby for authentication.

Begin by manually authenticating:

Rails version 6.0.6 and Ruby version 2.6.0 are used here.
now build a new authentication Rails application.

 rails new user_auth

A user_auth folder will be created, and this folder will contain the controller, model, and view. Go to the user_auth folder now.

Create a user model that includes a password and email field.

 rails generate model User email:string password_digest:string

Use the following command to execute migration after running the commands above to generate the model.

rails db:migrate

Create a user table and load the schema file.
The migration file appears as follows:

class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :email
t.string :password_digest

t.timestamps
end
end
end

For storing passwords in an encrypted format in this case, we utilized password digest. The bcrypt gem is used to encrypt passwords.

Let’s include a bcrypt gem.

gem 'bcrypt'

After including the command gem run the below command

bundle install

Before a user is added to the database, we’ll check to ensure the email field is valid and unique. Moreover, email addresses should follow a pattern; regular expression can help.

Add the following validation to the user model.

class User < ApplicationRecord  
validates :email, presence: true, uniqueness: true, format: { with: /\A[^@\s]+@[^@\s]+\z/, message: 'Invalid email' }
end

Here, the has_secure_password method is provided by the bcrypt gem. In the user model, include the has_secure_password method.

class User < ApplicationRecord
has_secure_password

validates :email, presence: true, uniqueness: true, format: { with: /\A[^@\s]+@[^@\s]+\z/, message: 'Invalid email' }
end

By default has_secure_password method considers the attribute name as a password. And provide authenticate method to check whether the password is correct or not. For example user.authenticate(‘password_value’)

If the attribute name is not a password, we can give a new name as an argument to has_secure_password. The password is encrypted and kept in the password_digest by the has_secure_password method.

Add column_name_digest into the database if you want any other column to store data in an encrypted manner with the has_secure_password method.

Let’s assume we require a profile_password column to store encrypted data. In the database, add the profile_password_digest field. Simply add the model’s code given below.

class User < ApplicationRecord
has_secure_password # Default password field
has_secure_password :profile_password # Utilised profile_password field
end

We utilized the profile_password filed name at the front-end, which has_secure_password method generated encryption automatically and stored it in profile_password_digest. Use user.authenticate_profile_password(‘profile_password_value’) to determine whether the profile_password entered is correct or not.

Just add the field name after the authentication method for an automatic reference to the custom authentication field we added.

Register User:

Let’s explore creating a user account for an application in more detail.
A registration controller should be created:

rails g controller registrations

Add routes as shown below

# config/routes.rb
resources :registrations, only: [:new,:create]

Let’s create a view file for registration after adding routes.

<!-- app/views/registrations/new.html.erb -->
<div class="text-center">
<h1>Registration</h1>

<%= form_with(model: @user, url: registrations_path, local: true) do |f| %>

<!-- Display error message of form -->
<% if f.object.errors.full_messages.any? %>
<ul class='error'>
<% f.object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>

<!-- Field of registration form -->
<div class='row'>
<%= f.label :email %>
<%= f.text_field :email, placeholder: 'Please enter email id' %>
</div>

<div class='row'>
<%= f.label :password %>
<%= f.password_field :password, placeholder: 'Please enter password' %>
</div>

<div class='row'>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, placeholder: 'Please enter password confirmation' %>
</div>

<div class='row'>
<%= f.submit 'Sign Up' %>
</div>
<% end%>
</div>

Updating the registration controller now

# app/controllers/registrations_controller.rb
class RegistrationsController < ApplicationController
skip_before_action :authenticate_user, only: [:new, :create]
before_action :redirect_if_authenticated, only: [:new, :create]

def new
@user = User.new
end

def create
@user = User.new(user_params)
if @user.save
session[:user_id] = @user.id
redirect_to root_path, flash: { success: 'Registration successfully' }
else
render :new
end
end

def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end

The registration page looks like the one below.

Registration page

Login User

Let’s now focus on sign-in features for the user login process. I created a session controller as a result.

rails g controller sessions

Add the following paths for the user’s login and logout.

 resources :sessions, only: [:new, :create, :destroy]

Let’s create a view file for sign-in after adding routes.

<!-- app/views/sessions/new.html.erb -->
<div class="text-center">
<h1>Sign In</h1>

<%= form_with(model: @user, url: sessions_path, local: true) do |f| %>

<!-- Display error message of form -->
<% if f.object.errors.full_messages.any? %>
<ul class='error'>
<% f.object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>

<!-- Field of sessoin form -->
<div class='row'>
<%= f.label :email %>
<%= f.text_field :email, placeholder: 'Please enter email id' %>
</div>

<div class='row'>
<%= f.label :password %>
<%= f.password_field :password, placeholder: 'Please enter password' %>
</div>

<div class='row'>
<%= f.submit 'Sign In' %>
</div>
<% end %>
</div>

Updating the session controller now

# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
skip_before_action :authenticate_user, only: [:new, :create]
before_action :redirect_if_authenticated, only: [:new, :create]

def new
@user = User.new
end

def create
@user = User.find_by(email: params[:user][:email])
if @user.present? && @user.authenticate(params[:user][:password])
session[:user_id] = @user.id
redirect_to root_path, flash: { success: 'Logged in successfully' }
else
render :new
end
end

def destroy
session[:user_id] = nil
redirect_to root_path, flash: { success: 'Logged Out' }
end
end

If you attempt to access the root page without logging in, redirect_if_authenticated action will call first and route you to the below-described sign-in page.

Sign in page

Here, we’ve implemented authenticate_user and redirect_if_authenticated to application_controller.rb

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
helper_method :current_user
helper_method :user_signed_in?
before_action :authenticate_user

def current_user
@current_user ||= session[:user_id] && User.find_by(id: session[:user_id])
end

def user_signed_in?
!current_user.nil?
end

def authenticate_user
redirect_to new_session_path, flash: {danger: 'You must be signed in'} if current_user.nil?
end

def redirect_if_authenticated
redirect_to root_path, flash: { info: 'You are already logged in.'} if user_signed_in?
end
end

We redirect to the root page when the user signs in and registers. Made changes to the route file as shown below:

root to: 'home#index'

Build a home controller with an index action, as below

rails g controller home index

We’ll explore home_controller.rb

class HomeController < ApplicationController
def index; end
end

Now let’s modify the home’s index page

<div class="text-center">
<h1>Welcome to authentication application</h1>
<%= current_user.email %>
<p>user sign in <%= user_signed_in? %></p>
</div>

When a user is logged in, include a logout link

<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
<head>
<title>UserAuth</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>

<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>

<body>
<%= render 'layouts/flash' %>

<% if user_signed_in? %>
<%= link_to 'Sign Out', sign_out_path, class: 'sign_out', method: :delete %>
<% end %>

<%= yield %>
</body>
</html>

Build a flash message file as shown below.

<% flash.each do |type, msg| %>
<div class="alert alert-<%= type %>">
<%= msg %>
</div>
<% end %>

types include info, success, danger, warning, and providing CSS to the application.css file

.alert {
font-weight: bold;
-moz-border-radius: 4px 4px 4px 4px;
-webkit-border-radius: 4px 4px 4px 4px;
border-radius: 4px 4px 4px 4px;
margin-bottom: 18px;
padding: 12px 25px 12px 24px;
-moz-box-shadow: 3px 3px 1px 1px #CCCCCC;
-webkit-box-shadow: 3px 3px 1px 1px #CCCCCC;
box-shadow: 3px 3px 1px 1px #CCCCCC;
}


.alert-danger {
background-color: #ffebe9;
border-color: #fc7e72;
color: black;
padding-top: 20px;
}

.alert-success {
background-color: #dafbe1;
border-color: #44ab5a;
color: black;
padding-top: 20px;
}

.alert-info {
background-color: #ddf4ff;
border-color: #54aeff66;
color: black;
padding-top: 20px;
}

.alert-warning {
background-color: #fff8c5;
border-color: #cda22966;
color: black;
padding-top: 20px;
}

After logging in, add a screen sort of root page with the flash message as shown below.

Root page after user logged in

Conclusion

We have developed and set up an authentication system from scratch using Rails. For this purpose, we have utilized a session-based authentication approach. The major security of authentication is password protected, so we used the bcrypt gem to store encrypted passwords. We hope this tutorial will help you to implement robust authentication in your Rails project.

Until next time, stay tuned and keep following Simform Engineering for more such important and exciting tutorial on latest tools and technologies.

--

--