Veri is a cookie-based authentication library for Ruby on Rails. It provides only essential building blocks for secure user authentication without cluttering your app with generated controllers, views, and mailers. This makes it ideal for building custom authentication flows.
Veri supports multi-tenancy, granular session management, multiple password hashing algorithms, and provides a user impersonation feature for administration purposes.
Example of Usage:
Consider a multi-tenant SaaS application where users need to manage their active sessions across devices and browsers, terminating specific sessions remotely when needed. Administrators require similar capabilities in their admin panel, with additional powers to lock accounts and temporarily assume user identities for troubleshooting. You can build all this easily with Veri.
Gem Usage:
- Installation
- Configuration
- Password Management
- Controller Integration
- Authentication Sessions
- Account Lockout
- Multi-Tenancy
- User Impersonation
- View Helpers
- Testing
Community Resources:
Add Veri to your Gemfile:
gem "veri"Install the gem:
bundle installGenerate the migration for your user model (replace users with your table name if different):
# For standard integer IDs
rails generate veri:authentication users
# For UUID primary keys
rails generate veri:authentication users --uuidRun the migration:
rails db:migrateConfigure Veri in an initializer if customization is needed:
# These are the default values; you can change them as needed
Veri.configure do |config|
config.hashing_algorithm = :argon2 # Password hashing algorithm (:argon2, :bcrypt, :pbkdf2, or :scrypt)
config.inactive_session_lifetime = nil # Session inactivity timeout (nil means sessions never expire due to inactivity)
config.total_session_lifetime = 14.days # Maximum session duration regardless of activity
config.user_model_name = "User" # Your user model name
endYour user model is automatically extended with password management methods:
# Set or update a password
user.update_password("password")
# Verify a password
user.verify_password("password")Include the authentication module in your controllers and configure protection:
class ApplicationController < ActionController::Base
include Veri::Authentication
with_authentication # Require authentication by default
end
class PicturesController < ApplicationController
skip_authentication only: [:index, :show] # Allow public access to index and show actions
endBoth with_authentication and skip_authentication work exactly the same as Rails' before_action and skip_before_action methods.
This is a simplified example of how to use Veri's authentication methods:
class RegistrationsController < ApplicationController
skip_authentication
def create
user = User.new(user_params)
if user.valid?
user.update_password(user_params[:password])
log_in(user)
redirect_to dashboard_path
else
render :new, status: :unprocessable_content
end
end
end
class SessionsController < ApplicationController
skip_authentication except: [:destroy]
def create
user = User.find_by(email: params[:email])
if user&.verify_password(params[:password])
log_in(user)
redirect_to return_path || dashboard_path
else
flash.now[:alert] = "Invalid credentials"
render :new, status: :unprocessable_content
end
end
def destroy
log_out
redirect_to root_path
end
endAvailable controller methods:
# Returns authenticated user or nil
current_user
# Returns true if user is authenticated
logged_in?
# Authenticates user and creates session, returns true on success or false if account is locked
log_in(user)
# Terminates current session
log_out
# Returns path user was trying to access before authentication, if any
return_path
# Returns current authentication session
current_sessionBy default, when unauthenticated, Veri redirects back (HTML) or returns 401 (other formats). Override this private method to customize behavior for unauthenticated users:
class ApplicationController < ActionController::Base
include Veri::Authentication
with_authentication
private
def when_unauthenticated
redirect_to login_path
end
endThe when_unauthenticated method can be overridden in any controller to provide controller-specific handling.
Veri stores authentication sessions in the database, providing session management capabilities:
# Get all sessions for a user
user.sessions
# Get current session in controller
current_session# Get the authenticated user
session.identity
# Get session details
session.info
# => {
# device: "Desktop",
# os: "macOS",
# browser: "Chrome",
# ip_address: "1.2.3.4",
# last_seen_at: "2023-10-01 12:00:00"
# }# Session is active (neither expired nor inactive)
session.active?
# Session exceeded inactivity timeout
session.inactive?
# Session exceeded maximum lifetime
session.expired?
# Fetch active sessions
Veri::Session.active
# Fetch inactive sessions
Veri::Session.inactive
# Fetch expired sessions
Veri::Session.expired# Terminate a specific session
session.terminate
# Terminate all sessions
Veri::Session.terminate_all
# Terminate all sessions for a specific user
user.sessions.terminate_all
# Clean up expired/inactive sessions, and sessions with deleted tenant
Veri::Session.prune
# Clean up expired/inactive sessions for a specific user
user.sessions.pruneVeri provides account lockout functionality to temporarily disable user accounts.
# Lock a user account
user.lock!
# Unlock a user account
user.unlock!
# Check if account is locked
user.locked?
# Fetch locked users
User.locked
# Fetch unlocked users
User.unlockedWhen an account is locked, the user cannot log in. If they're already logged in, their sessions are terminated and they are treated as unauthenticated.
Veri supports multi-tenancy, allowing you to isolate authentication sessions between different tenants such as organizations, clients, or subdomains.
By default, Veri assumes a single-tenant setup where current_tenant returns nil. Tenants can be represented as either a string or an ActiveRecord model instance.
To isolate authentication sessions between different tenants, override the current_tenant method:
class ApplicationController < ActionController::Base
include Veri::Authentication
with_authentication
private
def current_tenant
# Option 1: String-based tenancy (e.g., subdomain)
request.subdomain
# Option 2: Model-based tenancy (e.g., organization)
Company.find_by(subdomain: request.subdomain)
end
endSessions expose their tenant through the tenant method:
# Returns the tenant (string, model instance, or nil in single-tenant applications)
session.tenantTo manage sessions for a specific tenant:
# Fetch all sessions for a given tenant
Veri::Session.in_tenant(tenant)
# Fetch sessions for a specific user within a tenant
user.sessions.in_tenant(tenant)
# Terminate all sessions for a specific user within a tenant
user.sessions.in_tenant(tenant).terminate_allWhen you rename or remove models used as tenants, you need to update Veri's stored data accordingly. Use these irreversible data migrations:
# Rename a tenant class (e.g., when you rename your Organization model to Company)
migrate_authentication_tenant!("Organization", "Company")
# Remove orphaned tenant data (e.g., when you delete the Organization model entirely)
delete_authentication_tenant!("Organization")
⚠️ Known Issues
- User Impersonation in multitenant environments has security implications
- Use only in single-tenant applications
- Full fix coming in v2.0
Veri provides user impersonation functionality that allows administrators to temporarily assume another user's identity:
module Admin
class ImpersonationController < ApplicationController
def create
user = User.find(params[:user_id])
current_session.shapeshift(user)
redirect_to root_path, notice: "Now viewing as #{user.name}"
end
def destroy
original_user = current_session.true_identity
current_session.to_true_identity
redirect_to admin_dashboard_path, notice: "Returned to #{original_user.name}"
end
end
endAvailable session methods:
# Assume another user's identity (maintains original identity)
session.shapeshift(user)
# Return to original identity
session.to_true_identity
# Returns true if currently shapeshifted
session.shapeshifted?
# Returns original user when shapeshifted, otherwise current user
session.true_identityController helper:
# Returns true if the current session is shapeshifted
shapeshifter?Access authentication state in your views:
<% if logged_in? %>
<p>Welcome, <%= current_user.name %>!</p>
<% if shapeshifter? %>
<p><em>Currently viewing as <%= current_user.name %> (Original: <%= current_session.true_identity.name %>)</em></p>
<%= link_to "Return to Original Identity", revert_path, method: :patch %>
<% end %>
<%= link_to "Logout", logout_path, method: :delete %>
<% else %>
<%= link_to "Login", login_path %>
<% end %>Veri doesn't include test helpers, but you can easily create your own:
module AuthenticationHelpers
def log_in(user)
password = "test_password"
user.update_password(password)
post login_path, params: { email: user.email, password: }
end
def log_out
delete logout_path
end
end
# In your spec_helper.rb
RSpec.configure do |config|
config.include AuthenticationHelpers, type: :request
endmodule AuthenticationHelpers
def log_in(user)
controller.log_in(user)
end
def log_out
controller.log_out
end
end
# In your spec_helper.rb
RSpec.configure do |config|
config.include AuthenticationHelpers, type: :controller
endHave a question or need assistance? Open a discussion in our discussions section for:
- Usage questions
- Implementation guidance
- Feature suggestions
Found a bug? Please create an issue with:
- A clear description of the problem
- Steps to reproduce the issue
- Your environment details (Rails version, Ruby version, etc.)
Ready to contribute? You can:
- Fix bugs by submitting pull requests
- Improve documentation
- Add new features (please discuss first in our discussions section)
Before contributing, please read the contributing guidelines
The gem is available as open source under the terms of the MIT License.
Everyone interacting in the Veri project is expected to follow the code of conduct.