ESC

Type to search the knowledge base.

Auth Session UX Security

Session UX that doesn’t weaken security: login states, logout everywhere, idle timeouts, and step-up auth.

intermediate3 min read
  • security
  • auth
  • session
  • ux
  • cookies

Auth is security and product UX. Over-aggressive logouts create password reuse and support tickets; under-aggressive sessions leave shared computers open. Frontend owns a lot of the visible session lifecycle even when tokens live in HttpOnly cookies.

Docs: OWASP Session Management, web.dev sign-in, Secure cookies.

Session model for SPAs

Prefer server sessions or short-lived access + rotating refresh via HttpOnly cookies over long-lived JWTs in localStorage — JWT pitfalls.

Set-Cookie: session=...; HttpOnly; Secure; SameSite=Lax; Path=/

Frontend reads “am I logged in?” from /me or bootstrap payload, not from decoding secrets in JS.

UX states to design explicitly

State UI Security note
Anonymous Marketing / login No authed API data in HTML cache
Authenticated App shell CSRF protection as needed
Idle warning Modal countdown Reduce surprise logout
Expired Re-auth, preserve return URL safely Open-redirect safe — open redirects
Step-up needed Re-enter password / WebAuthn Sensitive actions

Idle and absolute timeouts

// sketch: idle detection (pair with server-side session TTL)
let last = Date.now();
const IDLE_MS = 15 * 60 * 1000;
['pointerdown', 'keydown'].forEach((e) =>
  window.addEventListener(e, () => { last = Date.now(); }, { passive: true }),
);
setInterval(() => {
  if (Date.now() - last > IDLE_MS) warnOrLogout();
}, 30_000);

Server must enforce TTL regardless of client timers (user can disable JS). Client warnings are UX.

Logout

  1. Call server revoke endpoint.
  2. Clear client state (memory, non-HttpOnly storage).
  3. Clear-Site-Data header when available for thoroughness.
  4. Offer logout all devices for account settings.

Return URLs

// BAD
location.href = new URLSearchParams(location.search).get('next');
// GOOD: allowlist paths
function safeNext(raw) {
  if (!raw || !raw.startsWith('/') || raw.startsWith('//')) return '/app';
  return raw;
}

Shared device patterns

  • “Remember this device” only with clear risk copy
  • No forever sessions on kiosk profiles
  • Mask PII on lock screens / inactive tabs when product requires

Interview out-loud

“Session UX uses HttpOnly cookies, server-enforced TTLs, idle warnings, safe return URLs, and full logout/revoke. Frontend never stores long-lived access tokens in localStorage if we can avoid it, and sensitive actions use step-up auth.”

Reviewer prompts

  • What is the asset (session, PII, money movement)?
  • What is the attacker capability (web, XSS, network, dependency)?
  • Which control fails closed if misconfigured?
  • Is the server still enforcing authz?
  • Any new third party, iframe, or URL sink?

Residual risk note

Browser controls reduce likelihood and impact; they do not eliminate bugs. Prefer defense in depth: safe defaults in code, strict headers, and monitoring (CSP reports, auth anomaly alerts). When product pressure weakens a control, write down the accepted risk and a revisit date.

Further depth

Teams often under-invest in this topic until an incident or CWV regression. Schedule a one-hour drill: reproduce the failure mode in DevTools, list the top three mitigations for your stack, and file tickets with owners. Revisit after the next major feature that touches networking, rendering, auth, or third parties — those are the moments regressions land. Keep primary documentation links in the runbook so on-call is not searching chat history at 2am.

Concrete artifacts to leave behind: a short architecture note, a CI assertion or header snapshot, and a dashboard panel (lab or field) that would have caught the last bug. Teaching the rest of the team the mental model matters as much as the one-line fix.

Further reading

Related guides