The Statelessness Problem
HTTP is inherently stateless—each request is independent with no knowledge of previous interactions. However, real-world applications require maintaining user state across multiple requests. Imagine a user logging into a social platform: they expect their feed, comments, and follows to all occur within their authenticated session.
Storage Solutions
The solution involves issuance, storage, and transmission of authentication markers. While the server generates these markers during login, the client must store and transmit them with subsequent requests.
Client-side storage options vary in persistence:
- Memory variables: Ephemeral storage that vanishes on page refresh—useful only for temporary state
- Persistent storage: Mechanisms like cookies, localStorage, and sessionStorage that survive browser refreshes
Cookies: The HTTP-Aware Storage
Cookies offer a unique advantage: they integrate with the HTTP protocol for automatic transmission, requiring no explicit JavaScript intervention during requests.
Typical flow:
- Server sends authentication markers via the
Set-Cookieheader - Browser automatically includes matching cookies in subsequent request headers
Domain and Path Restrictions
Cookies scope their availability through two dimensions:
Domain attribute: Specifies which domains should receive the cookie. Without explicit definition, browsers default to the current hostname's root domain. Cookies set for one domain won't be transmitted to unrelated domains.
Path attribute: Defines URL paths that trigger cookie transmission. For example, a cookie with path=/docs gets sent for requests to /docs, /docs/page, but not for /products.
Temporal Constraints
Cookies include expiration controls:
Expires: Sets a specific UTC timestamp for cookie deletion. If unset, the cookie becomes a session cookie—destroyed when the browser closes.
Max-Age: Defines duration in seconds from the current time. When both Expires and Max-Age exist, Max-Age takes precedence.
Security Attributes
Secure flag: Restricts cookie transmission to HTTPS connections only. HTTP requests won't include these cookies, even if the domain matches.
HttpOnly flag: Prevents JavaScript access to the cookie via document.cookie, XMLHttpRequest, or the Fetch API. The cookie remains accessible only during actual HTTP transmissions, protecting against XSS-based cookie theft.
Protocol-Level Operations
The Set-Cookie header writes individual cookies with their configuration:
Set-Cookie: session_id=abc123; domain=example.com; path=/api; Expires=Wed, 21 Oct 2025 07:28:00 GMT; Secure; HttpOnly
Multiple cookies require multiple headers:
Set-Cookie: user_pref=dark_mode; domain=example.com
Set-Cookie: cart_count=3; domain=example.com
Set-Cookie: locale=en_US; domain=example.com
The Cookie header transmits all matching cookies in a semicolon-separated format, omitting configuration details since the browser filters based on scope rules:
Cookie: session_id=abc123; cart_count=3; locale=en_US
JavaScript Interaction
Scripts can read and write non-HttpOnly cookies through document.cookie. Each operation affects one cookie:
document.cookie = 'theme=dark; path=/; Secure';
console.log(document.cookie); // theme=dark; cart_count=3
Session-Based Authentication
The session pattern mirrors physical access cards: the card contains only an identifier, while actual credentials reside in a backend database. When authentication occurs, the server stores user state and returns only a session identifier to the client.
Authentication workflow:
- User submits credentials via login form
- Server validates against user database
- Server creates session record and generates unique session identifier
- Server sets session ID in a cookie
- Subsequent requests include session ID automatically
- Server validates session ID against stored records
- Authorized requests proceed to business logic
Sesion Storage Backends
Since the server maintains session data, storage strategy becomes critical:
- Redis: In-memory key-value store optimized for session use cases. Provides fast read/write operations and suits distributed deployments.
- Process memory: Simplest implementation but data loss on server restart.
- Relational databases: Persistent but slower for high-frequency lookups.
Distributed Session Challenges
Load-balanced server clusters create session distribution problems: subsequent requests may reach different servers lacking the user's session data.
Solutions include centralized session storage (Redis cluster) or request affinity (routing identical client IPs to the same server). The centralized approach dominates because request affinity compromises load distribution and fails during server outages.
Token-Based Authentication
Tokens shift authentication data entirely to the client, eliminating server-side session storage. Think of a physical employee badge: security personnel verify credentials directly from the badge rather than calling headquarters.
Token workflow:
- User authenticates with credentials
- Server generates token containing user information and metadata
- Token gets transmitted to client for storage
- Client includes token with each request
- Server validates token authenticity and content directly
Client Storage Flexibility
Unlike session-based approaches requiring cookie transmission, tokens work with any storage mechanism. While web applications often store tokens in cookies for convenience, mobile apps might use secure storage, and commend-line tools could use files.
Token Encoding Strategies
Base64 Encoding
Simple token libraries encode data as base64 strings. A token containing {"user_id":"john"} becomes eyJ1c2VyaWQiOiJqaG4ifQ==.
Tamper Prevention
Base64 encoding alone permits manipulation since clients can decode, modify, and re-encode tokens. Signature mechanisms verify integrity.
Generation involves computing a cryptographic hash from token content and a secret key:
const crypto = require('crypto');
const secret = 'secret_key_123';
const payload = '{"user_id":"john"}';
const signature = crypto.createHmac('sha256', secret).update(payload).digest('base64');
// signature: 3Fz3kl_uKWRkwjOP6uQRJFqMlwSABcgqqcJofFH5XCo
The client receives both token and signature. Attempting to forge the token without knowing the secret produces an invalid signature.
JSON Web Tokens (JWT)
JWT standardizes token format and signing, producing self-contained authentication tokens. A typical JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJqaG4iLCJpYXQiOjE1NTE5NTE5OTh9.sig_value_here
Structure:
Header: Contains token type and signing algorithm (HMAC SHA256 or RSA).
Payload: Contains claims—statements about the user and metadata. Standard fields include:
iss: Issuersub: Subject (user identifier)exp: Expiration timestampiat: Issued-at timestamp
Signature: Verifies header and payload integrity using the specified algorithm and secret key.
Implementation libraries exist for major frameworks: express-jwt, koa-jwt for Node.js.
Refresh Token Strategy
Short-lived access tokens minimize security exposure—if stolen, they expire quickly. However, frequent expiration frustrates users with constant re-authentication.
The solution employs dual tokens:
- Access token: Short expiration (minutes), grants API access, transmitted with every request
- Refresh token: Longer expiration (days/weeks), used exclusively to obtain new access tokens
When access tokens expire, clients use refresh tokens to obtain new access tokens without requiring user interaction. If refresh tokens also expire, re-authentication becomes necessary.
Session vs Token Comparison
Session and token approaches differ fundamentally:
| Aspect | Session | Token |
|---|---|---|
| Storage Location | Server (with session ID in client cookie) | Client (entire payload) |
| Cookie Dependency | Required for session ID | Optional (alternatives available) |
| State Management | Stateful (server maintains state) | Stateless (server validates payload) |
| Request Size | Minimal (session ID only) | Larger (contains user data) |
| CSRF Vulnerability | Susceptible (automatic cookie transmission) | Resistant (custom header transmission) |
Cookie-Based Storage Considerations
Cookies simplify transmission but introduce CSRF vulnerabilities since browsers attach cookies to all matching requests automatically. This matters for state-changing operations.
Stateless Validation Benefits
Token approaches eliminate session storage infrastructure and distributed state synchronization. Verification requires only cryptographic operations and decoding—much faster than database lookups.
Single Sign-On Implementation
Enterprise environments span multiple applications across various domains. Users shouldn't authenticate separately to each system—a single login should grant access everywhere.
Same-Parent-Domain Scenario
When all systems share a parent domain (app1.example.com, app2.example.com), cookie sharing becomes straightforward. Setting domain=example.com makes the cookie accessible across all subdomains. Major cloud platforms use this approach.
Cross-Domain Single Sign-On
Distinct parent domains require centralized authentication infrastructure. The SSO system becomes the authoritative authentication source.
Authentication flow:
- User visits Application A without credentials, redirected to SSO
- SSO checks for existing session—no session found, user authenticates
- SSO creates session record and generates temporary authorization code
- User returns to Application A with authorization code
- Application A exchanges authorization code for ticket at SSO
- SSO validates authorization code, returns ticket to Application A
- Application A stores ticket locally, user proceeds authenticated
- Later, user visits Application B without credentials, redirected to SSO
- SSO finds existing session, generates authorization code immediately
- User completes authentication flow with Application B
Browser Considerations
Browser security restrictions prevent cookie sharing across different domains. Cross-origin requests cannot read each other's cookies or localStorage.
The workaround involves server-to-server communication:
- SSO domain sets authentication cookie locally
- SSO redirects to application callback URL with temporary code in URL parameters
- Application's server validates code by communicating with SSO directly
- Application's server creates its own cookie in the application domain
- Browser receives application domain cookie, enabling local authentication
This architecture ensures credentials never flow through the browser, only temporary codes.