skills/rails-auth-patterns/SKILL.md
Authentication patterns for Rails applications. Automatically invoked when working with user login, signup, sessions, password resets, token authentication, has_secure_password, Devise, or auth configuration. Triggers on "authentication", "auth", "login", "signup", "session", "password", "has_secure_password", "Devise", "current_user", "sign_in", "sign_out", "remember me", "password reset", "email confirmation". NOT for authorization/permissions (use controller patterns for Pundit) or API token auth (use rails-api-patterns for JWT).
npx skillsauth add ag0os/rails-dev-plugin rails-auth-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The authentication approach is an orthogonal project fact, not an architecture axis — a native project can use Devise, an extracted project can use built-in auth. Match what the project already has; never suggest replacing one approach with another unless explicitly asked.
See patterns.md for detailed code examples.
Read it from the project-conventions fingerprint (Auth category), or detect directly:
1. grep "devise" Gemfile → Devise
2. grep "has_secure_password" app/models/ → built-in auth
3. Check for app/controllers/sessions_controller.rb → custom auth
4. Rails 8+? → built-in generator available
| Approach | Use When |
|----------|----------|
| Rails 8 generator | New Rails 8+ projects, full control, no gem dependencies |
| has_secure_password (manual) | Simple auth, full control, pre-Rails 8 |
| Devise | Multi-feature auth (confirmable, lockable, omniauthable) |
bin/rails generate authentication
# Creates: User model, Session model, SessionsController,
# Authentication concern, PasswordsController, PasswordsMailer, migrations
generates_token_for with automatic invalidation:
class User < ApplicationRecord
has_secure_password
has_many :sessions, dependent: :destroy
normalizes :email_address, with: -> { _1.strip.downcase }
# Token auto-invalidates when password_salt changes (i.e., password changed)
generates_token_for :password_reset, expires_in: 15.minutes do
password_salt&.last(10)
end
# Token auto-invalidates when email changes
generates_token_for :email_confirmation, expires_in: 24.hours do
email_address
end
# Non-expiring token (e.g., unsubscribe links)
generates_token_for :unsubscribe
end
# Generate: user.generate_token_for(:password_reset)
# Find: User.find_by_token_for(:password_reset, token) # nil if expired/invalid
# Find!: User.find_by_token_for!(:password_reset, token) # raises if invalid
CurrentAttributes pattern:
# app/models/current.rb
class Current < ActiveSupport::CurrentAttributes
attribute :session
delegate :user, to: :session, allow_nil: true
end
Rate limiting (Rails 8 built-in):
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: "Try again later."
}
end
class PasswordsController < ApplicationController
rate_limit to: 5, within: 1.hour, only: :create, with: -> {
redirect_to new_password_url, alert: "Too many reset requests."
}
end
Multiple session management:
# Allow users to see/terminate their active sessions
def destroy_all
Current.user.sessions.where.not(id: Current.session.id).destroy_all
redirect_to sessions_path, notice: "All other sessions terminated."
end
Follow standard Devise setup/installation docs. The non-obvious parts:
Turbo compatibility (Rails 7+) — required:
# config/initializers/devise.rb
config.responder.error_status = :unprocessable_entity
config.responder.redirect_status = :see_other
config.navigational_formats = ["*/*", :html, :turbo_stream]
Custom failure app for Turbo (if redirects break):
class TurboFailureApp < Devise::FailureApp
def respond
if request_format == :turbo_stream
redirect
else
super
end
end
def skip_format?
%w[html turbo_stream */*].include?(request_format.to_s)
end
end
# config/initializers/devise.rb
config.warden do |manager|
manager.failure_app = TurboFailureApp
end
See patterns.md for OmniAuth integration and custom controller patterns.
Write tests in the project's framework — read it from the project-conventions fingerprint (Testing category). The two examples below pair an auth approach with a framework, but the framework choice is independent of the auth approach.
Built-in auth, Minitest example:
class SessionsControllerTest < ActionDispatch::IntegrationTest
test "login with valid credentials" do
user = users(:jane)
post session_url, params: { email_address: user.email_address, password: "password" }
assert_redirected_to root_path
end
test "rate limiting after 10 attempts" do
11.times { post session_url, params: { email_address: "[email protected]", password: "wrong" } }
assert_redirected_to new_session_url
follow_redirect!
assert_match "Try again later", flash[:alert]
end
end
Devise, RSpec example:
# spec/rails_helper.rb
RSpec.configure do |config|
config.include Devise::Test::IntegrationHelpers, type: :request
config.include Devise::Test::IntegrationHelpers, type: :system
end
# In specs — use sign_in helper, don't hit the login endpoint
before { sign_in user }
| Anti-Pattern | Problem | Fix |
|-------------|---------|-----|
| No rate limiting on login | Brute force attacks | rate_limit (Rails 8) or Rack::Attack |
| Password reset tokens without expiry | Token reuse attacks | expires_in: 15.minutes |
| Leaking user existence on reset | Enumeration attacks | Same response for valid/invalid emails |
| Session fixation | Hijacking | reset_session on login |
| Rolling your own token system | Crypto bugs | Use generates_token_for or Devise tokens |
When implementing auth, provide:
development
WHAT: Language-agnostic corrective guidance for the refactoring phase. WHEN: Agent is restructuring code, fixing code smells, reducing complexity, or improving maintainability. NOT FOR: Writing new features, debugging runtime errors, performance tuning, or object design decisions.
tools
Analyzes Rails view templates, partials, layouts, helpers, and form patterns for best practices. Use when reviewing ERB templates, improving view performance with fragment caching, fixing form helpers, organizing partials, adding accessibility attributes, or evaluating collection rendering. NOT for Stimulus/Turbo logic (use hotwire-patterns), controller concerns, or API-only responses.
testing
Analyzes Rails test suites and recommends testing best practices for RSpec and Minitest. Use when writing new tests, reviewing test coverage, fixing flaky tests, improving test performance, choosing between test types (unit, integration, system, request), or setting up factories and fixtures. NOT for production monitoring, deployment verification, or load/stress testing infrastructure.
development
Detects a Rails project's architecture axes — logic placement (native vs extracted) and delivery (html vs api) — so other skills load profile-appropriate guidance without inline conditionals. Use when planning architecture or when a recommendation depends on where business logic lives or whether the app renders HTML or serves JSON. NOT for test framework, job backend, cache store, or auth library choices — those are orthogonal facts detected by project-conventions.