Authentication is fundamental to application security, yet it's easy to get wrong. Understanding the patterns, their trade-offs, and security implications helps you choose the right approach for your application.
Authentication vs Authorization
Authentication: Verifying identity ("Who are you?") Authorization: Checking permissions ("What can you do?")
Session-Based Authentication
How It Works
1. User logs in with credentials
2. Server creates session, stores in database/Redis
3. Server sends session ID in cookie
4. Browser sends cookie with each request
5. Server validates session ID, retrieves user
Implementation
Pros and Cons
Pros:
✓ Easy to invalidate (delete from store)
✓ Session data stays on server
✓ Works well with traditional web apps
✓ Simple to understand
Cons:
✗ Requires server-side storage
✗ Harder to scale (need shared session store)
✗ Not ideal for APIs consumed by mobile/SPAs
JWT (JSON Web Tokens)
How It Works
1. User logs in with credentials
2. Server creates signed JWT with user claims
3. Server sends JWT to client
4. Client stores JWT and sends in Authorization header
5. Server validates signature and extracts claims
Structure
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. // Header
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4ifQ. // Payload
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c // Signature
Implementation
Token Refresh
Pros and Cons
Pros:
✓ Stateless (no server-side storage for access tokens)
✓ Works well for APIs and microservices
✓ Self-contained (includes user data)
✓ Easy to scale
Cons:
✗ Can't invalidate before expiry (without blocklist)
✗ Larger payload than session IDs
✗ Must handle token refresh
✗ Security-sensitive to implement correctly
OAuth 2.0 / OpenID Connect
Common Flows
Authorization Code Flow (recommended for web apps):
1. Redirect user to provider
2. User authenticates with provider
3. Provider redirects back with code
4. Exchange code for tokens
5. Use tokens to access resources
PKCE (for SPAs and mobile):
Same as above, but with code_verifier/code_challenge
to prevent code interception attacks
Implementation with NextAuth.js
Security Best Practices
Password Handling
Token Storage (Client-Side)
Access Tokens:
✓ In-memory (JavaScript variable)
✓ Short-lived (15 minutes)
✗ Not in localStorage (XSS vulnerable)
Refresh Tokens:
✓ httpOnly cookie (prevents XSS)
✓ Secure flag (HTTPS only)
✓ SameSite attribute
✗ Not in localStorage
CSRF Protection
Rate Limiting
Multi-Factor Authentication
Choosing the Right Pattern
Traditional Web App:
→ Sessions with cookies
API for Mobile/SPA:
→ JWT with refresh tokens
Third-Party Login:
→ OAuth 2.0 / OpenID Connect
High Security:
→ Sessions + MFA
→ Short-lived tokens + refresh rotation
Conclusion
Authentication is security-critical and complex. Use established libraries and patterns rather than rolling your own. Understand the trade-offs between stateful (sessions) and stateless (JWT) approaches, and choose based on your application's needs.
Security isn't a feature—it's a requirement. Implement defense in depth with rate limiting, proper password hashing, secure token storage, and multi-factor authentication.