Skip to content

fix(workspace): a client session that EXPIRES on a browser holding a platform login falls back to the platform identity #2261

Description

@trinity-ability

Summary

Residual of #2258, stated there rather than fixed. #2258 made the Workspace's user-initiated "Sign out" end whichever credential is live (store.signOutEverywhere() → authStore.logout() then signOut()). The expiry path was deliberately left alone: fetchRoster's 401 → endSession({expired:true}) → signOut() clears only the portal token, and if a platform JWT is also present in the browser, isPlatformSession re-derives and the client silently becomes the operator — the same class as #2258, by a different door.

Why it was left

  • Expiry is not a user act. Ending the platform session there would log out an operator working in another tab because a client's idle timeout lapsed.
  • The alternative — a persisted "suppress the platform fallback" flag — was rejected in fix(workspace): Sign out doesn't sign out — the platform JWT silently re-authenticates on refresh #2258 on evidence: the platform JWT is an axios DEFAULT header and per-request headers merge over defaults, so a flag hides the disclosure while every portal request still carries the operator's credential (see docs/memory/feature-flows/workspace-session-signout.md → Why not the suppression flag).

Reachability

Narrow. The Workspace OTP form never renders while a platform JWT exists (Portal.vue gates it on !isClientSignedIn, and isPlatformSession is already true), so this needs the platform login to arrive after the client signed in on the same browser (a shared machine where an operator logs in later), and then the client session to idle out (7d default) with that tab still open or revisited.

Acceptance Criteria

  • After a client session expires on a browser that also holds a platform login, the Workspace shows the "session expired" sign-in state — not the operator's roster — and a refresh keeps it there.
  • An operator's platform session in another tab is not ended by a client's idle timeout.
  • Whatever the mechanism, the wire agrees with the screen: no portal request carries the platform JWT while the UI says "signed out" (the fix(workspace): Sign out doesn't sign out — the platform JWT silently re-authenticates on refresh #2258 constraint).
  • Unit coverage in src/frontend/tests/unit/workspaceSession.spec.js.

Technical Notes

The honest options are both non-trivial, which is why this is filed rather than folded into #2258:

  1. A positive in-memory entry marker (workspaceEntered, NOT persisted) that isPlatformSession requires and that expiry clears — fails closed to the sign-in form when lost, and needs no localStorage; but it needs a "Continue as " escape for the operator, and a live-request guard equivalent to the flag's (the axios-default problem still applies to any state that hides a live JWT).
  2. A self-service portal logout endpoint (server-side, per-email today via dependencies.revoke_portal_sessions_for_email — ent#281) does NOT help here: the client credential is already gone; the problem is the other credential.

Related: #2258 (parent fix), ent#357 (implicit platform entry), ent#281 (operator-side revocation).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions