Sign-up, log-in and sessions
After this you can answer: Why does MiniTickets use server-side sessions instead of tokens in the browser?
Picture it
Section titled “Picture it”sequenceDiagram participant B as Browser participant A as API participant V as Valkey B->>A: auth.login (email, password) A->>A: argon2 verify A->>V: SET session:hash(id), 7 days A-->>B: Set-Cookie sid=id (HttpOnly) B->>A: next request with sid A->>V: GET session:hash(id)How it works
Section titled “How it works”- Sign-up. The password is hashed with argon2id, which is slow and memory-hungry on purpose, so stolen hashes are expensive to guess.
- Log-in. The API always runs one argon2 verify, even for an unknown email, so timing does not reveal which emails exist. Log-in is rate-limited per IP.
- Session. A new random id (32 bytes) per log-in. Valkey stores
session:<sha256 of the id>, so a Valkey dump does not contain usable cookies. - Every request.
createContextreads thesidcookie and loads the session. AprotectedProcedureanswers 401 without one.
In MiniTickets
Section titled “In MiniTickets”The cookie is HttpOnly (scripts cannot read it), SameSite=Lax (not sent on cross-site POSTs)
and Secure in production (HTTPS only). Its life matches the Valkey entry: 7 days.
A user keeps at most 20 active sessions; the oldest is dropped. auth.logoutEverywhere deletes all
of them at once. Log-out deletes the one session and clears the cookie, and calling it twice is safe.
The session holds only the user id, never the role: the Roles chapter explains why.
Code: apps/api/src/queries/sessions.ts → create and
apps/api/src/trpc/context.ts → createContext.
| From → to | What happens | What allows it |
|---|---|---|
| API app → Sessions | reads the session of every request |
|
| Sessions → ElastiCache Valkeyonly when dataEnabled = true | session:<hash> keys with a 7-day TTL |
|
See it in AWS
Section titled “See it in AWS”Sessions are keys inside Valkey, so the console does not show them. ElastiCache → the cache → Events shows restarts, which log everyone out.
aws logs tail /minitickets/dev/api --since 10m --filter-pattern login
The cookie options: apps/api/src/procedures/auth.ts → authRouter.
Break it
Section titled “Break it”| Symptom | Cause | Where to look |
|---|---|---|
| Log-in works, next page says logged out | Secure cookie on a plain http:// address | Browser → Application → Cookies |
| Everyone is logged out at the same moment | Valkey restarted; sessions live only there | ElastiCache → Events |
| Log-in returns 503 for everyone | Valkey unreachable; the limiter fails closed | API log; redis-sg |
Say it in an interview
Section titled “Say it in an interview”Follow-ups
Section titled “Follow-ups”Why not a JWT?
A JWT is valid until it expires; you cannot take it back without creating a deny list, which is a server-side store again. A session in Valkey is gone the moment we delete it.
How does the cookie protect against XSS and CSRF?
HttpOnly means injected script cannot read the cookie. SameSite=Lax stops the browser sending it
on cross-site POSTs. The API also checks the Origin header on every change (Security headers).
Why a new session id on every log-in?
If an attacker planted a known id in the browser before log-in (session fixation), a new id makes their copy worthless. The old cookie’s session is deleted in the same Valkey script.
Advanced: The Valkey keys
session:<hash> holds { userId, createdAt } with a 7-day TTL. user-sessions:<userId> is a
sorted set of the user’s session hashes, used to cap them at 20 and to revoke all. A session is
only valid while it is in that index, so if Valkey evicts the index, sessions fail closed. Both
writes run in one Lua script, so they happen together.
Checkpoint
Section titled “Checkpoint”Answer these without scrolling up. If one feels hard, reread that section.
- Why argon2id, and why verify even for an unknown email?
- What does Valkey store as the key, and why not the raw id?
- What do the three cookie flags each protect against?
- What does log out everywhere do that a JWT cannot?
- What happens to sessions when Valkey restarts?
Redraw it from memory: the log-in sequence: browser, API, argon2 verify, Valkey SET, cookie, next request GET
Try this: log in on the local stack, open developer tools → Application → Cookies, and read the sid cookie's flags
Read this book online: https://book.theversehub.in/