Prompt and Applicable Context
Design session management for a two-region B2B web application. Browsers use server-side sessions for normal and administrator access. The product needs logout on the current device, logout on all devices, and revocation after a password change. A global logout must reject protected requests in both regions within five seconds.
For this exercise, choose a 256-bit opaque identifier, a 30-minute idle timeout, and a 12-hour absolute timeout. These are scenario decisions, not universal security constants. Explain what changes at login, administrator elevation, expiry, logout, and a suspected theft event. Include concurrent requests and cross-region propagation rather than describing only cookie attributes.
This is a backend lifecycle question. A strong design keeps the browser token meaningless, makes the server the authority for authentication state, rotates identity across trust-boundary changes, and can prove that old credentials stop working. Secure, HttpOnly, and SameSite matter, but none of them alone supplies server-side expiry, authorization, or revocation.
What the Interviewer Evaluates
The first signal is whether the candidate treats a session identifier as a temporary bearer credential. It must be unpredictable, accepted through one intended mechanism, protected in transit and at rest, and absent from URLs and logs. A database or log reader should not automatically obtain a usable token.
The second signal is lifecycle reasoning. The anonymous identifier must not simply become authenticated. Login and privilege elevation cross trust boundaries, so the application creates a new identifier and destroys the old one. Idle and absolute expiry are enforced by the server. Clearing a browser cookie is client cleanup; it does not revoke a copy already held by an attacker.
The third signal is the distinction between authentication and authorization. A valid session locates a user and authentication context. Every request still checks current tenant membership and permissions. Copying roles into a long-lived session record without an invalidation rule can preserve access after an administrator removes a role.
The fourth signal is distributed consistency. A promise that global logout takes effect in five seconds requires an authoritative version or revocation state, bounded cache staleness, and behavior during a partition. Saying “delete it from Redis” is incomplete when another region can continue using a cached positive result.
The final signal is verification. The candidate should turn fixation, theft, CSRF, expiry, concurrent rotation, replica lag, and log leakage into executable cases. Security claims become credible when each old identifier has a specific event after which requests using it must fail.
Questions to Clarify Before Answering
- Which clients are in scope? This answer targets a same-site browser application. Native apps and
third-party API clients usually need a different token transport and lifecycle.
- What does “within five seconds” mean? The server must reject protected requests by then. A browser
tab may still display stale content until its next request.
- Are simultaneous sessions allowed? This design permits several devices and stores each session
separately. A stricter product can cap them or replace older sessions at login.
- Which events revoke everything? Global logout and password change increment an account-wide session
epoch. A permission change can increment an authorization version without necessarily signing out every device.
- How risky is administrator access? This design rotates on elevation and records authentication
strength. High-impact operations may also require recent reauthentication.
- Must cross-site login or embedding work?
SameSite=Laxfits the assumed first-party navigation.
A legitimate cross-site flow needs a narrow exception plus explicit CSRF protection.
- What happens during a regional partition? The five-second security requirement implies fail-closed
behavior for high-risk routes when fresh revocation state cannot be obtained.
30-Second Answer Framework
“I would put a 256-bit opaque value in a __Host- cookie with Secure, HttpOnly, Path=/, and an explicit SameSite policy, while storing only an HMAC-derived lookup key on the server. The session row contains the user, tenant, authentication strength, creation and activity times, expiries, and the user’s session and authorization versions. Login and administrator elevation atomically issue a new session and invalidate the old identifier. Every protected request enforces idle and absolute expiry, current account epoch, and current authorization. Logout revokes the row; logout-all or password change increments the account epoch and publishes invalidation to both regions with a five-second cache bound. I would verify fixation, old-ID reuse, concurrent rotation, CSRF, timeout boundaries, regional lag, and log redaction.”
Step-by-Step Deep Dive
Start with the threat model. An attacker may set an identifier before the victim logs in, steal one from the browser or infrastructure, replay it from another device, keep it alive, exploit a stale permission, or race a rotation. The system must also resist a cross-site state-changing request because browsers send cookies automatically.
Use the framework’s reviewed session implementation rather than inventing a random generator and parser. For the stated design, generate 32 random bytes with a cryptographically secure generator. Send the raw opaque value only in the cookie. Derive a fixed-length lookup key such as HMAC-SHA-256(serverkey, rawid) before querying storage, so a database snapshot does not contain the bearer values. Key rotation for that HMAC needs an explicit dual-read migration plan.
An illustrative server record is:
session_lookup_key
user_id, tenant_id
created_at, last_seen_at
idle_expires_at, absolute_expires_at
authentication_time, authentication_strength
session_epoch, authorization_version
revoked_at, revocation_reasonThe browser receives a host-only cookie such as:
Set-Cookie: __Host-session=<opaque>; Secure; HttpOnly; SameSite=Lax; Path=/The __Host- prefix requires Secure, omits Domain, and uses Path=/, reducing subdomain cookie injection. HttpOnly blocks direct JavaScript reads but does not prevent injected script from causing authenticated actions. SameSite reduces some cross-site requests but is defense in depth; state-changing routes still use a CSRF token or another request-bound proof and validate Origin where appropriate.
Accept the session only from the cookie. Reject identifiers supplied through URL parameters or alternate headers unless a separate client protocol explicitly defines them. URLs leak through history, referrers, analytics, screenshots, and proxy logs. Validate the token’s syntax and perform constant-shape failure handling, while rate-limiting repeated invalid identifiers.
Keep anonymous and authenticated state separate. When credentials succeed, create a fresh authenticated session in a short transaction and invalidate the pre-login identifier. On administrator elevation or other privilege increase, require the appropriate proof, create another fresh identifier, and invalidate the lower-trust identifier. The response sets the new cookie only after server state is committed.
Parallel browser requests make rotation subtle. If the old identifier is destroyed immediately, an in-flight request may receive an unauthorized response. A bounded handoff can map the old identifier to the already-created successor for a few seconds, but it must never mint multiple successors or return the new bearer value to an arbitrary replay. Use one atomic rotation record and make the successor available only through the legitimate response. For highly sensitive elevation, accepting a brief retry is safer than a broad grace window.
On every protected request, look up the session, reject a revoked row, enforce both expiry clocks using server time, and compare session_epoch with the current account epoch. The 30-minute idle timeout moves only on meaningful activity and can update in buckets to avoid a write on every request. The 12-hour absolute deadline never moves. Client countdowns improve usability but do not decide validity.
Then load current tenant membership and authorization state, or compare a version whose invalidation contract is explicit. A valid session never replaces object-level authorization. IP address, network, device, and user-agent changes are useful risk signals; hard-binding to them causes false logouts behind mobile networks, proxies, and shared devices. High-risk changes can trigger reauthentication or revoke the session according to policy.
Current-device logout atomically marks that session revoked before expiring the cookie. Logout-all and password change increment the user’s session_epoch; every older session then fails even if individual rows remain. Publish the new epoch to both regions and invalidate positive caches. Bound cache lifetime to at most five seconds, and have administrator or other high-risk routes read authoritative state when the cache is stale. During a partition, those routes fail closed because availability cannot override the stated revocation guarantee.
Do not promise a five-second bound without measuring it. Record the authoritative commit time and the first rejection time observed in each region. Alert when propagation approaches the budget. Audit session creation, rotation, elevation, expiry, and revocation with a non-secret correlation ID; never log raw cookies, lookup keys, or full Cookie headers.
Build tests around transitions. Set an attacker-chosen anonymous cookie, log in, and prove that it cannot access the account. Replay pre-login, pre-elevation, logged-out, expired, and pre-password-change IDs. Exercise the exact idle and absolute boundaries with a controlled server clock. Race two elevation requests, delay invalidation in one region, simulate a partition, send cross-site requests, and scan every application, proxy, tracing, analytics, and support log for bearer values.
High-Quality Sample Answer
“I would model a session as a server-side state machine whose browser handle is a temporary bearer credential. For this scenario, the handle is 32 random bytes. The cookie is host-only, secure, HTTP-only, path-wide, and explicitly SameSite=Lax; the session store receives only an HMAC-derived lookup key.
The record contains the user and tenant, authentication strength, creation and last-activity times, a 30-minute idle deadline, a fixed 12-hour absolute deadline, and snapshots of the account session epoch and authorization version. Every request verifies the row, both deadlines, the current epoch, and current authorization. IP and device changes feed risk decisions rather than acting as brittle identity proof.
Login and administrator elevation each create a fresh session and invalidate the lower-trust identifier. Rotation is atomic so parallel requests cannot create competing successors. Logout first revokes the server row, then clears the cookie. Logout-all and password change increment the account epoch and publish cache invalidations. Both regions cap positive cache age at five seconds; high-risk routes fail closed if they cannot refresh state.
Finally, I would test fixation, replay of every previous identifier, CSRF, concurrent rotation, idle and absolute boundaries, authorization changes, regional propagation, partition behavior, and secret-free logs. The key proof is that each trust-changing event has a defined old credential and a measured point after which that credential is rejected.”
Common Mistakes
- Keeping the same ID after login → an attacker can preselect it and wait for authentication →
issue a fresh authenticated session and destroy the anonymous one.
- Clearing only the cookie on logout → a stolen copy remains valid → **revoke server state before
clearing client state.**
- Treating
HttpOnlyas XSS protection → injected code can still send authenticated requests →
prevent XSS and enforce authorization and CSRF defenses independently.
- Using
SameSiteas the sole CSRF control → legitimate exceptions and browser behavior weaken the
assumption → use request-bound CSRF proof for state changes.
- Putting the ID in a URL → history, referrers, and logs copy the credential → **accept it only through
the intended cookie mechanism.**
- Refreshing only idle expiry → active theft can last forever → enforce a fixed absolute deadline.
- Caching a valid session indefinitely → global logout cannot meet its bound → **version sessions and
bound or bypass positive caches.**
- Embedding roles forever in the session → removed permissions survive → **check current authorization
or a deliberately invalidated version.**
- Hard-binding to IP address → ordinary network changes sign users out → **use context changes as risk
signals.**
- Adding a broad rotation grace period → two bearers remain useful → **use an atomic, narrowly bounded
handoff or accept a retry for sensitive elevation.**
- Logging cookie headers for debugging → observability becomes a credential store → **log only a
non-secret correlation value.**
Follow-Up Questions and Responses
Follow-up 1: Why use server-side sessions instead of a self-contained JWT?
The required global revocation already needs current server state. An opaque identifier keeps claims out of the browser and makes one-row revocation straightforward. A JWT can work, but immediate logout still requires a short expiry, revocation list, or account-version lookup; signing alone does not solve it.
Follow-up 2: How do you avoid a write on every request for idle expiry?
Keep the authoritative last-activity value in coarse buckets, for example updating only when the stored value is several minutes old. The server still rejects when the derived idle deadline has passed. Choose the bucket so its maximum extension is included in the security policy, and test that boundary explicitly.
Follow-up 3: What if the cross-region store is unavailable?
Separate route risk. Public and read-only routes may accept a bounded cached decision if policy permits. Administrator and other high-impact routes must obtain fresh epoch state or fail closed, because otherwise the five-second global-logout promise is false. Track this as an availability and security SLO.
Follow-up 4: Should periodic session-ID renewal always be enabled?
No. Renewal can reduce the useful lifetime of one stolen identifier, but it introduces handoff races and does not replace idle or absolute expiry. Rotate at login and privilege changes first. Add periodic renewal only with a tested atomic protocol and a clear threat-model benefit.
Follow-up 5: How would you show a user their active devices?
Store non-secret metadata such as creation time, recent activity bucket, approximate device label, and coarse location. Let the user revoke one row or increment the account epoch for all. Labels are hints, not proof of device identity, and raw session identifiers never enter the UI or audit export.
Follow-up 6: Which single test best exposes session fixation?
Start with an identifier chosen before authentication, complete login, then send a protected request using the old value from another client. It must fail while the newly issued identifier succeeds. Repeat for administrator elevation and verify that storage and logs do not reveal the replacement bearer.