docs: finalize demo session persistence

This commit is contained in:
2026-08-20 17:21:25 +03:30
parent 384ea54282
commit 50030a20e8
3 changed files with 22 additions and 11 deletions

View File

@@ -9,18 +9,21 @@ The merchant dashboard needs simple username/password authentication for the hac
## Decision
Use cryptographically random opaque session tokens transported only in host-only `Secure`, `HttpOnly`, `SameSite=Lax` cookies. Store only a non-reversible token digest with server-side session state. Never place authentication tokens in local storage, session storage, URLs, or frontend-readable application state.
Use 32-byte cryptographically random opaque session tokens transported only in a host-only `__Host-zpa_session` cookie with path `/`, no `Domain`, `Secure`, `HttpOnly`, and `SameSite=Lax`. Store sessions in PostgreSQL and persist only the SHA-256 token digest. Never place authentication tokens in local storage, session storage, URLs, frontend-readable application state, logs, or database columns.
Sessions expire after 12 hours of inactivity and no later than seven days after creation. Logout revokes the current session before clearing the cookie; disabling a merchant user revokes every active session for that user. Every protected HTTP request, SSE connection, and replay request resolves the merchant boundary from the current server-side session.
Persist activity at most once every five minutes. The idle timeout does not require per-request write precision, and coalescing avoids turning normal dashboard and SSE traffic into unnecessary PostgreSQL writes.
Passwords use Argon2id with unique salts and reviewed parameters. Expected login failures are externally generic. Demo credentials are generated through a one-time administrative workflow, displayed once, and never stored in Git or logs.
Demo usernames are stable lowercase `merchant-NNNN`-style identifiers with case-insensitive comparison. Failed logins are throttled by normalized username and source IP, return `429` with `Retry-After`, and never cause permanent account lockout. State-changing authenticated requests require CSRF tokens and strict same-origin evidence. A user may have at most two active sessions; a third successful login revokes the oldest.
Demo usernames are stable lowercase `merchant-NNNN`-style identifiers with case-insensitive comparison. Failed logins default to five attempts per normalized username and 30 per source IP in five minutes, return `429` with `Retry-After`, and never cause permanent account lockout. State-changing authenticated requests require CSRF tokens and strict same-origin evidence. A user may have at most two active sessions; a third successful login revokes the oldest.
## Consequences
- Logout, disablement, expiry, and incident response can invalidate access immediately.
- The backend session store is on the protected-request path and must fail closed, be indexed, and have observable latency/availability.
- A PostgreSQL outage also prevents authentication checks; this is preferable to trusting unverifiable client state, and no additional auth service is introduced for the demo.
- SSE reconnect and replay must reauthorize instead of trusting a previous connection or event id.
- Scaling API instances does not require sticky sessions when they share the approved authoritative session store.
- Cookie scope, login-throttle thresholds, and the physical session-store contract remain explicit Identity-spec decisions rather than implicit framework defaults.