Authentication

Lean CMS owns the authentication flow. Login, password reset, and magic-link account setup are served from the gem at /lean-cms/login, /lean-cms/reset-password, and /lean-cms/setup-password/:token. Your app provides the User model; the gem provides everything else.

URLs

Path Purpose
GET /lean-cms/login Login form
POST /lean-cms/login Submit credentials
DELETE /lean-cms/login Sign out
GET /lean-cms/reset-password Forgot-password form
POST /lean-cms/reset-password Send reset email
GET /lean-cms/reset-password/:token/edit Set new password via signed token
GET /lean-cms/setup-password/:token Magic-link account setup (new invitations and admin-triggered resets)

URL helpers are namespaced: lean_cms_new_session_path, lean_cms_new_password_path, lean_cms_password_setup_path, etc.

Including the concern

Add include LeanCms::Authentication to your ApplicationController:

class ApplicationController < ActionController::Base
  include LeanCms::Authentication
  # …
end

This installs before_action :require_authentication globally, plus the authenticated?, current_user, start_new_session_for, and terminate_session helpers. Public-facing controllers should opt out per action or wholesale:

class PagesController < ApplicationController
  allow_unauthenticated_access
end

class CheckoutController < ApplicationController
  allow_unauthenticated_access only: :pricing
end

What the User model must provide

The gem stores LeanCms.user_class.constantize and calls these methods on it:

Class method Used by
authenticate_by(email_address:, password:) LeanCms::SessionsController#create — provided automatically by has_secure_password.
find_by(email_address:) LeanCms::PasswordsController#create
find_by_password_reset_token!(token) LeanCms::PasswordsController#edit/update — provided by has_secure_password reset_token: true (default in Rails 8).
Instance method Used by
active? Sessions controller — deactivated users cannot log in.
must_change_password? Sessions controller — forces password reset on next login.
record_login! Called after successful login.
has_any_cms_permission? LeanCms::Authorization — gates /lean-cms/* admin routes.
can_edit_pages?, can_edit_blog?, can_manage_users?, can_access_settings? Specific admin areas.

(Lean CMS does not require has_many :sessions or has_many :magic_links on User — it uses LeanCms::Session and LeanCms::MagicLink directly. That keeps it compatible with Rails 8’s built-in auth, which already defines has_many :sessions on its generated User.)

Minimum schema for the host users table:

create_table :users do |t|
  t.string :email_address, null: false
  t.string :password_digest, null: false
  t.string :name
  t.boolean :active, default: true, null: false
  t.boolean :must_change_password, default: false, null: false
  t.datetime :last_login_at
  # Permission flags
  t.boolean :is_super_admin, default: false
  t.boolean :can_edit_pages, default: false
  t.boolean :can_edit_blog, default: false
  t.boolean :can_manage_users, default: false
  t.boolean :can_access_settings, default: false
  t.timestamps
  t.index :email_address, unique: true
end

And the model:

class User < ApplicationRecord
  has_secure_password
  # … your permission predicates here (see installation guide) …
end

Sessions

LeanCms::Session is a thin Rails 8 session record (id, user reference, ip_address, user_agent, timestamps). Sessions are tied to a signed session_id cookie set on login and destroyed on logout. They live in the lean_cms_sessions table.

To force-log-out a user (e.g. on deactivation), destroy their sessions through LeanCms::Session directly:

LeanCms::Session.where(user: user).destroy_all

(Lean CMS deliberately does not require a has_many :sessions association on User — that would collide with Rails 8 auth’s built-in has_many :sessions.)

LeanCms::MagicLink powers two flows:

  • Invitations — when a new user is created, send them a magic link to set their password and activate their account.
  • Admin-triggered password resets — admins can issue a password-reset link for any user from the CMS users page.
magic_link = LeanCms::MagicLink.create_for_invitation(user)
LeanCms::UsersMailer.invitation(user, magic_link).deliver_later

magic_link = LeanCms::MagicLink.create_for_password_reset(user)
LeanCms::UsersMailer.admin_triggered_password_reset(user, magic_link).deliver_later

Magic links are single-use, expire (invitations: 24h, resets: 2h), and are invalidated when used.

Logged-in detection in views

Inside any view, current_user is available. To show admin chrome only when the user has CMS access:

<% if current_user&.has_any_cms_permission? %>
  <div class="admin-bar"></div>
<% end %>