Sessions & devices
RailsFast ships with the sessions gem wired in by default, giving every app a GitHub-style security layer on top of Devise:
- A "Sessions & devices" page at
/settings/sessions: users see every device they're signed in on ("Chrome 137 on macOS", "MyApp 1.2 on Pixel 8"), can sign out any single device remotely, or sign out everywhere else with one click. - A login-activity trail: an append-only record of every login (successful and failed) plus logouts and revocations, with parsed device info and approximate location.
- An admin Security section in Madmin: a platform-wide device registry with per-device revoke, a per-user security panel on each user page, and the login trail: your first stop in any brute-force or account-takeover triage.
- Security emails out of the box: a "was this you?" email on the first sign-in from a never-seen device, and a "someone is trying to get into your account" email when an identity crosses 5 failed logins in 15 minutes (sent once at the crossing, never per attempt).
- A step-up reauthentication gate: revoking sessions is a sensitive action, so RailsFast asks the user to confirm it's them unless they've already done so in the last 15 minutes (
app/controllers/concerns/recent_authentication.rb). That's their password, or, for accounts created through social login that have never set one, a fresh round trip through the provider.
Everything is configured in config/initializers/sessions.rb; the file documents each decision inline. The user-facing views are ejected to app/views/sessions/ so you can restyle them like any other template view.
Re-authentication assertions (sessions 0.3, not wired yet)
sessions 0.3 can also record a fresh re-authentication on the session row (session.reauthenticated!, session.reauthenticated_within?(5.minutes)) and hand out a short-lived signed assertion of it, which another service verifies with a shared key (Sessions::Reauthentication.verify). That is for flows where a separate service must know the person just proved it is them, such as approving a payout.
The template does not use it yet. The RecentAuthentication concern above stays the authority for "did this person just confirm it is them", and the three settings that turn assertions on (reauthentication_key, reauthentication_window, reauthentication_assertion_ttl) sit commented out in config/initializers/sessions.rb. If you need them, set the key (32 bytes or more) and follow the "Re-authentication assertions" section of the sessions README. No migration is needed.
How liveness works (worth understanding)
One session row = one signed-in device. Liveness is explicit lifecycle state (sessions.ended_at / ended_reason): the gem never infers auth state from a missing row, and tracking failures can never log anyone out ("tracking never breaks login" is the gem's #1 rule). Revoking a device ends its row; that device is signed out on its next request, and its long-lived remember-me cookie is rotated too, which is what makes the native apps' 1-year sessions actually revocable.
For the full API (user.sessions.live, session.revoke!, user.revoke_all_sessions!, custom hooks), the sessions README is the source of truth.
Housekeeping
SessionsSweepJob runs daily (see config/recurring.yml): it purges trail events past the 12-month retention window and evicts sessions beyond the per-user cap. No idle timeouts are configured by default: RailsFast never silently shortens anyone's session.
Hotwire Native awareness
Native app sessions are first-class: the WebView user agent convention (MyApp/1.2 (Pixel 8; Android 16; build 14); MyApp Android; RailsFast Native Android;) and the native HTTP clients' X-Client-* headers let the registry show real app versions, builds, and device models instead of "unknown browser". This works out of the box with the RailsFast Native shells. If you never build a native app, the sessions feature works exactly the same for plain web browsers.
Native push notifications follow the session too: a device's push registration belongs to the sign-in session it was made from, so revoking that session (or any other sign-out) also stops delivery to the device. See Notifications.
IP geolocation (trackdown)
Session locations (and the signup_country / signup_city columns on users) come from the trackdown gem, which is part of the default template. In production behind Cloudflare it's zero-config and free: Cloudflare's CF-IPCountry / geo headers answer synchronously. Optionally, you can configure a MaxMind database for richer offline lookups (BYOK; see the trackdown README). In development there are usually no Cloudflare headers, so locations simply stay blank. That's expected.