CtrlK
BlogDocsLog inGet started
Tessl Logo

authentication-flow

Implements authentication using Rails 8 built-in generator. Use when setting up user authentication, login/logout, session management, password reset flows, or securing controllers.

Install with Tessl CLI

npx tessl i github:fernandezbaptiste/rails_ai_agents --skill authentication-flow
What are skills?

88

Does it follow best practices?

Validation for skill structure

SKILL.md
Review
Evals

Rails 8 Authentication

Overview

Rails 8 includes a built-in authentication generator that creates a complete, secure authentication system without external gems.

Quick Start

# Generate authentication
bin/rails generate authentication

# Run migrations
bin/rails db:migrate

This creates:

  • User model with has_secure_password
  • Session model for secure sessions
  • Current model for request-local storage
  • Authentication concern for controllers
  • Session and Password controllers
  • Login/logout views

Generated Structure

app/
├── models/
│   ├── user.rb              # User with has_secure_password
│   ├── session.rb           # Session tracking
│   └── current.rb           # Current.user accessor
├── controllers/
│   ├── sessions_controller.rb      # Login/logout
│   ├── passwords_controller.rb     # Password reset
│   └── concerns/
│       └── authentication.rb       # Auth helpers
└── views/
    ├── sessions/
    │   └── new.html.erb     # Login form
    └── passwords/
        ├── new.html.erb     # Forgot password
        └── edit.html.erb    # Reset password

Core Components

User Model

# app/models/user.rb
class User < ApplicationRecord
  has_secure_password
  has_many :sessions, dependent: :destroy

  normalizes :email_address, with: -> { _1.strip.downcase }

  validates :email_address, presence: true, uniqueness: true,
            format: { with: URI::MailTo::EMAIL_REGEXP }
end

Session Model

# app/models/session.rb
class Session < ApplicationRecord
  belongs_to :user

  before_create { self.token = SecureRandom.urlsafe_base64(32) }

  def self.find_by_token(token)
    find_by(token: token) if token.present?
  end
end

Current Model

# app/models/current.rb
class Current < ActiveSupport::CurrentAttributes
  attribute :session
  delegate :user, to: :session, allow_nil: true
end

Authentication Concern

# app/controllers/concerns/authentication.rb
module Authentication
  extend ActiveSupport::Concern

  included do
    before_action :require_authentication
    helper_method :authenticated?
  end

  class_methods do
    def allow_unauthenticated_access(**options)
      skip_before_action :require_authentication, **options
    end
  end

  private

  def authenticated?
    Current.session.present?
  end

  def require_authentication
    resume_session || request_authentication
  end

  def resume_session
    if session_token = cookies.signed[:session_token]
      if session = Session.find_by_token(session_token)
        Current.session = session
      end
    end
  end

  def request_authentication
    redirect_to new_session_path
  end

  def start_new_session_for(user)
    session = user.sessions.create!
    cookies.signed.permanent[:session_token] = { value: session.token, httponly: true }
    Current.session = session
  end

  def terminate_session
    Current.session&.destroy
    cookies.delete(:session_token)
  end
end

Usage Patterns

Protecting Controllers

class ApplicationController < ActionController::Base
  include Authentication

  # All actions require authentication by default
end

class PostsController < ApplicationController
  # All actions protected
end

class HomeController < ApplicationController
  # Allow public access to specific actions
  allow_unauthenticated_access only: [:index, :about]
end

Accessing Current User

# In controllers
Current.user
Current.user.email_address

# In views
<%= Current.user.email_address %>

# In models (use sparingly)
Current.user

Login Flow

# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
  allow_unauthenticated_access only: [:new, :create]

  def new
  end

  def create
    if user = User.authenticate_by(email_address: params[:email_address],
                                    password: params[:password])
      start_new_session_for(user)
      redirect_to root_path, notice: "Signed in successfully"
    else
      flash.now[:alert] = "Invalid email or password"
      render :new, status: :unprocessable_entity
    end
  end

  def destroy
    terminate_session
    redirect_to root_path, notice: "Signed out"
  end
end

Testing Authentication

Request Specs

# spec/requests/sessions_spec.rb
RSpec.describe "Sessions", type: :request do
  let(:user) { create(:user, password: "password123") }

  describe "POST /session" do
    context "with valid credentials" do
      it "signs in the user" do
        post session_path, params: {
          email_address: user.email_address,
          password: "password123"
        }

        expect(response).to redirect_to(root_path)
        expect(cookies[:session_token]).to be_present
      end
    end

    context "with invalid credentials" do
      it "shows error" do
        post session_path, params: {
          email_address: user.email_address,
          password: "wrong"
        }

        expect(response).to have_http_status(:unprocessable_entity)
      end
    end
  end

  describe "DELETE /session" do
    it "signs out the user" do
      # First sign in
      post session_path, params: {
        email_address: user.email_address,
        password: "password123"
      }

      delete session_path

      expect(response).to redirect_to(root_path)
    end
  end
end

Test Helper

# spec/support/authentication_helpers.rb
module AuthenticationHelpers
  def sign_in(user)
    session = user.sessions.create!
    cookies[:session_token] = session.token
  end

  def sign_out
    cookies.delete(:session_token)
  end
end

RSpec.configure do |config|
  config.include AuthenticationHelpers, type: :request
  config.include AuthenticationHelpers, type: :system
end

Protected Route Specs

# spec/requests/posts_spec.rb
RSpec.describe "Posts", type: :request do
  let(:user) { create(:user) }

  describe "GET /posts" do
    context "when not authenticated" do
      it "redirects to login" do
        get posts_path
        expect(response).to redirect_to(new_session_path)
      end
    end

    context "when authenticated" do
      before { sign_in(user) }

      it "shows posts" do
        get posts_path
        expect(response).to have_http_status(:ok)
      end
    end
  end
end

References

  • See sessions.md for session management details
  • See current.md for Current attributes patterns

Common Customizations

Remember Me

def start_new_session_for(user, remember: false)
  session = user.sessions.create!
  cookie_options = { value: session.token, httponly: true }
  cookie_options[:expires] = 2.weeks.from_now if remember
  cookies.signed.permanent[:session_token] = cookie_options
  Current.session = session
end

Multiple Sessions Tracking

# In User model
def active_sessions
  sessions.where('created_at > ?', 30.days.ago)
end

def terminate_all_sessions_except(current_session)
  sessions.where.not(id: current_session.id).destroy_all
end

Rate Limiting

# app/controllers/sessions_controller.rb
rate_limit to: 10, within: 3.minutes, only: :create,
           with: -> { redirect_to new_session_path, alert: "Too many attempts" }
Repository
fernandezbaptiste/rails_ai_agents
Last updated
Created

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.