Auth for Client Projects: JWT Cookies Without the Drama
LocalStorage tokens, refresh-token choreography, third-party auth platforms — most small applications need none of it. An httpOnly cookie and bcrypt cover the real requirement.
Authentication is where small projects most often import big-company complexity. Before reaching for an auth platform or a refresh-token dance, it is worth stating the actual requirement of a typical client build: an admin signs in, stays signed in for a while, and nobody else gets in.
The shape that works
- Passwords hashed with bcrypt — never stored, never logged.
- On login, sign a JWT with a server-side secret and a sensible expiry.
- Deliver it in an httpOnly, SameSite=Lax cookie — JavaScript cannot read it, so a stray XSS cannot exfiltrate it, and the browser sends it automatically.
- Protected API routes verify the token and check the role. That's the whole middleware.
Why not localStorage?
Because any script that runs on your page — including the compromised analytics snippet you pasted in 2024 — can read localStorage. The httpOnly cookie removes that entire class of theft in exchange for one CSRF consideration, which SameSite=Lax plus JSON-only endpoints largely settles for same-origin apps.
What I deliberately skip on small builds
- Refresh tokens — a 7-day expiry and a login page cost less than the choreography.
- Session stores — statelessness keeps single-server deploys simple; add Redis when you actually have two servers.
- Auth SaaS — worth it for social login and orgs; overkill for one admin panel.
Security budgets are real. Spend yours on the hash, the cookie flags, and HTTPS — not on architecture you won't operate.
Scale changes these answers. But scale is a good problem, and you will see it coming.