Implement Rails 8 built-in authentication with has_secure_password, session cookies, and the Authentication concern...
Lightweight authentication using Rails 8's built-in features. No Devise or external gems required.
has_secure_password) and Session| Component | Purpose | Reference |
|---|---|---|
| User model | Password hashing with bcrypt | models.md |
| Session model | Track active sessions | models.md |
| Current model | Request-scoped attributes | models.md |
| Authentication concern | Controller authentication | controllers.md |
| SessionsController | Login/logout endpoints | controllers.md |
| Login view | Sign-in form | views.md |
| Migrations | Database schema | migrations.md |
| I18n translations | User-facing strings | i18n.md |
class User < ApplicationRecord
has_secure_password
has_many :sessions, dependent: :destroy
normalizes :email_address, with: ->(e) { e.strip.downcase }
validates :email_address, presence: true, uniqueness: { case_sensitive: false }
end
class Session < ApplicationRecord
belongs_to :user
end
class Current < ActiveSupport::CurrentAttributes
attribute :session
delegate :user, to: :session, allow_nil: true
end
module Authentication
extend ActiveSupport::Concern
included do
before_action :require_authentication
end
class_methods do
def allow_unauthenticated_access(**options)
skip_before_action :require_authentication, **options
end
end
private
def require_authentication
resume_session || request_authentication
end
def resume_session
Current.session ||= find_session_by_cookie
end
def find_session_by_cookie
Session.find_by(id: cookies.signed[:session_id])
end
def request_authentication
session[:return_to_after_authenticating] = request.url
redirect_to new_session_path
end
def start_new_session_for(user)
user.sessions.create!(user_agent: request.user_agent, ip_address: request.remote_ip).tap do |session|
Current.session = session
cookies.signed.permanent[:session_id] = { value: session.id, httponly: true, same_site: :lax }
end
end
def terminate_session
Current.session.destroy
cookies.delete(:session_id)
end
end
class SessionsController < ApplicationController
allow_unauthenticated_access only: %i[new create]
rate_limit to: 10, within: 3.minutes, only: :create,
with: -> { redirect_to new_session_url, alert: t("sessions.flash.rate_limit") }
def new; end
def create
if user = User.authenticate_by(params.permit(:email_address, :password))
start_new_session_for user
redirect_to after_authentication_url, notice: t("sessions.flash.create.success")
else
redirect_to new_session_path, alert: t("sessions.flash.create.error")
end
end
def destroy
terminate_session
redirect_to new_session_path, notice: t("sessions.flash.destroy.success")
end
private
def after_authentication_url
session.delete(:return_to_after_authenticating) || root_url
end
end
Rails.application.routes.draw do
resource :session # new, create, destroy
end
For complete implementation details: