Here is how you can implement session-based authentication functionality in your rails application without using any gem.
Create author resources
Run the commands below.
$ rails generate model Author $ rails generate controller Authors name:string email:string password_digest:string $ rails generate migration add_index_to_authors_email // Add index $ rake db:migrate
Set validations
Add validations for name
and email
.
# models/author.rb class Author < ApplicationRecord VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i.freeze validates :name, presence: true, length: { maximum: 50 } validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } end
Add secure password to Author
You'll have your users put the password and its confirmation in the form and send them as hashed values. (Hash values can not be decrypted even though they got intercepted by a third party during the transmission.)
You check if the sent hashed value matches the hashed password stored in the DB. And if it does, you allow your user to log in to the application.
Add has_secure_password
It's quite easy to set up in rails. Simply put has_secure_password
in the Author model. (Also add the minimum length of each password.)
class Author < ApplicationRecord # # other code # validates :password, length: { minimum: 6 } has_secure_password end
has_secure_password
- Enables you to store the hashed password in your DB as password_digest
- Lets you use password and password_confirmation params and validations for them.
- Lets you use the
authenticate
method.
Add bcrypt gem
Add gem 'bcrypt'
to your Gemfile and run bundle install
.
gem 'bcrypt'
Check if it's working correctly
Run the commands in the rails console to see if you can use the authenticate
method.
The authenticate
method returns false if the given password was wrong and returns the author object if the given password was correct.
$ Author.create(name:"test", email:"test@email.com", password:"000000") $ Author.first.authenticate('test') //=> false $ Author.first.authenticate('000000') //=> #<Author:0x0000560ee2e0a1b8 id: 1, name: "test", email: "test@email.com", password_digest: "$2a$12$bQQu49N3xNCKO8StooXLBOqwwCAv7NbPqt3aG35AFDHRUgh.C8BgO", created_at: Mon, 30 Sep 2019 08:40:11 UTC +00:00, updated_at: Mon, 30 Sep 2019 08:40:11 UTC +00:00>
Sign up functionality
Let's start by setting up the routes for users to sign up.
Rails.application.routes.draw do resources :authors get '/signup', to: 'authors#new' post '/signup', to: 'authors#create'
Add the code below to the author controller.
class AuthorsController < ApplicationController def show @author = Author.find(params[:id]) end def new @author = Author.new end def create @author = Author.new(author_params) if @author.save redirect_to @author else render 'new' end end private def author_params params.require(:author).permit(:name, :email, :password, :password_confirmation) end end
Lastly, create a signup page and show page for each user under views/authors/
.
# views/authors/show.html.erb <%= @author.name %> <%= @author.email %>
# views/authors/new.html.erb <% provide(:title, 'Sign up') %> <h1>Sign up</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_for(@author) do |f| %> <%= f.label :name %> <%= f.text_field :name, class: 'form-control' %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.label :password_confirmation, "Confirmation" %> <%= f.password_field :password_confirmation, class: 'form-control' %> <%= f.submit "Create my account", class: "btn btn-primary" %> <% end %> </div> </div>
Sign in/out
HTTP
is a stateless protocol. So we use sessions to maintain the user state.
The new
action is used to put information for a new session and create
action is used to actually create a new session. And the destroy
action is used to delete a session.
Set up routes
Set up routes for sessions
.
Rails.application.routes.draw do resources :authors # Create new users get '/signup', to: 'authors#new' post '/signup', to: 'authors#create' # Sessions get '/login', to: 'sessions#new' post '/login', to: 'sessions#create' delete '/logout', to: 'sessions#destroy' end
Create a session controller
$ rails generate controller Sessions
Add the code to SessionsController
.
class SessionsController < ApplicationController def new end def create author = Author.find_by(email: params[:session][:email].downcase) if author && author.authenticate(params[:session][:password]) log_in author redirect_to author else render 'new' end end def destroy log_out redirect_to root_url end end
And add the code to SessionHelper
and include session helper in ApplicationController
.
The session
used in the code below is the built-in session
method in Rails.
module SessionsHelper def log_in(author) session[:author_id] = author.id end def current_author @author ||= Author.find_by(id: session[:author_id]) if session[:author_id] end def logged_in? !current_author.nil? end def log_out session.delete(:author_id) @current_author = nil end end
class ApplicationController < ActionController::Base protect_from_forgery with: :exception include SessionsHelper end
Remember me functionality
First of all, add a column called remember_digest
to Author
.
$ rails generate migration add_remember_digest_to_users remember_digest:string
Update code in the Author model. Each method has its description in the code.
class Author < ApplicationRecord attr_accessor :remember_token VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i.freeze validates :name, presence: true, length: { maximum: 50 } validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } validates :password, length: { minimum: 6 } has_secure_password class << self # Return the hash value of the given string def digest(string) cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost BCrypt::Password.create(string, cost: cost) end # Return a random token def generate_token SecureRandom.urlsafe_base64 end end # Create a new token -> encrypt it -> stores the hash value in remember_digest in DB. def remember self.remember_token = Author.generate_token update_attribute(:remember_digest, Author.digest(remember_token)) end # Check if the given value matches the one stored in DB def authenticated?(remember_token) BCrypt::Password.new(remember_digest).is_password?(remember_token) end def forget update_attribute(:remember_digest, nil) end end
Update the session helper.
module SessionsHelper def log_in(author) session[:author_id] = author.id end def current_author if (author_id = session[:author_id]) @current_author ||= User.find_by(id: author_id) elsif (author_id = cookies.signed[:author_id]) author = User.find_by(id: author_id) if author && author.authenticated?(cookies[:remember_token]) log_in author @current_author = author end end end def logged_in? !current_author.nil? end # Make the author's session permanent def remember(author) author.remember cookies.permanent.signed[:author_id] = author.id cookies.permanent[:remember_token] = author.remember_token end # Delete the permanent session def forget(author) author.forget cookies.delete(:author_id) cookies.delete(:remember_token) end def log_out forget(current_author) session.delete(:author_id) @current_author = nil end end
Update the session controller.
class SessionsController < ApplicationController def new end def create author = Author.find_by(email: params[:session][:email].downcase) if author && author.authenticate(params[:session][:password]) log_in author params[:session][:remember_me] == '1' ? remember(author) : forget(author) redirect_to author else render 'new' end end def destroy log_out redirect_to root_url end end
Lastly, add remember_me
checkbox in the view.
<div class="login-form"> <h2>Log in</h2> <%= form_for(:session, url: login_path) do |f| %> <%= f.email_field :email, autofocus: true, autocomplete: "email", placeholder: 'Email', class: 'login-input'%><br/> <%= f.password_field :password, autocomplete: "current-password", placeholder: 'Password', class: 'login-input' %> <div class="check-field"> <%= f.check_box :remember_me %> <%= f.label :remember_me %> </div> <%= f.submit "Log in", class: 'btn btn-outline-primary login-btn' %> <% end %> </div>
Authorization
Add the following methods to the author controller.
class AuthorsController < ApplicationController before_action :authenticate_author ## Other code ## private def author_params params.require(:author).permit(:name, :email, :password, :password_confirmation) end def authenticate_author unless logged_in? flash[:danger] = "Please log in." redirect_to login_url end end def correct_author @author = Author.find(params[:id]) redirect_to(root_url) unless current_author?(@author) end end
Add the current_author?
method to the session helper.
module SessionsHelper def current_author?(author) author == current_author end end
That's it! Now you should have a simple authentication functionality on your rails app!
Top comments (9)
This is definitely an article with valuable information that I was looking for. So I'd love to thank you for the effort you've made in writing this information. smash karts
Cool! This is a really helpful guide for implementing session-based authentication in Rails. I've been looking for a simple tutorial like this for ages. Thanks for sharing! Internet Roadtrip
This is a really helpful guide! I've always wanted to implement session-based authentication from scratch in Rails. This makes it so much easier to understand the process. Thanks for sharing! Cheese Chompers 3D
I have used this for setting up a basic login system for a side project to help a friend run his book printing toronto. We didn't require much in terms of logging him in and keeping track of orders safely. This guide was helpful: it cleared up session-based authentications while avoiding the use of extra gems. If it's a lightweight app, you can start here and do whatever you like. I surely would suggest giving it a try, especially toward local service apps and small-time businesses.
This tutorial provides a clear and detailed approach to implementing session-based authentication in a Rails application without Football Bros using any gems, giving users a deeper understanding of the security mechanism and how the system works.
This tutorial offers a straightforward and comprehensive guide to setting up session-based authentication in a Rails application without relying on external gems. By following this step-by-step approach, users gain a thorough understanding of the underlying security mechanisms and the inner workings of the authentication system, empowering them to build and manage secure sessions effectively. Read more: football-bros.io
Management games offer the thrill of building systems and watching them thrive. While city builders and economic sims dominate, smaller-scale games like store management titles can be just as engaging. Enter drift boss —not your typical management game, but it hooks you with similar principles: timing, control, and decision-making. It's a fast-paced challenge that tests your ability to stay on track—literally. If you enjoy the addictive rhythm of resource handling, Drift Boss might just surprise you.
Thanks for sharing. lovely informative codes.. It appears that you are asking about implementing session-based authentication for a website. Cookie Clicker Unblocked
Some comments may only be visible to logged-in visitors. Sign in to view all comments.