Enforce 15-minute idle logoff

Task 5942 · Done · Coder · created · updated

Epic ask (verbatim): "Research the gap to get Jaah HIIPA compliant and fix everything that does not require human and does not break what is working now by imposing limits" (HIIPA = HIPAA). Full PRD/architecture: the epic's settled `prd` (Task 5917) and `architecture` (Task 5918) — architecture child **C5**. Builds on the HIPAA setting core child (`apps/api/app/hipaa.py` `is_on`, `IDLE_TIMEOUT`, `REFRESH_AFTER`; `/api/auth/me` `org_hipaa`). BA decisions (verbatim): B2 "HIPAA session idle logoff = 15 minutes of inactivity." B3 "The idle rule applies to a session whose current org is a HIPAA org; a user's sessions in non-HIPAA orgs keep today's 7-day idle." Operator decision E1: an open console pane in a HIPAA org, 15 min with no keystroke → **Close it**. ## Requirement (verbatim) - **AC5.4 Idle logoff.** In a session whose current org is HIPAA ON, 15 minutes without a request ends the session; the next request lands on sign-in with a message saying the session timed out for inactivity. Sessions in OFF orgs keep 7-day idle / 30-day absolute. Switching from a HIPAA org to a non-HIPAA org mid-session uses the current org's rule. - verify: api test with a clock: HIPAA org session idle 14:59 → 200, 15:01 → 401; OFF org idle 6 days → 200. Dev browser: sign in to a HIPAA test org, idle 16 min, click → sign-in page with the timeout message. - **AC5.9 Quality** (own surfaces: the timeout message). Follow `docs/standards/ui-copy-standards.md`, fit supported viewport widths. ## Approach (architecture C5, D-A2/D-A3, K5) - Idle enforced SERVER-SIDE from the signed cookie's timestamp — a client timer alone is not a control. `apps/api/app/auth.py`: where the row is loaded (`_session_user`/`session_row_is_live` `:501-534` and the `SessionAuthMiddleware` gate), if `hipaa.is_on(row.org)` and cookie signature age > `hipaa.IDLE_TIMEOUT` → refuse with **K5** `401 {"detail": "session_idle_timeout"}`. Keep `read_session` cookie-only; ride the existing row load (no extra query per request beyond the org join). - `SessionRefreshMiddleware` (`login_funnel.py:579-602`): refresh threshold = `hipaa.REFRESH_AFTER` (60 s) for HIPAA sessions, else unchanged — otherwise a 15-min slide with 15-min idle logs active users out mid-work. - Web: `apps/web/src/lib/activity.ts:5` background threshold from `admin.org_hipaa` (60 s in HIPAA org / 15 min otherwise); `apps/web/src/api.ts:32-49` maps K5 → `/login?reason=idle`; Login shows "Signed out after 15 minutes of inactivity." Every other 401 unchanged. - Console (E1): relay records last client-input frame time per console; the existing sweep (`console_relay/tickets.py` sweep, `routes/console.py:206-225` `_session_ref`) closes a HIPAA-org console idle ≥ 15 min with a close reason the pane shows. SSE `routes/stream.py:293`: at each keepalive tick end the stream for a HIPAA session past idle. - The MFA child edits the same `auth.py` row-load seam concurrently; keep your change a small, self-contained branch on `hipaa.is_on` and rebase cleanly. Credentials & reach: `jaah/debug/main/operator-login` — operator; `https://app.dev.jaah.ai` — **verified**. Dev browser idle check runs in the dedicated "HIPAA Test" org as operator (never turn HIPAA on in Org B). Api tests — worktree only. Out of scope: N1 "No new limit for any org whose HIPAA setting is OFF: idle 7 d / 30 d absolute, MFA skip/exempt, providers, Slack, Help chat, Jira, plan entitlements — all unchanged." N5 "No change to password rules, lockout thresholds, or session absolute max for non-HIPAA orgs." Do NOT touch MFA logic (C6), PHI audit (C7), warnings (C8), or the K1–K4 contracts. Done means: - AC5.4 — api tests with a clock: HIPAA org idle 14:59 → 200, 15:01 → 401 `session_idle_timeout`; OFF org idle 6 days → 200; org switch mid-session uses the current org's rule; HIPAA refresh slides every 60 s (active use never logs out); console sweep closes a HIPAA console idle ≥15 min and not an OFF-org one; SSE ends past idle for HIPAA only. - AC5.4 dev — after deploy, in "HIPAA Test": idle 16 min, click → sign-in page showing "Signed out after 15 minutes of inactivity."; an OFF org session is unaffected. - AC5.9 (own surface) — the message fits at desktop and 360 px, copy per ui-copy-standards. - N1/N5 — existing auth tests green unchanged; `./dc check full` green; PR merged to develop and Jaah Deploy green on dev.

Outcome

HIPAA 15-min idle logoff shipped (#5160; #5161 fixed a migration-id clash with #5154). Dev deploy green, gate green by stage (API suite run standalone). Dev 16-min idle walk not run: HIPAA Test has no members and the shared login kept getting signed out. Follow-ups in report.

Pull request

Artifacts

Activity

  • Draft
  • Ready
  • Runner started
  • Runner ended (done)