visit
rails new simple_session
cd simple_session
rails generate model User name:string email:string password_digest:string
rails generate model Item name:string user:references
rails db:migrate
rails generate controller Users
rails generate controller Items
#Gemfile
gem 'bcrypt'
bundle install
#application.html.erb
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
touch app/assets/stylesheets/custom.scss
#users_controller.rb
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
end
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
flash[:success] = "Welcome to the app!"
redirect_to @user
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
#application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>RailsSession</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>
<body>
<%= render 'layouts/header' %>
<div class='container'>
<% flash.each do |message_type, message| %>
<div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>
<%= yield %>
</div>
</body>
</html>
#layouts/_header.html.erb
<nav class="navbar mb-2">
<div class="container-fluid">
<div class="navbar-header">
<%= link_to "Items", '#' %>
</div>
<ul class="nav navbar-nav navbar-right">
<li><%= link_to "Sign up", signup_path %></li>
</ul>
</div>
</nav>
#views/items/home.html.erb
<div class="text-center home">
<h2>This is a simple guide on how to create session based authentication system</h2>
<br/>
<p>
Please signup or login to test the app.
<p>
<%= link_to "Sign up", signup_path, class: "btn btn-primary" %>
</div>
#views/shared/_error_messages.html.erb
<% if @user.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(@user.errors.count, "error") %>.
</div>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<li class='text-danger'><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
#views/users/new.html.erb
<h1 class='text-center'>Sign up</h1>
<div class="row">
<div class="col-md-4 offset-4">
<%= form_with(model: @user, local: true) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'mb-1 form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'mb-1 form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'mb-1 form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'mb-1 form-control' %>
<%= f.submit "Create my account", class: "mt-2 btn btn-primary" %>
<% end %>
</div>
</div>
#model/user.rb
class User < ApplicationRecord
has_many :items
has_secure_password
before_save { self.email = email.downcase }
validates :name, presence: true
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
end
#config/routes.rb
Rails.application.routes.draw do
root 'items#home'
get '/signup', to: 'users#new'
resources :users
end
#views/users/show.html.erb
<div class="row">
<div class="offset-4 col-md-4">
<h3 class='text-center'>
<%= @user.name %>
</h3>
<h3 class='text-center'>
<%= @user.email %>
</h3>
</div>
</div>
rails generate controller Sessions
#config/routes.rb
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
#views/sessions/new.html.erb
<h1 class='text-center'>Log in</h1>
<div class="row">
<div class="col-md-6 offset-3">
<%= form_with(url: login_path, scope: :session, local: true) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.submit "Log in", class: " mt-2 btn btn-primary" %>
<% end %>
</div>
</div>
#sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
redirect_back_or user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
log_out
redirect_to root_url
end
end
#app/helpers/session_helper.rb
module SessionsHelper
def log_in(user)
session[:user_id] = user.id
end
def current_user
if session[:user_id]
@current_user ||= User.find_by(id: session[:user_id])
end
end
def logged_in?
!current_user.nil?
end
def log_out
session.delete(:user_id)
@current_user = nil
end
def current_user?(user)
user == current_user
end
def redirect_back_or(default)
redirect_to(session[:forwarding_url] || default)
session.delete(:forwarding_url)
end
def store_location
session[:forwarding_url] = request.original_url if request.get?
end
end
The rails way of creating a session is just using 'session[:user_id] = user.id'. This will create a session with the user_id. The current_user method will return the current user if there is one or if there is a session present. That means if a user is logged in, the current user will be the that user. The logged_in? method just return true or false based on whether there is a current user or not. The log_out method will log out the user by deleting the session and setting the current_user to nil. The current_user? method accepts user and returns true if it's the same with the current user or false otherwise. This can be useful to restrict a user from visiting other user's details. The next two methods are useful to redirect a non logged-in user to the first visited URL after logging in.
Then we will import the session helper in the application controller. That means methods like logged_in? or current_user will be available to all controllers. Let's do that.#application_controller.rb
include SessionsHelper
private
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
#users_controller.rb
class UsersController < ApplicationController
before_action :logged_in_user, only: [:show]
...
#users_controller.rb
def create
@user = User.new(user_params)
if @user.save
log_in @user
flash[:success] = "Welcome to the app!"
redirect_to @user
else
render 'new'
end
end
#views/layouts/_header.html.erb
<nav class="navbar mb-2">
<div class="container-fluid">
<div class="navbar-header">
<%= link_to "Items", root_path, class: "mx-2" %>
</div>
<% if logged_in? %>
<ul class="nav navbar-right ">
<li><%= link_to "Log out", logout_path, class: "mx-2", method: :delete %></li>
</ul>
<% else %>
<ul class="nav navbar-right ">
<li><%= link_to "Log in", login_path, class: "mx-2" %></li>
<li><%= link_to "Sign up", signup_path, class: "mx-2" %></li>
</ul>
<% end %>
</div>
</nav>
#app/models/item.rb
class Item < ApplicationRecord
belongs_to :user
validates :name, presence: true
end
#items_controller.rb
class ItemsController < ApplicationController
before_action :logged_in_user
def home
end
def new
@item = current_user.items.new
end
def index
@items = Item.all
end
def create
@item = current_user.items.build(item_params)
if @item.save
flash[:success] = "Item has been created!"
redirect_to @item
else
render 'new'
end
end
def edit
@item = current_user.items.find(params[:id])
end
def update
@item = current_user.items.find(params[:id])
if @item.update_attributes(item_params)
flash[:success] = "Item updated"
redirect_to @item
else
render 'edit'
end
end
def destroy
@item = current_user.items.find(params[:id])
if @item
@item.destroy
flash[:success] = "Item has been deleted"
else
flash[:alert] = "Error"
end
redirect_to root_path
end
def show
@item = Item.find(params[:id])
end
private
def item_params
params.require(:item).permit(:name)
end
end