fix(GRO-2234): bounded sliding expiration for SSO portal sessions #183

Merged
Flea Flicker merged 1 commits from flea-flicker/gro-2234-portal-session-sliding-ttl into dev 2026-06-08 18:55:43 +00:00
Member

GRO-2234 — Portal Book New: impersonation session TTL → waitlist submit 401

Problem

Slow-paced Book New wizard completions (~1–2 min) hit 401 {"error":"Unauthorized"} on the final POST /api/portal/waitlist; a freshly-minted session with the same payload returns 201. The SSO-bridge portal session minted at page load was a static window with no refresh-on-activity, so a deliberately-paced multi-step wizard could lapse mid-flow.

Fix — bounded sliding expiration (preferred approach per triage)

  • src/middleware/portalSession.tsvalidatePortalSession now slides expiresAt forward to now + 30 min on each authenticated /api/portal/* request, bounded by startedAt + 8h (absolute cap). A 60s threshold avoids a DB write on every rapid request. Sliding is scoped to reason="sso-bridge" sessions only, so staff-initiated impersonation sessions sharing this middleware are untouched (AC: no staff regression).
  • src/routes/portal.ts — the session-from-auth SSO-bridge mint TTL is aligned to the 30-min idle window (matches the staff-console impersonation idle model in routes/impersonation.ts). The dev-only dev-session mint is unchanged.

Security (Phase-3, Barkley)

TTL stays bounded and is tightened, not weakened: idle window 30 min (was a 24h static mint), absolute lifetime capped at 8h from startedAt. Sliding only refreshes an already-valid active session — expired sessions are never resurrected (covered by test).

Tests

src/__tests__/portalSessionSliding.test.ts — slide-on-activity, stays valid past the original window, bounded by max lifetime, no slide for staff sessions, no resurrection of expired sessions, sub-threshold write skip. Full suite green (64 tests), typecheck + lint clean.

UAT_PLAYBOOK

Updated §4.8 — added TC-API-8.17 (SSO session slides on activity) and TC-API-8.18 (slow-wizard Book New submit returns 201).

Note for reviewer

The deployed mint was already 24h (not literally ~1 min), so this API-side sliding tightens/repairs the model but a companion groombook/web change (transparent re-mint on 401 in CustomerPortal) carries the deterministic user-facing fix and ships alongside.

cc @cpfarhood

## GRO-2234 — Portal Book New: impersonation session TTL → waitlist submit 401 ### Problem Slow-paced Book New wizard completions (~1–2 min) hit `401 {"error":"Unauthorized"}` on the final `POST /api/portal/waitlist`; a freshly-minted session with the same payload returns 201. The SSO-bridge portal session minted at page load was a static window with no refresh-on-activity, so a deliberately-paced multi-step wizard could lapse mid-flow. ### Fix — bounded sliding expiration (preferred approach per triage) - **`src/middleware/portalSession.ts`** — `validatePortalSession` now slides `expiresAt` forward to `now + 30 min` on each authenticated `/api/portal/*` request, **bounded** by `startedAt + 8h` (absolute cap). A 60s threshold avoids a DB write on every rapid request. Sliding is **scoped to `reason="sso-bridge"` sessions only**, so staff-initiated impersonation sessions sharing this middleware are untouched (AC: no staff regression). - **`src/routes/portal.ts`** — the `session-from-auth` SSO-bridge mint TTL is aligned to the 30-min idle window (matches the staff-console impersonation idle model in `routes/impersonation.ts`). The dev-only `dev-session` mint is unchanged. ### Security (Phase-3, Barkley) TTL stays **bounded and is tightened**, not weakened: idle window 30 min (was a 24h static mint), absolute lifetime capped at 8h from `startedAt`. Sliding only refreshes an *already-valid* active session — expired sessions are never resurrected (covered by test). ### Tests `src/__tests__/portalSessionSliding.test.ts` — slide-on-activity, stays valid past the original window, bounded by max lifetime, **no slide for staff sessions**, no resurrection of expired sessions, sub-threshold write skip. Full suite green (64 tests), typecheck + lint clean. ### UAT_PLAYBOOK Updated **§4.8** — added TC-API-8.17 (SSO session slides on activity) and TC-API-8.18 (slow-wizard Book New submit returns 201). ### Note for reviewer The deployed mint was already 24h (not literally ~1 min), so this API-side sliding tightens/repairs the model but a companion **groombook/web** change (transparent re-mint on 401 in `CustomerPortal`) carries the deterministic user-facing fix and ships alongside. cc @cpfarhood
Flea Flicker added 1 commit 2026-06-08 18:51:39 +00:00
fix(GRO-2234): bounded sliding expiration for SSO portal sessions
CI / Test (pull_request) Successful in 27s
CI / Lint & Typecheck (pull_request) Successful in 29s
CI / Build & Push Docker Images (pull_request) Successful in 1m10s
2a3905d2ea
Portal Book New submissions returned 401 when a customer lingered in the
multi-step wizard. SSO-bridge portal sessions (POST /api/portal/session-from-auth)
are now minted with a 30-min idle TTL and slid forward on each authenticated
/api/portal/* request, bounded by an 8h absolute cap from startedAt.

- portalSession.ts: validatePortalSession extends expiresAt to now+30m for
  reason="sso-bridge" sessions only (staff-initiated impersonation untouched),
  capped at startedAt+8h; write skipped below a 60s slide threshold.
- portal.ts: session-from-auth mint TTL aligned to the 30-min idle window
  (matches the staff-console impersonation idle model). dev-session unchanged.
- Tests: sliding extends, stays valid past original window, bounded by max
  lifetime, no-slide for staff sessions, no resurrection of expired sessions.
- UAT_PLAYBOOK.md §4.8: TC-API-8.17/8.18 (slide-on-activity, slow-wizard submit).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Flea Flicker merged commit aabedc8152 into dev 2026-06-08 18:55:43 +00:00
Sign in to join this conversation.