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)
- Ruby, Rails, Node and Yarn 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!
Top comments (0)