TC-AUTH-006: Session Renewal & Refresh-Token Rotation
🆕 New on stage — 2026-06-19 → 2026-07-01. This TC covers the refresh-token surface added across the release window: org-portal refresh-token rotation (#359, Jun 19), the session-expiry modal + form preservation (#223, Jun 29), renewable super-admin sessions (#531/#532/#533, Jul 1), and cross-portal refresh-cookie isolation (#539/#540, Jul 1). Before this, both portals hard-expired at the access-token TTL with no renewal. See the Test Plan banners and the Jun 19 / Jun 29 / Jul 1 release notes.
Module: Authentication & Identity — Session Renewal (org + platform portals)
Org Test User: admin@rcme.membervu.com / Admin123!
Platform Test User: superadmin@zeniark.com / SuperAdmin123!
Priority: P1 (security-adjacent — replay/revocation correctness)
Objectives
- Confirm a sitting-idle session — past the access-token TTL — stays logged in via silent refresh, on both the org portal and the admin/platform portal.
- Confirm two-tab logout: logging out in one tab causes the other tab to lose access on its next action.
- Confirm a password reset revokes all outstanding refresh tokens for that user (all existing sessions killed).
- Confirm replaying an already-rotated refresh token is rejected (single-use rotation, not just "works until expiry").
- Confirm cross-portal isolation: with both the org portal and admin portal open in the same browser, logging in/out of one does not affect the other, and a stale org cookie does not cause spurious
403s in the admin portal (#539/#540).
Scenario summary
| ID | Scenario | Key data assertion |
|---|---|---|
| AU6-01 | Org-portal session survives past access-token TTL | After the access token would have expired, a UI action succeeds with no login redirect; POST /api/auth/refresh returned a new access token in the network log. |
| AU6-02 | Admin-portal session survives past access-token TTL | Same as AU6-01 but on stg-admin.membervu.com, via POST /api/auth/platform/refresh. |
| AU6-03 | Two-tab logout | Logout in Tab 1 revokes the refresh chain server-side; Tab 2's next action returns 401 and redirects to login — it does NOT keep working. |
| AU6-04 | Password reset revokes all refresh tokens | After a password reset, a previously-valid refresh cookie from another session is rejected (401) on its next /api/auth/refresh call. |
| AU6-05 | Replayed (already-rotated) refresh token rejected | Presenting a refresh token a second time (after it has already been rotated once) returns 401, not a new access token. |
| AU6-06 | Cross-portal cookie isolation | With org + admin portals both logged in, logging out of one leaves the other's session functional; each refresh cookie is read by name (refresh_token vs platform_refresh_token). |
| AU6-07 | Bearer preferred over stale cookie (#540) | An admin action in the platform portal succeeds (no unexpected 403) even when a stale org auth_token cookie is present in the same browser. |
TTL note: the documented access-token TTL is 15 minutes on staging/prod. Local dev overrides this to 60 minutes (
docker-compose.yml JWT_EXPIRES_IN_SECONDS). Run TTL-sensitive scenarios (AU6-01/02) on staging, not local — do not assert a specific wall-clock number you can't actually guarantee outside staging; assert the behavior (stays logged in / silent refresh occurs).
➡️ Full click-by-click steps: TC-AUTH-006 (Detailed).