In This Guide
Key Takeaways
- Auth is not just login: Authentication (who are you?) and authorization (what can you do?) are separate concerns. Get authentication right first, then layer authorization on top.
- Sessions vs JWTs: Cookie-based sessions are stateful (server stores session data) and easy to invalidate. JWTs are stateless (client stores the token) and easier to scale, but cannot be easily revoked. Use sessions for most web apps; JWTs for API authentication.
- Never store plaintext passwords: Always hash passwords with bcrypt, Argon2, or scrypt before storing. These algorithms are designed to be slow, which makes brute-force attacks expensive.
- Use a managed auth service: Clerk, Auth0, Supabase Auth, and NextAuth handle the hard parts — OAuth flows, token rotation, session management — correctly. Build your own auth only if you have specific requirements that managed services cannot meet.
Authentication is the part of web development that most tutorials get wrong. They show you how to implement it but skip why the specific choices matter — and those choices are the difference between a secure application and a data breach.
This guide covers the authentication mechanisms used in modern web applications: sessions, JWTs, OAuth 2.0, and the primitives underneath all of them. By the end, you will understand not just how to implement auth but why specific patterns are safer than others.
Authentication vs Authorization
Authentication answers: who is this person? It is the process of verifying that a user is who they claim to be — by checking a password, a token, a biometric, or a certificate.
Authorization answers: what is this authenticated person allowed to do? It is the process of checking whether the authenticated user has permission to access a specific resource or perform a specific action.
Most web applications confuse the two because they happen sequentially and close together. But they are distinct concerns with distinct implementations. Authentication determines identity. Authorization enforces policy. You can be authenticated but not authorized — logged in as a real user, but not permitted to access the admin panel.
The auth flow for a typical web request: authenticate (verify the session cookie or JWT token), identify (load the user record from the token/session), authorize (check if this user has permission for this specific action on this specific resource), then handle the request.
Cookie-Based Sessions: How They Work
Cookie-based sessions are the traditional web authentication mechanism. The server creates a session record in a data store (database, Redis, memory), assigns it a random session ID, and sets that ID as a cookie in the browser. On subsequent requests, the browser sends the cookie, the server looks up the session, and the session contains the user's identity and any other session data.
The flow:
- User submits login form with email/password
- Server verifies credentials, creates a session record (session_id, user_id, created_at, expires_at) in Redis or database
- Server sets a
Set-Cookie: session=SESSION_ID; HttpOnly; Secure; SameSite=Laxheader - Browser stores the cookie and sends it with every subsequent request
- Server reads the session ID from the cookie, looks up the session, retrieves the user_id, and proceeds
Security properties of cookies:
HttpOnly: prevents JavaScript from reading the cookie, protecting against XSS cookie theftSecure: cookie only sent over HTTPS, never HTTPSameSite=Lax: prevents the cookie from being sent on cross-site requests (protects against CSRF for most cases)
Session invalidation is simple: delete the session record from the server. The session ID in the user's cookie becomes useless immediately. This is the key advantage of server-side sessions over JWTs.
JWTs: Benefits, Pitfalls, and When to Use Them
A JSON Web Token (JWT) is a self-contained token that carries the user's claims (user ID, email, roles) signed with a secret key. Unlike sessions, the server does not store the token — the client stores it. The server verifies the signature and reads the claims on every request.
A JWT has three parts separated by dots: header.payload.signature. The header specifies the algorithm (HS256, RS256). The payload contains claims (user_id, email, exp). The signature is a cryptographic hash of the header + payload using the server's secret key. Tampering with the payload invalidates the signature.
// JWT payload example (decoded) { "sub": "user_123", "email": "[email protected]", "role": "admin", "iat": 1712800000, "exp": 1712803600 }
JWT benefits: Stateless (no server-side storage), works well for APIs (mobile apps, third-party clients), enables microservices architectures where each service can verify the token independently.
JWT pitfalls:
- Cannot be revoked before expiration without a blocklist (which makes them semi-stateful)
- Sensitive data in the payload is base64-encoded, not encrypted — anyone can read it
- Long-lived JWTs are a security risk if leaked — use short expiry (15-30 minutes) + refresh tokens
alg: nonevulnerability: always verify the algorithm in the token header matches what you expect
When to use JWTs: API authentication for mobile apps, microservices authorization, and stateless verification across service boundaries. For traditional web applications with server-rendered pages, cookie sessions are simpler and easier to reason about.
OAuth 2.0 and OpenID Connect Explained
OAuth 2.0 is an authorization framework that allows a user to grant a third-party application access to their resources at another service, without sharing their password. "Sign in with Google" is OAuth 2.0. OpenID Connect (OIDC) is an authentication layer built on top of OAuth 2.0 that also provides identity information (who the user is).
The Authorization Code Flow (the most secure flow for web apps):
- User clicks "Sign in with Google"
- Your app redirects to
accounts.google.com/o/oauth2/auth?client_id=...&redirect_uri=...&scope=openid email - Google authenticates the user, asks for consent
- Google redirects back to your
redirect_uriwith an authorization code - Your server exchanges the code for an access token and ID token by calling Google's token endpoint
- Your server verifies the ID token (JWT signed by Google), extracts the user's email and Google user ID
- Your server finds or creates the user in your database, creates a session
The PKCE extension (Proof Key for Code Exchange) protects against authorization code interception attacks. Always use PKCE for OAuth flows from public clients (SPAs, mobile apps).
Password Storage: Never Store Plaintext
Passwords must be hashed with a slow, purpose-built hashing algorithm before storage. bcrypt, Argon2id, and scrypt are the correct choices. MD5 and SHA-1 are cryptographic hash functions — they are fast and not appropriate for password hashing.
Why slow hashing matters: an attacker who obtains your password database will try to crack the hashes by computing hashes for millions of common passwords. Fast algorithms (MD5, SHA-256) can compute billions of hashes per second on modern hardware. Bcrypt is designed to compute thousands per second — 1 million times slower. Each factor-of-10 slowdown reduces the attacker's effective keyspace by 90%.
# Python: hash a password with bcrypt import bcrypt def hash_password(password: str) -> bytes: salt = bcrypt.gensalt(rounds=12) # cost factor 12 return bcrypt.hashpw(password.encode(), salt) def verify_password(password: str, hashed: bytes) -> bool: return bcrypt.checkpw(password.encode(), hashed)
Argon2id is the current recommended algorithm (winner of the Password Hashing Competition). It is memory-hard, making GPU-based cracking more expensive. If your language's bcrypt library is well-maintained, bcrypt at cost factor 12 is still acceptable and has a longer track record.
Auth Best Practices Checklist
- Use a managed auth service unless you have specific reasons to build your own
- Hash passwords with bcrypt (cost 12+) or Argon2id — never MD5, SHA-1, or SHA-256
- Set cookies with HttpOnly, Secure, and SameSite=Lax attributes
- Use short-lived JWTs (15-30 minutes) with refresh token rotation for stateless auth
- Store refresh tokens server-side to enable revocation
- Implement rate limiting on login endpoints — 5-10 attempts per minute maximum
- Use PKCE for all OAuth Authorization Code flows from public clients
- Implement MFA for admin accounts and as an opt-in for all users
- Set strong password policies (12+ characters minimum, check against HaveIBeenPwned breach database)
- Log all authentication events (login success, login failure, password change) with timestamps and IP addresses
- Never log passwords, tokens, or session IDs
- Regenerate session IDs on privilege escalation (login, role change) to prevent session fixation
Frequently Asked Questions
What is the difference between sessions and JWTs?
Sessions store authentication state on the server (in Redis or a database). JWTs store authentication state in the token itself (on the client). Sessions are easier to revoke and more appropriate for web applications. JWTs are stateless and work better for APIs, mobile apps, and microservices. Both are valid approaches — the choice depends on your architecture.
What is OAuth 2.0?
OAuth 2.0 is an authorization framework that allows users to grant third-party applications limited access to their accounts at another service without sharing their password. When you click 'Sign in with Google,' your app uses OAuth 2.0 to ask Google to verify the user's identity and share basic profile information (email, name) with your app.
How should I store passwords?
Hash passwords with bcrypt (cost factor 12 or higher) or Argon2id before storing them in your database. Never store plaintext passwords. Never use MD5, SHA-1, or regular SHA-256 for password hashing — these are fast hash functions, not password hashing functions, and they are trivially cracked by attackers with modern hardware.
What is MFA and should I require it?
Multi-factor authentication (MFA) requires users to verify their identity with two or more factors: something they know (password), something they have (authenticator app, SMS), or something they are (biometric). Require MFA for admin accounts and internal tools. Offer it as opt-in for regular users. TOTP authenticator apps (Google Authenticator, Authy) are more secure than SMS-based MFA.
Authentication done right protects your users and your business. Get the skills.
Join professionals from Denver, NYC, Dallas, LA, and Chicago for two days of hands-on AI and tech training. $1,490. October 2026. Seats are limited.
Reserve Your SeatNote: Information in this article reflects the state of the field as of early 2026. Technology evolves rapidly — verify specific details directly with vendors before making decisions.