A Simple Authentication with Ruby On Rails

When building a web app, you don't want every user to have access to every single part of the app, so you will want to restrict access. That's why we add authentication to our app. In this tutorial, we will create a simple authentication for your Ruby On Rails app.

PREREQUISITES

To follow along with this tutorial, you will need:

SET UP

To get started, create a new rails application and navigate to the directory:

rails new authentication
cd authentication

Next, create a welcome page and set the root to index action in WelcomeController.

WELCOME PAGE

To do this, run the following command:

rails g controller welcome index

Now, let's set up the route in config/routes.rb like so:

Rails.application.routes.draw do

  get 'welcome', to: 'welcome#index'
  root to: 'welcome#index'
end

The generator creates a welcome_controller.rb file in app/controllers

class WelcomeController < ApplicationController

  def index
  end

end

It also creates an index.html.erb file in app/views/welcome. By default, rails render a view that corresponds with the name of the controller. Now, add some content for that page in the file:

<h1>Welcome Page</h1>

BCRYPT

In the database, we will not like to store our passwords in plain text. Instead, we encrypt the password. We can do this by uncommenting gem 'bcrypt', '~> 3.1.7' in Gemfile and run bundle install to install the gem.

gem 'bcrypt', '~> 3.1.7'
bundle install

Let's continue by adding the model and controllers.

MODEL

Create a user model with attributes, email and password_digest

rails g model User email:string password_digest:string

And run rails db:migrate to migrate it to the database. The email stores the email address of the user. Also, password_digest creates a password_digest field in the users table.

In app/models/user.rb, add has_secure_password and some validations.

class User < ApplicationRecord
    has_secure_password

    validates :email , presence: true, uniqueness: true
end

Here, before a user gets saved to the database, an email should be present, and it should also be unique.

REGISTRATION CONTROLLER

Let's create a registration controller by running the following command:

rails g controller registrations new

Add the following to app/controllers/registrations_controller.rb

class RegistrationsController < ApplicationController
  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
      else
       render :new  
    end
  end

  private

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

The code snippet above creates a new user and saves it to the database. The new action initializes a new object in the User model and stores it as an instance variable that can be accessed in the views. The create action creates the user instance and sets its id to a session which redirects to the root_path if successful and renders new when it fails.

Create a sign_up form in app/views/registrations/new.html.erb.

<%= form_with model: @user, url: sign_up_path do |f|  %>
    <%= f.label :email %>
    <%= f.text_field :email %>

    <%= f.label :password %>
    <%= f.password_field :password %>

    <%= f.label password_confirmation %>
    <%= f.password_field :password_confirmation %>

    <%= f.submit "Sign Up" %>
<% end %>

and update the route:

Rails.application.routes.draw do

  get 'welcome', to: 'welcome#index'
  root to: 'welcome#index'

  resources :users

  get 'sign_up', to: 'users#new'
  post 'sign_up', to: 'users#create'

end

Now, run $ rails s to start the development server and visit localhost:3000/sign_up to create a new user.

SESSIONS CONTROLLER

Create the sessions controller by running the code:

rails g controller sessions new create destroy

and add the following code to app/controllers/sessions_controller.rb:

class SessionsController < ApplicationController
  def new
  end

  def create
    user = User.find_by(email: params[:email])

    if user.present? && user.authenticate(params[:password])
       session[:user_id] = user.id
       redirect_to root_path
    else
        render :new
    end
  end

  def destroy
    session[:user_id] = nil
    redirect_to sign_in_path
  end
end

The create method for SessionsController finds the user based on the email in the database. If a user is present and the password matches, the id of the user instance is stored in a session and they are logged in. Otherwise, we will be redirected to the sign_in page.
The destroy method sets the user session to nil and logs out the user.

In app/views/sessions/new.html.erb, add:

<h1>Sign In </h1>

<%= form_with url: sign_in_path do |f| %>

    <%= f.label :Email %>
    <%= f.text_field :email %>

    <%= f.label :Password %>
    <%= f.password_field :password %>

    <%= f.submit "Sign in" %>
<% end %>

and set up the routes:

Rails.application.routes.draw do

  get 'welcome', to: 'welcome#index'
  root to: 'welcome#index'

  resources :users

  get 'sign_up', to: 'users#new'
  post 'sign_up', to: 'users#create'

  get 'sign_in', to: 'sessions#new'
  post 'sign_in', to: 'sessions#create'
  delete 'logout', to: 'sessions#destroy'

end

Now, a user can sign_in by visiting localhost:3000/sign_in in the browser.

Current.user

Add the following in app/controllers/application_controller.rb to create a Current.user

class ApplicationController < ActionController::Base
    before_action :set_current_user

    def set_current_user
      if session[:user_id]
       Current.user = User.find_by(id: session[:user_id])   
      end
    end
end

The set_current_user method will return the current user if it finds one or if there is a session present. Also, since all controllers inherit from the ApplicationController, set_current_user will be accessible in all the controllers.

Let's create a current.rb file in app/models and add the following:

class Current < ActiveSupport::CurrentAttributes

  attribute :user

end

This code will allow us to call Current.user in our views.

Now, Open app/views/welcome/index.html.erb and update it with the following:

<h1>Welcome Page</h1>

<% if Current.user %>
  Logged in as: Current.user.email<br>
  <%= button_to "Sign Out", logout_path,  method: :delete %>
  <% else %>
  <%= link_to "Sign up", sign_up_path %>
  <%= link_to "Sign In", sign_in_path %>
<% end %>

This code checks if Current.user is present and provides a sign_out button when signed in. Otherwise, a sign_up and sign_in link is seen.

CONCLUSION

Congrats, we have successfully built a simple authentication in Ruby On Rails without relying on any gem. I hope you found this tutorial easy to follow, and If you have any questions, please add your query in the comments section. Thank you!

29