Authentication systems protect the most valuable part of an application: user identity and access. A leaked password, session cookie, refresh token, API key, or authentication credential can allow an attacker to impersonate a legitimate user.
A secure authentication system therefore needs to assume that credentials can eventually be exposed. The design goal is to reduce the usefulness of leaked credentials, detect abnormal usage, limit the attacker’s capabilities, and provide mechanisms for rapid revocation.
This article describes a practical defense architecture for preventing and containing authentication-related information leaks.
1. Use Secure, HttpOnly Cookies
For browser-based applications, authentication sessions should use secure cookies.
A recommended cookie configuration is:
Set-Cookie: __Host-session=<random-session-id>;
Path=/;
Secure;
HttpOnly;
SameSite=Lax
HttpOnly
HttpOnly prevents JavaScript from directly reading the cookie.
document.cookie
The authentication session remains inaccessible to JavaScript, which significantly reduces the impact of many XSS-based credential theft scenarios.
Secure
Secure ensures the cookie is transmitted only over HTTPS.
SameSite
SameSite=Lax or SameSite=Strict provides protection against many cross-site request forgery scenarios.
__Host- prefix
The __Host- cookie prefix provides additional restrictions:
-
Secureis required. -
Path=/is required. - A
Domainattribute cannot be specified.
Example:
__Host-session=<random-value>; Secure; HttpOnly; SameSite=Lax; Path=/
This creates a strong browser-side boundary around the authentication session.
2. Avoid Storing Authentication Tokens in localStorage
Browser applications should avoid storing long-lived authentication credentials in JavaScript-accessible storage.
Example:
localStorage.setItem("access_token", token);
Any successful XSS payload running in the application's origin may be able to read the token:
const token = localStorage.getItem("access_token");
A safer browser architecture uses:
Browser
│
▼
HttpOnly Cookie
│
▼
Session ID
│
▼
Server-side Session
The browser stores only the session identifier, while the actual authentication state remains server-side.
3. Generate High-Entropy Session IDs
Session identifiers must be generated using a cryptographically secure random number generator.
A practical baseline is a 256-bit random value.
Node.js:
import crypto from "node:crypto";
const sessionId = crypto.randomBytes(32).toString("base64url");
Web Crypto:
const bytes = new Uint8Array(32);
crypto.getRandomValues(bytes);
Avoid predictable identifiers based on:
User ID
Timestamp
Incrementing counters
Email address
IP address
Weak hashes
Deterministic UUIDs
A session ID should be computationally infeasible to guess.
4. Rotate Sessions After Authentication
Session rotation protects against session fixation and reduces the lifetime of previously issued credentials.
Before authentication:
Session A
anonymous
After successful login:
Session A → revoked
Session B → authenticated
The browser receives a new session cookie.
Session rotation should also happen after important authentication state changes, such as:
- Login
- Privilege escalation
- MFA enrollment
- Password changes
- Passkey enrollment
- Recovery flows
- Suspicious authentication events
Example:
Anonymous Session
│
▼
Authentication
│
▼
Revoke old session
│
▼
Create authenticated session
5. Use Short-Lived and Bounded Sessions
Sessions should have explicit lifetime limits.
A practical session policy can include:
Absolute Lifetime: 30 days
Idle Timeout: 7 days
The absolute lifetime defines the maximum age of the session.
The idle timeout defines how long a session can remain unused.
Example:
Login
│
├── active usage → session remains valid
│
└── 7 days idle → session expires
30 days after creation → session expires regardless of activity
Shorter lifetimes can be used for highly sensitive applications.
6. Support Server-Side Session Revocation
Authentication systems should retain the ability to revoke individual sessions.
Example session record:
sessions
--------------------------------
id
user_id
created_at
expires_at
last_seen_at
revoked_at
ip
user_agent
device_id
When a user logs out:
UPDATE sessions
SET revoked_at = NOW()
WHERE id = ?;
Deleting the browser cookie provides the client-side logout behavior.
Revoking the session on the server provides the security control.
This is critical when dealing with stolen credentials.
Stolen Cookie
│
▼
Attacker Request
│
▼
Session Lookup
│
▼
revoked_at != NULL
│
▼
401 Unauthorized
7. Validate Session State Before Granting Access
Every authenticated request should pass basic session validation.
At minimum:
Cookie exists
↓
Session exists
↓
Session belongs to user
↓
Session is active
↓
Session is not expired
↓
Session is not revoked
A session can maintain explicit lifecycle states:
active
reauth_required
suspended
revoked
expired
Example:
Session
│
├── active
│
├── reauth_required
│
├── suspended
│
├── revoked
│
└── expired
This gives the authentication layer more control than a simple valid/invalid flag.
8. Store Session Context
A session should contain enough context to identify unusual authentication behavior.
Useful metadata includes:
session_id
user_id
created_at
expires_at
last_seen_at
ip_address
asn
country
user_agent
device_id
authentication_method
mfa_verified
revoked_at
These fields support security analysis while keeping the actual credential separate from the browser.
The IP address and similar metadata should primarily function as risk signals.
9. Use Risk-Based Session Detection
A stolen cookie may initially look identical to a legitimate session.
The server therefore needs behavioral and contextual analysis.
For example:
Known Session
│
├── Same device
├── Same browser
├── Same ASN
└── Same region
│
▼
Low Risk
Another request may look like:
Known Session
│
├── New device
├── New ASN
├── New country
└── Unusual behavior
│
▼
High Risk
The session can remain valid while its risk level changes.
10. Monitor IP Changes
IP changes are common in real-world environments because users move between Wi-Fi, cellular networks, VPNs, and corporate networks.
For this reason, IP changes should usually increase risk rather than immediately invalidate the session.
Example:
Previous IP:
203.x.x.x
Current IP:
103.x.x.x
Risk:
+10
A major geographic change can increase the score further.
11. Monitor Autonomous System Changes
ASN information provides another useful signal.
Example:
Original:
Residential ISP
Current:
Cloud VPS Provider
A transition from a normal residential network to a hosting provider can indicate automation, proxying, or stolen-session usage.
Possible signals:
Residential ISP → Residential ISP
Low risk
Residential ISP → Corporate ISP
Medium risk
Residential ISP → Cloud Provider
Higher risk
Cloud providers and VPN networks can also be legitimate, so ASN should remain a risk signal rather than a standalone blocking rule.
12. Monitor Geographic Changes
Country changes can provide useful context.
Example:
17:00 — Jakarta, Indonesia
17:05 — Singapore
17:10 — New York, United States
Large location changes over very short intervals can indicate session theft.
This becomes especially useful when combined with other signals:
Country change
+
New device
+
New ASN
+
Sensitive action
=
High risk
13. Detect Impossible Travel
Authentication systems can estimate whether a user could realistically travel between observed locations.
For example:
08:00
Jakarta
08:10
London
A transition requiring an impossible travel speed is a strong anomaly signal.
Possible response:
Impossible Travel
│
▼
Increase Risk Score
│
▼
Require Re-authentication
14. Track Device Identity
Sessions can be associated with a logical device identifier.
Example:
Device A
├── Session 1
└── Session 2
Device B
└── Session 3
If Session 1 suddenly appears from an unfamiliar device:
Session 1
│
▼
Device mismatch
│
▼
Risk increases
Device binding can be useful for sensitive applications.
For general SaaS products, device identity works best as a risk signal because users legitimately switch devices and browsers.
15. Monitor User-Agent Changes
A large browser change can be suspicious.
Example:
Chrome / Windows
↓
curl
or:
Chrome / Windows
↓
Firefox / Linux
The signal becomes stronger when combined with:
New device
New ASN
New country
High request frequency
Sensitive endpoint access
User-Agent alone should rarely determine session validity.
16. Separate Authentication from Authorization
A valid session establishes identity.
Authorization determines whether that identity may perform a particular operation.
Example:
Authentication
│
▼
Who is this user?
│
▼
Authorization
│
▼
What may this user do?
A user session may allow:
Profile: Read
Profile: Write
Billing: Read
Admin: Deny
A stolen cookie therefore remains subject to the application's authorization rules.
17. Require Step-Up Authentication for Sensitive Actions
A valid session should provide convenient access to ordinary operations.
Sensitive operations should require stronger authentication assurance.
Examples:
Change password
Change email
Disable MFA
Create API key
Delete account
Modify billing
Add administrator
Withdraw funds
The system can request:
Password
MFA
Passkey
Security key
Example:
Valid Session
│
▼
Sensitive Operation
│
▼
Step-Up Authentication
│
├── Success → Continue
│
└── Failure → Reject
This dramatically limits the usefulness of a stolen session cookie.
18. Use Refresh Token Rotation
For applications using access tokens and refresh tokens, refresh tokens should rotate after successful use.
Example:
Refresh Token A
│
▼
Refresh
│
▼
Refresh Token B
Token A becomes invalid after the rotation.
If Token A is later reused:
Refresh Token A
│
▼
Reuse detected
│
▼
Revoke token family
│
▼
Require authentication
This provides an important stolen-token detection mechanism.
19. Protect Against CSRF
HttpOnly protects cookies from direct JavaScript access.
CSRF requires a separate defense strategy.
Useful controls include:
SameSite cookies
+
CSRF tokens
+
Origin validation
For sensitive state-changing requests:
POST /api/password/change
X-CSRF-Token: <token>
Origin: https://app.example.com
The server validates both the request origin and the CSRF token according to the application's architecture.
20. Validate the Origin Header
Sensitive endpoints can validate that the request originated from the expected application.
Example:
Origin: https://app.example.com
Expected:
https://app.example.com
Unexpected origins can be rejected or escalated to additional security checks.
This is particularly useful for:
Account changes
Payment actions
Administrative actions
Credential management
Security settings
21. Add Rate Limiting
A stolen session can be abused at high speed.
Rate limiting should consider several dimensions:
user_id
session_id
IP
ASN
device_id
endpoint
Example:
Normal traffic
10 requests/sec
│
▼
Allow
Extreme traffic
10,000 requests/sec
│
▼
Rate limit
Sensitive endpoints should have stricter limits.
22. Build a Risk Scoring Engine
Security conditions become easier to manage when centralized inside a risk engine.
Example scoring model:
New IP +10
New ASN +15
New country +20
New device +25
Impossible travel +30
High-risk network +40
Suspicious behavior +50
Example decision policy:
0–29 → Allow
30–59 → Step-up authentication
60–79 → Restrict sensitive operations
80+ → Revoke session
The exact values should be calibrated using application telemetry and false-positive rates.
A centralized risk engine also makes policy easier to evolve.
23. Create Security Events
Security events should be recorded for important anomalies.
Example:
{
"event": "session_anomaly",
"user_id": "usr_123",
"session_id": "ses_456",
"risk_score": 87,
"reasons": [
"new_device",
"new_country",
"asn_change"
],
"ip": "x.x.x.x",
"country": "US"
}
Useful event types include:
login_success
login_failure
session_created
session_rotated
session_revoked
session_anomaly
mfa_challenge
mfa_success
mfa_failure
refresh_token_reuse
password_changed
security_setting_changed
These events support detection, investigation, auditing, and automated response.
24. Design for Credential Theft
A robust authentication system assumes credentials can be exposed.
The security model should therefore look like:
Credential Theft
│
▼
Attacker Request
│
▼
Session Validation
│
▼
Risk Analysis
│
┌────────────┼────────────┐
▼ ▼ ▼
Low Medium High
│ │ │
Allow Step-Up Revoke
MFA Session
The stolen credential becomes only one input into the decision process.
25. Recommended Authentication Architecture
For a modern SaaS application, a strong architecture can look like this:
Browser
│
│ HTTPS
▼
┌────────────────────┐
│ HttpOnly Cookie │
│ __Host-session │
└─────────┬──────────┘
│
▼
┌────────────────────┐
│ Authentication │
│ Layer │
└─────────┬──────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
Session Store Risk Engine Authorization
│ │ │
▼ ▼ ▼
PostgreSQL Redis RBAC / ABAC
│ │ │
└──────────────┼──────────────┘
▼
Application
A practical stack can be:
Browser
│
▼
HttpOnly session cookie
│
▼
Authentication API
│
├── Session validation
├── CSRF / Origin validation
├── Risk evaluation
├── Authorization
├── Rate limiting
└── Security event logging
│
├── PostgreSQL
│ └── Users + Sessions
│
└── Redis
├── Session cache
├── Rate limits
└── Risk signals
Authentication Security Checklist
Cookie Security
- [x]
HttpOnly - [x]
Secure - [x]
SameSite=LaxorStrict - [x]
__Host-prefix where applicable - [x] No sensitive data inside the cookie
- [x] Strong random session identifier
Session Security
- [x] Server-side session storage
- [x] Session rotation after login
- [x] Session expiration
- [x] Idle timeout
- [x] Server-side revocation
- [x] Explicit session states
Leak Detection
- [x] IP monitoring
- [x] ASN monitoring
- [x] Country monitoring
- [x] Impossible-travel detection
- [x] Device tracking
- [x] User-Agent analysis
- [x] Behavioral anomaly detection
Account Protection
- [x] RBAC / authorization
- [x] Step-up authentication
- [x] MFA / passkeys
- [x] CSRF protection
- [x] Origin validation
- [x] Rate limiting
Token Protection
- [x] Short-lived access tokens
- [x] Refresh token rotation
- [x] Refresh token reuse detection
- [x] Token-family revocation
Monitoring
- [x] Authentication events
- [x] Session anomaly events
- [x] Security audit logs
- [x] Automated response
- [x] Session revocation controls
Final Design Principle
A secure authentication system should operate under this assumption:
Credentials may leak.
Sessions may be copied.
Devices may change.
Networks may change.
Attackers may replay valid credentials.
The authentication system should continuously answer three questions:
1. Is this session valid?
2. Does this request look trustworthy?
3. Does this operation require stronger authentication?
The resulting security model becomes:
Credential
│
▼
Session Validation
│
▼
Risk Assessment
│
▼
Authorization
│
▼
Action Assurance
│
├── Normal action
│ └── Allow
│
└── Sensitive action
└── MFA / Passkey
A secure authentication system is built around the assumption that credentials can eventually be exposed. The strongest defense combines secure cookie handling, server-side session management, continuous risk assessment, strict authorization, step-up authentication, token rotation, rate limiting, and security event monitoring to reduce the value of stolen credentials and contain unauthorized access.
By treating authentication as a continuous security process rather than a single login event, applications can detect suspicious sessions earlier, respond automatically, and protect sensitive operations even when an attacker possesses a valid credential.

Top comments (0)