In this tutorial, we will be creating a basic authentication in Ruby on Rails.
Step 1: Adding a password_digest field to the model.
This field will be used for authenticating the user.
Step 1.1-A: Coming from a fresh Rails project.
If you are doing this from a fresh project then you must first create a User model by running rails g model User in the terminal.
This will create a file in db/migrate/xxxxx_create_users.rb
where xxxxx
is the current date and time you ran the command.
In that file add the following:
1
2
3
4
5
6
7
8
9
class CreateUsers < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
t.string :username
t.string :password_digest
t.timestamps
end
end
end
Return to your terminal and run:
1
$ rake db:migrate
This will create a users table in the database with columns username and password digest.
Step 1.1-B: Coming from an existing Rails project.
If you are doing this from an existing project then you must add password_digest
field to your existing model.
In this tutorial, we will be using User
as the model. Feel free to change it on your preference.
Run the following command in the terminal:
1
$ rails g migration add_password_digest_to_users
This will create a file in db/migrate/xxxxx_add_password_digest_to_users.rb
In that file add the following:
1
2
3
4
5
class AddPasswordDigestToUsers < ActiveRecord::Migration[5.2]
def change
add_column :users, :password_digest, :string
end
end
Return to your terminal and run:
1
$ rake db:migrate
This will add a string column password_digest
to your users table in the database.
Note:
In this step, you may notice that ActiveRecord::Migration[5.2]
has 5.2
in it, you must change it to the correct version, otherwise, this might not work.
You might want to check your Rails version by running the following command in the terminal:
1
$ rails -v
This tutorial is created using Rails 5.2.3.
Step 2: Creating routes for the app.
Open config/routes.rb
in your text editor.
In this file, we will define the links in our app. For this tutorial, we will be creating six (6) routes. one (1) that needs authentication and five (5) unauthenticated routes.
1
2
3
4
5
6
7
8
9
10
11
Rails.application.routes.draw do
# AUTHENTICATED ROUTES
get '/dashboard' => 'dashboard#index' # THIS WILL BE THE DASHBOARD OF THE USER
# UNAUTHENTICATED ROUTES
get '/sign-up' => 'users#new' # THIS IS WHERE THE USER WILL REGISTER
post '/save-user' => 'users#create' # THIS WILL BE THE PROCESS OF REGISTERING THE USER
get '/' => 'sessions#new' # THIS IS WHERETHE USER WILL SIGN IN
post '/' => 'sessions#create' # THIS WILL BE THE PROCESS OF SIGNING IN THE USER
delete '/sign-out' => 'sessions#destroy' # THIS WILL BE THE PROCESS OF SIGNING OUT THE USER
end
Step 3: Creating controllers for the routes.
At this point, our app will raise an error because the routes we defined doesn’t have a matching controller.
We can see that we used three (3) different controllers in Step 2. These are the dashboard, users, and sessions.
To create a matching controller for these routes you can run the following command in the terminal. One (1) at a time.
1
2
3
$ rails g controller dashboard
$ rails g controller users
$ rails g controller sessions
This will create files in app/controllers
directory.
In the dashboard controller app/controllers/dashboard_controller.rb
add the following method:
1
2
def index
end
In the users controller app/controllers/users_controller.rb
add the following method:
1
2
3
4
5
def new
end
def create
end
In the sessions controller app/controllers/sessions_controller.rb
add the following method:
1
2
3
4
5
6
7
8
def new
end
def create
end
def destroy
end
If you observe the routes, it does not only matches to the controller but also matches to the methods in that controller.
The creation of method above ensures that our controller matches our routes.
Step 4: Creating the dashboard page.
In the app/views/dashboard/index.html.erb
paste the following code (create the file if it does not exists):
1
DASHBOARD
This page will simply return a text DASHBOARD when /dashboard
is accessed in the browser.
Step 5: Creating a sign up page.
From the steps above, we know that the users controller handles these processes.
In your app/controllers/users_controller.rb
, inside the new method, add the following:
1
@user = User.new
In the app/views/users/new.html.erb
paste the following code (create the file if it does not exists):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<h1>Sign Up</h1>
<%= form_with :model => @user, :url => '/save-user' do |f| %>
<%= f.label :username %>
<br>
<%= f.text_field :username %>
<br>
<br>
<%= f.label :password %>
<br>
<%= f.password_field :password %>
<br>
<br>
<%= f.label :password_confirmation %>
<br>
<%= f.password_field :password_confirmation %>
<br>
<br>
<%= f.submit %>
<% end %>
This will show an HTML form when we access the link /sign-up
in our browser that will make a POST
request to /save-user
when submitted.
Now, we are going to edit the method that will handle the data from our form.
In the same controller file add the following method:
1
2
3
4
5
private
def user_params
params.require(:user).permit :username, :password, :password_confirmation
end
This is to let to know that our app only permits username
, password
, and password_confirmation
which is coming from our form to prevent malicious parameters to be processed.
In the create
method add the following code:
1
2
3
4
5
6
7
8
@user = User.new user_params
if @user.save
session[:user_id] = @user.id
redirect_to '/dashboard'
else
redirect_to '/sign-up'
end
Basically, we are just creating a new instance of User
and its parameters.
If it was saved successfully then we will set the newly registered user to be the current user and redirect it the dashboard but returns back to sign up page if the registration fails.
Step 6: Creating the sign in page.
In the app/views/sessions/new.html.erb
paste the following code (create the file if it does not exists):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<h1>Sign In</h1>
<%= form_with :url => '/' do |f| %>
<%= f.label :username %>
<br>
<%= f.text_field :username %>
<br>
<br>
<%= f.label :password %>
<br>
<%= f.password_field :password %>
<br>
<br>
<%= f.submit %>
<% end %>
Now that we created the form, we now have to authenticate the credentials if it matches a record in the database.
In your app/controllers/sessions_controller.rb
, inside the create method, add the following:
1
2
3
4
5
6
7
8
@user = User.find_by :username => params[:username]
if @user && @user.authenticate(params[:password])
session[:user_id] = @user.id
redirect_to '/dashboard'
else
redirect_to '/'
end
What the code does is that it finds the user with the supplied username and authenticates its password.
It sets the session and redirects to the dashboard if it succeeds then redirects back it to the sign in page if it doesn’t.
Step 7: Creating the sign out function.
In your app/controllers/sessions_controller.rb
, inside the destroy method, add the following:
1
2
3
session[:user_id] = nil
redirect_to '/'
We are just going to set the session[:user_id]
that we defined in the sign up and sign in back to its original value which is nil
and redirects back the user to the sign in page.
Step 8: Creating the helper methods.
In your app/controllers/application_controller.rb
, add the following:
1
2
3
4
5
6
7
8
9
helper_method :current_user
def current_user
@current_user ||= User.find_by :id => session[:user_id]
end
def authorize
redirect_to '/' unless current_user
end
A helper method is a method that we can access in our views. The helper method current user holds the data of the currently authenticated user.
The authorize method is a method that we can call to our routes that needs a user to be authenticated before allowing access to a resource.
In this case, we defined /dashboard
to be the only route that needs authentication before being accessed.
In your app/controllers/dashboard_controller.rb
, add the following:
1
2
3
4
5
6
class DashboardController < ApplicationController
def index
authorize
# YOUR OTHER CODE HERE
end
end
This simply calls authorize method and redirects back to the user to the sign in page if no user is authenticated.
If you want the whole controller to be authenticated instead:
1
2
3
4
5
6
7
8
9
10
11
12
class DashboardController < ApplicationController
before_action :authorize
def index
end
def method1
end
def method2
end
end
This will authenticate not just the index method but also method1
and method2
.
Step 9: Adding the links in the view.
We created all this routes, pages, and stuff but we didn’t have links to access this pages at all without manually typing the links in the browser.
In your app/views/layouts/application.html.erb
, add the following inside the <body>
above the <%= yield %>
:
1
2
3
4
5
6
7
<% if current_user %>
Hi <%= current_user.username %>! | <%= link_to 'Sign Out', '/sign-out', :method => :delete %>
<% else %>
<%= link_to 'Sign In', '/' %> | <%= link_to 'Sign Up', '/sign-up' %>
<% end %>
<br>
<br>
What it does is printing the name of user and a sign out link if it’s authenticated and sign in and sign up links if it’s unauthenticated.
Step 10: Installing bcrypt gem.
Our app may look complete at this point, but if you run it at this state, you will just encounter a lot of errors because we are lacking the core function of this tutorial.
In your Gemfile
uncomment the following:
1
gem 'bcrypt', '~> 3.1.7'
But if it does not exists, add it instead.
In your terminal run:
1
$ bundle install
In your app/models/user.rb
, add the following:
1
2
3
class User < ApplicationRecord
has_secure_password
end
Step 11: Run the app.
In your terminal run:
1
$ rails s
You should be able to see the sign in page as your startup page if you access http://localhost:3000/ in your browser.
That’s it. This is just a basic authentication in Rails for learning purposes.