Authentication
🔐 Authentication
has_secure_password, and Rails' default session-based approach, which works fundamentally differently from the JWT-based authentication Express Course 2 covered.
🔑 has_secure_password
has_secure_password is a single macro that adds a huge amount of functionality on top of the password_digest column: virtual password and password_confirmation attributes (never actually stored), automatic bcrypt hashing before save, and an authenticate(password) method that returns the user if the password matches, false otherwise.
🍪 SessionsController — Login as a Singular Resource
Notice @user&.authenticate(...) — the safe navigation operator from Ruby Course 2, Chapter 8, guarding against a nil user (no matching email) before calling authenticate on it. resource :session (singular, no s) is Rails' convention for a resource where there's only ever one — a session either exists for the current browser or it doesn't, so there's no :id in its routes.
⚖️ Sessions vs JWT — A Genuinely Different Architecture
Recall Express Course 2's authentication chapter: a JWT is generated on login, signed, and handed to the client, which then attaches it to every subsequent request — the server verifies the signature and never needs to store anything about "who's logged in." Rails' default session[:user_id] = user.id works the opposite way:
JWT (Express) — stateless
The token itself contains the user's identity, cryptographically signed. The server verifies the signature on each request but stores nothing between requests — "statelessness" is the whole point, which is why JWTs scale easily across multiple servers with no shared session store.
Session cookie (Rails default) — stateful
session[:user_id] is stored in an encrypted, signed cookie sent to the browser — by default Rails keeps the session data itself inside that cookie (ActionDispatch::Session::CookieStore), tamper-proof via a secret key, but still fundamentally tied to "the browser holds a reference the server trusts."
This is the tradeoff the completed Authentication & Session Security course already covered in the abstract, now concrete: a Rails session can be invalidated instantly and server-side — change the session secret, or (with a database-backed session store) simply delete the session record, and that user is logged out immediately. A JWT, once issued, remains valid until it expires no matter what the server does, unless you build a separate revocation/blocklist mechanism — the "statelessness" that makes JWTs easy to scale is the exact same property that makes them hard to revoke early.
👤 current_user and Protecting Routes
current_user uses the ||= memoization pattern from both Ruby courses — computed once per request, cached in @current_user for any further calls in the same request. before_action :require_login is Rails' controller-scoped equivalent of Express's authentication middleware, running before the specified actions and redirecting away if the check fails — a genuinely close conceptual match between the two frameworks, unlike most of this course's other comparisons.
📜 Authentication, Side by Side
Express (JWT) vs Rails (Session)
| Concern | Express (typical JWT) | Rails (default sessions) |
|---|---|---|
| Where identity lives | Inside the signed token itself | Server-trusted reference in an encrypted cookie |
| Server storage needed? | No (stateless) | No, by default (cookie store) — but can be database-backed |
| Instant revocation | Hard — needs a separate blocklist | Easy — invalidate server-side |
| Route protection | Auth middleware (express2-1) | before_action :require_login |
| Password hashing | bcrypt, called manually | bcrypt, via has_secure_password |
💻 Coding Challenges
Challenge 1: A User Model
Write the migration command and resulting migration file for a User with email and password_digest, then write the User model with has_secure_password and a uniqueness validation on email.
Goal: Set up the full has_secure_password foundation from scratch.
Challenge 2: SessionsController
Write the full SessionsController with new, create, and destroy actions, following this chapter's pattern — including the safe-navigation authenticate check and the correct session key assignment on login/logout.
Goal: Practice the complete login/logout flow end to end.
Challenge 3: Protecting a Controller
Write current_user and require_login in ApplicationController, then add before_action :require_login to a CommentsController so that only index and show are accessible without logging in.
Goal: Practice guarding specific controller actions the way express2-1's middleware guarded specific Express routes.
JWTs win when an API needs to scale across many stateless servers with no shared session store, or serve non-browser clients (mobile apps, other services) where cookies don't naturally fit. Rails' session-based default wins for traditional server-rendered apps like the one this course has been building — simpler to revoke, and it doesn't require the client to manage token storage or attach headers manually. Rails can do JWT-based API authentication too (relevant once Chapter 5 covers building APIs with Rails) — the session default is a starting point suited to this course's app, not a hard limitation of the framework.
🎯 What's Next
Next chapter: Authorization — now that Rails knows who is logged in, controlling what they're allowed to do, using Pundit or CanCanCan for role-based access.