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

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>
This commit is contained in:
Flea Flicker
2026-06-08 18:51:23 +00:00
parent ca62fb8ef6
commit 2a3905d2ea
4 changed files with 238 additions and 3 deletions
+2
View File
@@ -282,6 +282,8 @@ This means:
| TC-API-8.14 | Portal pet update — non-owner blocked (GRO-2187) | `PATCH /api/portal/pets/{petId}` for a pet owned by a different client, using another client's portal session | 403 Forbidden (or 404 if pet id is unknown); no mutation persisted |
| TC-API-8.15 | Portal pet update — invalid enum rejected (GRO-2187) | `PATCH /api/portal/pets/{petId}` with `coatType: "fluffy"` or `petSizeCategory: "gigantic"` | 422 Unprocessable Entity; pet unchanged |
| TC-API-8.16 | Portal pet update — malformed (non-UUID) petId returns 404 (GRO-2203) | With a valid portal session, `PATCH /api/portal/pets/not-a-uuid` with header `X-Impersonation-Session-Id` and body `{"coatType":"short"}` | 404 Not Found with body `{"error":"Not found"}` (was an unhandled 500 from the Postgres uuid cast in GRO-2203; mirrors the GRO-2014 guard). No mutation persisted |
| TC-API-8.17 | SSO portal session slides on activity (GRO-2234) | Establish a portal session (TC-API-8.8). Note the returned `sessionId`. Make any authenticated portal call (e.g. `GET /api/portal/me`) several times spaced over ≥1 minute, each with `X-Impersonation-Session-Id: {sessionId}`. | Every call returns 200; the session's `expiresAt` is extended (slid forward to ~30 min from each request) so the session stays valid during continuous use — it does NOT lapse mid-session. SSO-bridge sessions mint with a 30-min idle TTL bounded by an 8h absolute cap from `startedAt`. |
| TC-API-8.18 | Slow-wizard Book New submit succeeds (GRO-2234) | Establish a portal session (TC-API-8.8). Wait >2 minutes while making at least one intervening authenticated portal call (mimicking the multi-step Book New wizard: pet/service/groomer/date GETs). Then `POST /api/portal/waitlist` with a valid pet+service payload and the same `X-Impersonation-Session-Id`. | 201 Created — the deliberately-paced wizard no longer 401s on submit because activity slid the session forward. (Regression guard for the GRO-2234 "session TTL too short → 401" defect.) |
### 4.9 Waitlist