devise
is one of popular authentication gem for rails. But, we are not using any gem here for user authentication here so that we can understand it from the base. We will be familiarized with working mechanism. Once we get basic knowledge, we can use them since they are for us.
So, lets start creating model and controllers for it.
lets generate controller for users
rails g controller home index
Output:
create app/controllers/home_controller.rb
route get 'home/index'
invoke erb
create app/views/home
create app/views/home/index.html.erb
invoke test_unit
create test/controllers/home_controller_test.rb
invoke helper
create app/helpers/home_helper.rb
invoke test_unit
The rails generate command above seem new for us. The syntax is rails g controller controller_name method_name(s)
. As seen in the output, the command generated view file for index
method inside app/views/home/index.html.erb
.
We only store the password digest in the database. For that we are using bcrypt gem
Lets add the gem to the gemfile and run bundle
gem 'bcrypt', '~> 3.1', '>= 3.1.20'
bundle install
We will be creating a scaffold of User
.
rails g scaffold user email:uniq password:digest
Scaffolding
A scaffold is a set of automatically generated files which forms the basic structure of a Rails project. These files include:
- A controller
- A model
- Views for every standard controller action (index, edit, show, new)
- A new route
- And a migration to prepare your database.
We can add additional fields to the migration file according to our requirements.
The email:uniq
stores the email address and also creates the unique database index. Incase of password_digest
, the generator creates a password_digest field which asks for an additional password_confimation in the form.
The has_secure_password
is used to encrypt and authenticate the passwords using the bcrypt
gem. It encrypts the password and provides the authenticate method to authenticate with the password.
Lets add some validations to user model.
class User < ApplicationRecord
has_secure_password
validates :email, presence: true, uniqueness: true
end
This makes sure that we have a unique email address.
We can start the rails server and create the new user and don't forget to add the routes for users.
Rails.application.routes.draw do
resources :users
end
A session is created when a user logs in and is destroyed when same user logs out.
Session
"Session" is the term used to refer to a user's time browsing a web site. It's meant to represent the time between their first arrival at a page in the site until the time they stop using the site. In practice, it's impossible to know when the user is done with the site.
Lets create a sessions controller with actions:
rails g controller sessions new create destroy
and in sessions_controller we have
class SessionsController < ApplicationController
def new
end
def create
end
def destroy
end
end
Let's find a user using their email and if the user is found from it's email and the password they have given is correct, create a new session for the user and redirect the user to root path.
# code above here remain unchanged
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to root_url, notice: "Logged in!"
else
flash.now[:alert] = "Email or password is invalid"
render :new, status: :unprocessable_entity
end
end
# code below here remain unchanged
To destroy the user session, we simply set the session for user to nil and redirect to root path.
def destroy
session[:user_id] = nil
redirect_to root_url, notice: "Logged out!"
end
and the new form for sessions should look like this:
<% if alert %>
<div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
<span class="font-medium"><%= alert %></span>
</div>
<% end %>
<h4>Login</h4>
<%= form_tag sessions_path do |form| %>
<div>
<%= label_tag :email %>
<%= text_field_tag :email %>
</div>
<div>
<%= label_tag :password %>
<%= password_field_tag :password %>
</div>
<div>
<%= submit_tag "Login" %>
</div>
<% end %>
now lets add the routes for sessions as well
Rails.application.routes.draw do
root 'home#index'
resources :users
resources :sessions, only: [:new, :create, :destroy]
get 'signup', to: 'users#new', as: 'signup'
get 'login', to: 'sessions#new', as: 'login'
delete 'logout', to: 'sessions#destroy', as: 'logout'
end
The user can use http://localhost:3000/login
to login and http://localhost:3000/logout
to logout easily.
current_user
current_user is a helper method that is used to get the logged in user in most rails applications.
We can use this method by adding it in the application_controller.
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
helper_method :current_user
def current_user
if session[:user_id]
@current_user ||= User.find(session[:user_id])
else
@current_user = nil
end
end
end
We will be using this method in our app.