Skip
Arish's avatar

46. Authentication Basics


Authentication in Rails

Authentication is how you verify who a user is. Rails provides tools to build authentication from scratch or use gems like Devise.

Rails 8+ Built-in Authentication

Rails 8 includes a built-in authentication generator:

bash
1rails generate authentication

This creates:

  • User model with secure password
  • Session controller
  • Password reset functionality
  • Current model for accessing the current user

Building Authentication from Scratch

1. Create User Model

bash
1rails generate model User email:string:uniq password_digest:string
2rails db:migrate

2. Add has_secure_password

ruby
1# app/models/user.rb
2class User < ApplicationRecord
3  has_secure_password
4  
5  validates :email, presence: true, 
6                    uniqueness: { case_sensitive: false },
7                    format: { with: URI::MailTo::EMAIL_REGEXP }
8  validates :password, length: { minimum: 8 }, if: :password_required?
9  
10  before_save :downcase_email
11  
12  def self.authenticate_by(email:, password:)
13    user = find_by(email: email.downcase)
14    user&.authenticate(password) || nil
15  end
16  
17  private
18  
19  def downcase_email
20    self.email = email.downcase
21  end
22  
23  def password_required?
24    password_digest.nil? || password.present?
25  end
26end

Add bcrypt to your Gemfile:

ruby
1# Gemfile
2gem 'bcrypt', '~> 3.1.7'

3. Create Sessions Controller

ruby
1# app/controllers/sessions_controller.rb
2class SessionsController < ApplicationController
3  def new
4    # Login form
5  end
6  
7  def create
8    user = User.authenticate_by(
9      email: params[:email],
10      password: params[:password]
11    )
12    
13    if user
14      session[:user_id] = user.id
15      redirect_to root_path, notice: 'Logged in successfully!'
16    else
17      flash.now[:alert] = 'Invalid email or password'
18      render :new, status: :unprocessable_entity
19    end
20  end
21  
22  def destroy
23    session.delete(:user_id)
24    redirect_to root_path, notice: 'Logged out successfully!'
25  end
26end

4. Create Registration Controller

ruby
1# app/controllers/registrations_controller.rb
2class RegistrationsController < ApplicationController
3  def new
4    @user = User.new
5  end
6  
7  def create
8    @user = User.new(user_params)
9    
10    if @user.save
11      session[:user_id] = @user.id
12      redirect_to root_path, notice: 'Account created successfully!'
13    else
14      render :new, status: :unprocessable_entity
15    end
16  end
17  
18  private
19  
20  def user_params
21    params.require(:user).permit(:email, :password, :password_confirmation)
22  end
23end

5. Add Routes

ruby
1# config/routes.rb
2Rails.application.routes.draw do
3  get 'signup', to: 'registrations#new'
4  post 'signup', to: 'registrations#create'
5  
6  get 'login', to: 'sessions#new'
7  post 'login', to: 'sessions#create'
8  delete 'logout', to: 'sessions#destroy'
9  
10  # Or RESTful:
11  resource :session, only: [:new, :create, :destroy]
12  resource :registration, only: [:new, :create]
13end

6. Application Controller Helpers

ruby
1# app/controllers/application_controller.rb
2class ApplicationController < ActionController::Base
3  helper_method :current_user, :logged_in?
4  
5  private
6  
7  def current_user
8    @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
9  end
10  
11  def logged_in?
12    current_user.present?
13  end
14  
15  def authenticate_user!
16    unless logged_in?
17      redirect_to login_path, alert: 'Please log in to continue'
18    end
19  end
20end

7. Protecting Controllers

ruby
1class ArticlesController < ApplicationController
2  before_action :authenticate_user!, except: [:index, :show]
3  
4  def new
5    @article = current_user.articles.build
6  end
7  
8  def create
9    @article = current_user.articles.build(article_params)
10    # ...
11  end
12end

Login Form

erb
1<!-- app/views/sessions/new.html.erb -->
2<h1>Log In</h1>
3
4<%= form_with url: login_path, local: true do |f| %>
5  <div class="field">
6    <%= f.label :email %>
7    <%= f.email_field :email, autofocus: true, required: true %>
8  </div>
9  
10  <div class="field">
11    <%= f.label :password %>
12    <%= f.password_field :password, required: true %>
13  </div>
14  
15  <div class="field">
16    <%= f.check_box :remember_me %>
17    <%= f.label :remember_me, "Remember me" %>
18  </div>
19  
20  <%= f.submit "Log In" %>
21<% end %>
22
23<p>
24  Don't have an account? <%= link_to "Sign up", signup_path %>
25</p>

Registration Form

erb
1<!-- app/views/registrations/new.html.erb -->
2<h1>Sign Up</h1>
3
4<%= form_with model: @user, url: signup_path do |f| %>
5  <% if @user.errors.any? %>
6    <div class="errors">
7      <% @user.errors.full_messages.each do |msg| %>
8        <p><%= msg %></p>
9      <% end %>
10    </div>
11  <% end %>
12  
13  <div class="field">
14    <%= f.label :email %>
15    <%= f.email_field :email, autofocus: true %>
16  </div>
17  
18  <div class="field">
19    <%= f.label :password %>
20    <%= f.password_field :password %>
21  </div>
22  
23  <div class="field">
24    <%= f.label :password_confirmation %>
25    <%= f.password_field :password_confirmation %>
26  </div>
27  
28  <%= f.submit "Sign Up" %>
29<% end %>
erb
1<!-- app/views/shared/_navigation.html.erb -->
2<nav>
3  <%= link_to "Home", root_path %>
4  
5  <% if logged_in? %>
6    <span>Welcome, <%= current_user.email %></span>
7    <%= link_to "Profile", profile_path %>
8    <%= button_to "Log Out", logout_path, method: :delete %>
9  <% else %>
10    <%= link_to "Log In", login_path %>
11    <%= link_to "Sign Up", signup_path %>
12  <% end %>
13</nav>

Remember Me (Persistent Session)

ruby
1class User < ApplicationRecord
2  has_secure_password
3  has_secure_token :remember_token
4end
5
6class SessionsController < ApplicationController
7  def create
8    user = User.authenticate_by(email: params[:email], password: params[:password])
9    
10    if user
11      if params[:remember_me] == "1"
12        user.regenerate_remember_token
13        cookies.permanent.encrypted[:remember_token] = user.remember_token
14      end
15      session[:user_id] = user.id
16      redirect_to root_path
17    else
18      # ...
19    end
20  end
21  
22  def destroy
23    cookies.delete(:remember_token)
24    session.delete(:user_id)
25    redirect_to root_path
26  end
27end
28
29class ApplicationController < ActionController::Base
30  def current_user
31    @current_user ||= if session[:user_id]
32      User.find_by(id: session[:user_id])
33    elsif cookies.encrypted[:remember_token]
34      User.find_by(remember_token: cookies.encrypted[:remember_token])
35    end
36  end
37end

This is the foundation for user authentication in Rails!