Rails token auth
Skill davidteren/hotwire-codex-skills/skills/rails-token-auth
Build secure DB-backed token session auth in Rails — one mechanism for web, Action Cable, and Hotwire Native, with Current attributes and secure-by-default controller concerns. Use when adding login/sessions to a Rails app, when web and native clients need to share authentication, when you need revocable server-side sessions (logout/kick a device) instead of stateless JWTs, or when auditing existing auth for user-enumeration, plaintext tokens, readable cookies, or opt-in-auth mistakes. Provides templates and a security audit.From its SKILL.md
npx -y skills add davidteren/hotwire-codex-skills --skill rails-token-authAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
- runs commandsInstructs the agent to run 1 command, including `scripts/audit_token_auth.sh path/to/rails-app`.
SKILL.md
3.5 KB, 724 tokens by cl100k_base, as published. Nobody here has run it
Rails token-backed session auth
A revocable, DB-backed session model (AppSession) that authenticates web and
native clients with one mechanism — plus Current attributes and secure-by-default
controller concerns. Built without a gem (plain has_secure_password +
authenticate_by). Full design + rationale: references/token-auth-guide.md.
When to use
- Adding login/logout to a Rails app and you want it done securely from the start.
- Web + Hotwire Native (and Action Cable) need to share one auth.
- You need revocable sessions (real logout, kick a device) — not stateless JWTs.
- Auditing existing auth for the expensive mistakes.
The shape
| File | Role |
|---|---|
AppSession (app/models/app_session.rb) | one row per login; token stored as a digest (has_secure_password :token) |
User::Authentication (app/models/user/authentication.rb) | has_secure_password; authenticate_by login; mint/verify sessions |
Current (app/models/current.rb) | request-scoped user / app_session / organization |
Authenticate (app/controllers/concerns/authenticate.rb) | secure-by-default before_action; skip_authentication / allow_unauthenticated; log_in/log_out |
SessionsController | create_app_session → log_in; destroy → log_out |
Copy the templates from templates/ (they're generic, ready to adapt). Add the
migration (app_sessions: user ref + token_digest), the routes
(login/logout), and include User::Authentication + include Authenticate in
ApplicationController.
The non-negotiables (why this design)
authenticate_by, notfind_by(email:)&.authenticate— timing-safe, no account enumeration.- Token hashed at rest (
has_secure_password :token) — plaintext token lives only in the encrypted cookie. A DB leak yields no usable sessions. cookies.encryptedfor the session payload — confidential + tamper-proof.- Secure-by-default: global
before_action :authenticate; public actions opt out explicitly. A forgotten controller stays locked. - Real logout destroys the
AppSessionrow — server-side revocation.
Audit an app
scripts/audit_token_auth.sh path/to/rails-app
Checks all six properties above and flags: find_by(email:)+authenticate (enumeration),
plaintext token storage, token/session in a plain cookie, opt-in auth (no global
before_action), cookie-only logout, missing has_secure_password / password length.
Heuristic grep scan — it names its ceiling; a clean run is not a formal proof, so still
review custom logic. Verified: passes the secure Piazza app, flags a synthetic
insecure one on every check.
Testing the concerns
The Authenticate concern is tested in isolation with a TestController +
draw_test_routes harness. That harness has a Rails 8 LazyRouteSet flake — use the
fixed helper and the detector from the rails-8-upgrade skill.
What ships with it: 7 files
14.8 KB alongside SKILL.md, 1 of them executable
references/
- token-auth-guide.md5.0 KB
scripts/
- audit_token_auth.shruns4.6 KB
templates/
- app_session.rb.tmpl961 B
- authenticate.rb.tmpl2.1 KB
- current.rb.tmpl295 B
- sessions_controller.rb.tmpl862 B
- user_authentication.rb.tmpl1.1 KB
Gives 0 of the 12 instructions most security skills give in 724 tokens
Counted across 666 of the 889 authors here whose files we hold, read 2026-09-06
- Use parameterized queries for database accessin 82 of 666, across 79 files
- Hash passwords with BCryptin 55 of 666, across 39 files
- Implement rate limiting for public endpointsin 48 of 666, across 34 files
- Use environment variables for secretsin 35 of 666
- Scan dependencies for vulnerabilitiesin 35 of 666, across 24 files
- Validate and sanitize all user inputin 35 of 666, across 32 files
- Add security headers to all responsesin 34 of 666, across 20 files
- Validate all external input at the system boundaryin 26 of 666, across 25 files
- Use parameterized queries to prevent SQL injectionin 25 of 666, across 13 files
- Store secrets in Vault or environment variablesin 25 of 666, across 10 files
- Run containers as a non-root userin 21 of 666, across 18 files
- Validate all input using Bean Validationin 19 of 666, across 5 files
Said here and by no other author read
- use authenticate_by for timing-safe authentication
- store token digests in the database
- implement global before_action for authentication
- require explicit opt-out for public controller actions
- destroy session records on logout
- copy templates from the provided directory
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.