A Simple Authentication with Ruby On Rails
Tags: rails, ruby, authentication
Published: 2021-07-29 11:46:32


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:
- Some basic knowledge of Ruby On Rails
- A text editor(I am using [sublime text](https://www.sublimetext.com/))
- [Ruby](https://www.ruby-lang.org/en/), [Rails](https://rubygems.org/gems/rails), [Node](https://nodejs.org/en/) and [Yarn](https://classic.yarnpkg.com/en/) installed on your computer.

## 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!