Warden
Authentication Library
Warden
Overview
Warden is a general Rack authentication framework for Ruby applications. It operates as Rack middleware and provides a modular and flexible authentication system using the strategy pattern. It serves as the foundation for Rails authentication libraries like Devise and enables low-level authentication control.
Details
Warden was created by Daniel Neighman in 2009 as a Rack authentication framework and continues to be actively developed. Warden::Manager serves as the entry point, integrating directly into the Rack application stack. For each request, a Warden::Proxy object is injected into the Rack environment to handle authentication processing.
It adopts the Strategy pattern, allowing developers to define various authentication mechanisms as independent strategies. All strategies must inherit from Warden::Strategies::Base and implement the authenticate! method. Strategies can return multiple outcomes such as success!, fail!, fail, redirect!, custom!, and pass, enabling flexible authentication flows.
Session management functionality is built-in, using Warden::SessionSerializer to handle serialization/deserialization of user objects. Additionally, a powerful hook system including after_set_user, after_authentication, before_logout, and on_request allows custom code execution at various points in the authentication lifecycle.
Advantages and Disadvantages
Advantages
- Rack-level integration: Authentication can be accessed beyond the application layer, including mountable engines and routing constraints
- Framework agnostic: Being Rack-based, it can be used with Ruby frameworks other than Rails
- Advanced modular design: Use only necessary features, avoiding bloat from unnecessary functionality
- Strategy chaining: Easy combination of multiple authentication strategies like password, OmniAuth, and Basic authentication
- Fine-grained control: Low-level API enables implementation of authentication flows that don't fit standard patterns
- Rich hooks: Custom processing can be inserted at each stage of the authentication process
Disadvantages
- High learning curve: Complex setup and concepts can be challenging for beginners
- Low-level implementation: More manual configuration compared to Devise, not suitable for rapid development
- Limited documentation: Fewer learning resources compared to higher-level libraries
- Rails integration overhead: Additional work required for integration with Rails-specific features
- Maintainability: Low-level implementation may increase code complexity
Reference Pages
- Warden GitHub Repository
- Warden Documentation
- Devise (Warden-based)
- Simple Authentication with Warden Guide
Code Examples
Basic Setup in Rack Application
# config.ru or Rails initializer
use Warden::Manager do |config|
config.default_strategies :password
config.failure_app = lambda { |env|
[401, {'Content-Type' => 'text/plain'}, ['Unauthorized']]
}
end
# Rails configuration example
Rails.application.config.middleware.insert_after ActionDispatch::Session::CookieStore, Warden::Manager do |manager|
manager.default_strategies :password
manager.failure_app = lambda { |env|
SessionsController.action(:new).call(env)
}
end
Custom Authentication Strategy Implementation
# Password-based authentication strategy
Warden::Strategies.add(:password) do
def valid?
params[:username] && params[:password]
end
def authenticate!
user = User.find_by_username(params[:username])
if user && user.authenticate(params[:password])
success!(user)
else
fail!("Invalid credentials")
end
end
end
# Token-based authentication strategy
Warden::Strategies.add(:api_token) do
def valid?
request.headers['Authorization'].present?
end
def authenticate!
token = request.headers['Authorization'].gsub(/^Bearer /, '')
user = User.find_by_api_token(token)
if user
success!(user)
else
fail!("Invalid token")
end
end
end
Using Authentication in Controllers
class ApplicationController < ActionController::Base
# Access to Warden
def warden
env['warden']
end
# Require authentication
def authenticate_user!
warden.authenticate!
end
# Get current user
def current_user
warden.user
end
# Check if user is signed in
def user_signed_in?
warden.authenticated?
end
end
class SessionsController < ApplicationController
def create
# Execute authentication (throws exception on failure)
user = warden.authenticate!(:password)
redirect_to dashboard_path
end
def destroy
warden.logout
redirect_to root_path
end
end
Utilizing Hook System
# Register hooks in Warden configuration
Warden::Manager.after_set_user do |user, auth, opts|
# Code executed after user is set
Rails.logger.info "User #{user.id} logged in"
end
Warden::Manager.before_logout do |user, auth, opts|
# Code executed before logout
user.update(last_logout_at: Time.current)
end
Warden::Manager.after_authentication do |user, auth, opts|
# Code executed after successful authentication
user.increment!(:sign_in_count)
end
Test Environment Configuration
# RSpec configuration example
RSpec.configure do |config|
config.include Warden::Test::Helpers
config.before(:suite) do
Warden.test_mode!
end
config.after(:each) do
Warden.test_reset!
end
end
# Login in tests
def sign_in(user)
login_as(user, scope: :user)
end
# Logout in tests
def sign_out
logout(:user)
end
API Authentication Usage
class ApiController < ApplicationController
before_action :authenticate_api_user!
private
def authenticate_api_user!
warden.authenticate!(:api_token)
rescue Warden::NotAuthenticated
render json: { error: 'Unauthorized' }, status: 401
end
end