Auth System Checklist
The essential playbook for implementing auth system checklist in your SaaS.
Use this checklist before launch or after major auth changes. It covers the minimum production requirements for a small SaaS auth system: secure credential handling, session/token design, account recovery, email verification, route protection, cookie settings, abuse controls, logging, and deployment-specific checks.
Quick Fix / Quick Setup
Auth System Checklist
- ✓ Passwords hashed with bcrypt/argon2 only
- ✓ SECRET_KEY and auth secrets stored in env vars
- ✓ Secure cookies enabled in production
- ✓ CSRF protection enabled for session-based auth
- ✓ JWT expiry/rotation configured if using tokens
- ✓ Email verification flow works end-to-end
- ✓ Password reset tokens expire and are single-use
- ✓ Protected routes enforce auth and roles
- ✓ Rate limiting on login/register/reset endpoints
- ✓ Logout invalidates session/token correctly
- ✓ OAuth redirect URIs set for production domain
- ✓ Auth events logged without storing secrets
- ✓ Test user signup/login/reset from production domain
- ✓ Session persistence verified behind proxy/HTTPS
- ✓ Admin accounts protected with strong passwords/2FA if supported
Use this as a release gate. If any item is unchecked, treat auth as not production-ready.
Quick setup
- Define your auth model first:
- session-based auth for server-rendered apps
- JWT access + refresh tokens for API/mobile-heavy apps
- Store
SECRET_KEY, JWT signing keys, OAuth secrets, SMTP credentials, and app base URLs in environment variables only. - Enable HTTPS in production before validating cookies, OAuth callbacks, and secure session behavior.
- Set cookie flags correctly:
Secure=trueHttpOnly=trueSameSite=LaxorStrictunless cross-site flow requires otherwise
- Hash passwords with bcrypt or Argon2.
- Require email verification for new accounts if account ownership matters.
- Make password reset tokens short-lived and single-use.
- Add rate limits to login, register, reset, and verification resend endpoints.
- Verify route protection on both frontend and backend.
- Test the full auth flow on the real production domain, not only localhost or staging.
Process Flow
What’s happening
Auth failures in production usually come from environment mismatches, cookie misconfiguration, broken proxy/HTTPS handling, incorrect redirect URLs, or missing backend authorization checks.
A launch-ready auth system must handle the full account lifecycle:
- register
- verify email
- login
- resume session or refresh token
- logout
- password reset
- role checks
- abuse prevention
Production auth issues often appear only after deployment because domains, TLS, proxies, and browser cookie policies differ from local development.
Checklist-driven validation reduces common launch regressions:
- login loops
- invalid sessions
- broken OAuth callbacks
- unprotected admin endpoints
- reset links pointing to the wrong domain
Step-by-step implementation
1) Inventory auth scope
List exactly what your app supports:
- email/password login
- registration
- email verification
- password reset
- session auth or JWT auth
- OAuth/social login
- RBAC/admin roles
- billing/admin sensitive actions
Do not ship partial protection by assumption.
2) Verify secrets and environment variables
All auth secrets must come from env vars and must differ across dev, staging, and production.
printenv | grep -E 'SECRET|JWT|AUTH|APP_URL|FRONTEND|API|OAUTH'Minimum expected variables:
APP_URL=https://yourdomain.com
API_URL=https://yourdomain.com
FRONTEND_URL=https://yourdomain.com
SECRET_KEY=replace_me
JWT_SECRET=replace_me
SESSION_SECRET=replace_me
GOOGLE_CLIENT_ID=replace_me
GOOGLE_CLIENT_SECRET=replace_me
SMTP_HOST=replace_me
SMTP_USER=replace_me
SMTP_PASS=replace_meChecks:
- no hardcoded secrets in source
- no dev values reused in prod
- no accidental secret rotation during deploy
3) Review password storage
Requirements:
- bcrypt or Argon2 only
- per-password salt handled by library
- no plaintext storage
- no reversible encryption
- no unsalted hash algorithms like raw SHA256/MD5
Example guidance:
// Node.js bcrypt example
const bcrypt = require("bcrypt");
const hash = await bcrypt.hash(password, 12);
const ok = await bcrypt.compare(inputPassword, hash);// Node.js argon2 example
const argon2 = require("argon2");
const hash = await argon2.hash(password);
const ok = await argon2.verify(hash, inputPassword);Checks:
- new registrations hash correctly
- password changes hash correctly
- imports/migrations do not bypass hashing
4) Validate registration flow
Registration must handle:
- email normalization
- duplicate account detection
- password strength validation
- optional disposable email policy
- verification token creation
Checks:
User@Example.comanduser@example.comdo not create duplicate accounts unintentionally- weak passwords are rejected
- registration endpoint is rate-limited
- signup response does not leak internal state
5) Validate email verification flow
Verification must work end-to-end on the production domain.
Checks:
- token generated securely
- token expires
- token is one-time use
- verification link uses correct
APP_URL/ frontend URL - resend verification is rate-limited
- already-verified users handled cleanly
Test:
- create new user in production
- open real email
- click verification link
- confirm account state changes in DB
- confirm verified user can access required features
6) Validate password reset flow
Minimum requirements:
- request endpoint does not reveal whether user exists
- reset token expires
- reset token is single-use
- reset link uses production domain
- new password is hashed
- previous reset token becomes invalid after use
Recommended extra check:
- revoke active sessions after password reset if your app supports it
Example generic reset response:
{
"message": "If the account exists, a password reset link has been sent."
}7) Check login behavior
Verify:
- rate limiting on failed attempts
- generic error messages
- correct session or token issuance
- optional audit log of successful and failed login attempts
- no password or token values logged
Example rate limit targets:
/api/login/api/register/api/password-reset/request/api/password-reset/confirm/api/email/verify/resend
8) Check logout behavior
Logout must invalidate auth state correctly.
For session auth:
- delete server-side session
- expire auth cookie
For JWT-based auth:
- remove refresh token from store
- rotate or revoke token if your architecture supports revocation
- clear client storage if used
- ensure protected endpoints reject old auth state
9) Review session settings
For cookie/session auth, verify:
Secure=truein productionHttpOnly=trueSameSite=LaxorStrictunless required otherwise- correct
DomainandPath - explicit expiration/idle timeout
- remember-me behavior is intentional
- session store is persistent and shared when scaling
- proxy trust is configured if behind load balancer/reverse proxy
Typical failure: app thinks requests are HTTP, so secure cookies do not behave correctly.
10) Review JWT settings if used
If using JWTs, verify:
- short access token TTL
- refresh token rotation
- signing algorithm configured explicitly
issandaudvalidated- token expiration enforced
- invalidation strategy exists where needed
- server time is correct
Safe local decode command:
python -c "import jwt,sys; print(jwt.decode(sys.argv[1], options={'verify_signature': False}))" '<token>'11) Validate backend protection
Every protected endpoint must enforce auth server-side.
Check:
- private API routes reject anonymous requests
- role checks protect admin actions
- billing and internal actions require authorization
- frontend route guards are not the only control
This is the most common production auth mistake.
For implementation patterns, see:
- Implement User Authentication (Login/Register)
- Session Management vs JWT (When to Use What)
- Payment System Checklist
12) Test OAuth providers in production
Verify for each provider:
- authorized redirect URI matches production exactly
- callback handler works over HTTPS
statevalidation enabled- account linking rules are defined
- missing-email provider edge cases handled
- logout behavior understood if provider session persists
Debug commands:
curl -I -L https://yourdomain.com/auth/google/login13) Add abuse controls
Apply rate limiting and anti-abuse controls to:
- login
- registration
- verification resend
- password reset request
- password reset confirm
- OAuth start endpoints
At minimum, limit by IP. Better: combine IP + email/user fingerprint + request ID logging.
14) Log auth events safely
Log enough to debug, but never log secrets.
Good auth log fields:
- event type
- timestamp
- user ID if known
- email hash or normalized identifier if needed
- IP
- request ID
- user agent
- result code
Do not log:
- passwords
- reset tokens
- JWTs
- session IDs
- OAuth client secrets
- raw authorization headers
15) Run production smoke tests
Run all auth flows from the real production domain:
- register
- verify email
- login
- page refresh / session resume
- protected route access
- password reset request
- password reset confirm
- logout
- repeat on desktop and mobile browser
Useful commands:
curl -I https://yourdomain.com/login
curl -v https://yourdomain.com/api/me
curl -X POST https://yourdomain.com/api/login \
-H 'Content-Type: application/json' \
-d '{"email":"user@example.com","password":"testpass"}'Common causes
SECRET_KEYor JWT signing key changed unexpectedly, invalidating sessions or tokens.- Cookies not marked
Securein HTTPS production, orSameSiteblocks expected flow. - Proxy headers not trusted, causing app to think requests are HTTP instead of HTTPS.
- OAuth redirect URI mismatch between provider dashboard and production domain.
- Session storage not shared or persisted correctly across deploys or multiple instances.
- Password reset or verification links using localhost or the wrong
APP_URL. - Protected routes enforced in frontend only, not in backend API handlers.
- CSRF protection missing or misconfigured for session-based auth.
- JWT tokens missing proper expiration, audience, issuer, or rotation handling.
- Login or reset endpoints exposed without rate limiting.
- Email delivery failure prevents verification or reset completion.
- Role checks missing on admin, billing, or internal endpoints.
Debugging tips
Check production env and auth config:
printenv | grep -E 'SECRET|JWT|AUTH|APP_URL|FRONTEND|API|OAUTH'Inspect headers and cookie behavior:
curl -I https://yourdomain.com/login
curl -v https://yourdomain.com/api/meTest login endpoint directly:
curl -X POST https://yourdomain.com/api/login \
-H 'Content-Type: application/json' \
-d '{"email":"user@example.com","password":"testpass"}'Test OAuth redirect chain:
curl -I -L https://yourdomain.com/auth/google/loginDecode JWT locally without verifying signature:
python -c "import jwt,sys; print(jwt.decode(sys.argv[1], options={'verify_signature': False}))" '<token>'Inspect proxy and app logs:
grep -Ei 'auth|login|logout|reset|verify|oauth|csrf|session' /var/log/nginx/access.log
grep -Ei 'auth|login|logout|reset|verify|oauth|csrf|session|error' /var/log/nginx/error.log
journalctl -u your-app.service -n 200 --no-pager
docker logs --tail 200 your-app-containerAdditional checks:
- inspect browser devtools for blocked cookies and failed redirects
- verify server time sync if tokens are immediately expired
- check whether deploys are wiping in-memory session stores
- compare reverse proxy logs with app logs for the same request ID
For broader hardening and release validation, also review:
Checklist
- ✓ Passwords hashed with bcrypt or Argon2
- ✓ No plaintext passwords, tokens, or secrets stored or logged
- ✓ Auth secrets and OAuth credentials stored in environment variables only
- ✓ HTTPS enabled in production
- ✓ Secure and HttpOnly cookie flags enabled where applicable
- ✓ SameSite configured correctly for your auth flow
- ✓ CSRF protection enabled for session-based auth
- ✓ Session expiration and idle timeout configured
- ✓ JWT expiry and refresh strategy configured if using tokens
- ✓ Email verification works end-to-end on production domain
- ✓ Password reset tokens are expiring and single-use
- ✓ Reset request flow does not reveal whether a user exists
- ✓ Protected API and page routes enforce authentication server-side
- ✓ Role and permission checks protect admin and sensitive actions
- ✓ Rate limiting enabled on login, register, reset, and verification endpoints
- ✓ OAuth redirect URIs configured for production
- ✓ Logout properly invalidates sessions or tokens
- ✓ Session behavior verified across refreshes, subdomains, and proxies
- ✓ Auth event logs enabled without exposing sensitive values
- ✓ Manual smoke test completed for register, verify, login, reset, and logout
Product CTA
Use this page as a production release gate for auth changes.
If you maintain a small SaaS, convert this checklist into:
- deploy-blocking release criteria
- CI/CD validation steps
- smoke-test scripts
- incident review checks after auth-related changes
Use the same checklist model across auth, payments, security, and deployment so production quality is repeatable, not manual.
Relevant next checklists:
Related guides
- Implement User Authentication (Login/Register)
- Session Management vs JWT (When to Use What)
- Protecting Routes and APIs
- Security Checklist
- Payment System Checklist
FAQ
What should I verify first before launch?
Verify password hashing, cookie or token settings, route protection, email verification, password reset, logout behavior, and production-domain testing over HTTPS.
Is frontend route protection enough?
No. Every protected backend endpoint must enforce authentication and authorization independently of the frontend.
What breaks most often after deployment?
Cookie security settings, wrong callback URLs, missing proxy trust, broken email links, and non-persistent session storage.
Should auth secrets ever change?
Only through a controlled rotation process. Unplanned changes can invalidate all sessions or tokens immediately.
Do small SaaS apps need rate limiting on auth endpoints?
Yes. Login, register, reset, and verification resend endpoints should all have abuse protection in production.
Final takeaway
An auth system is production-ready only when the full user lifecycle works under real deployment conditions:
- HTTPS
- real domains
- proxies
- email delivery
- cookie policy
- backend authorization enforcement
Use this checklist as a release gate, not just documentation. Auth regressions are easier to prevent than to debug after launch.