Skip to content

Sign-out leaves the cached metadata (and the active-org id) in storage — the next user in the same tab is seeded with the previous user's permission-filtered app list #5198

Description

@os-steve

Found while implementing #4486 (org-scoping the MetadataProvider seed cache). Filed separately and unassigned: it is a different boundary from that card, and #4486's fix does not close it.

What

AuthProvider.signOut clears only the bearer token:

asyncsignOut(){const{ error }=awaitbetterAuth.signOut();TokenStorage.clear();
...
}

Nothing clears:

  • the objectui:metadata:* entries in sessionStorage (written by MetadataProvider), or
  • ActiveOrganizationStorage (auth-active-organization-id in localStorage).

No sign-out call site reloads the page either — AppSidebar, AppHeader, UserMenu and RemediationOverlay all just call signOut(), so the SPA keeps running and sessionStorage (which is per-tab, not per-session) survives into whatever happens next in that tab.

Why it matters

The cached app list is the per-user list: the server filters GET /api/v1/meta/:type per session (filterAppForUser, objectstack#8013), so what sits in storage is "the apps THIS user was allowed to see". If a second person signs in in the same tab — a shared or kiosk browser, a handover, a support session — MetadataProvider seeds them from that entry and they briefly read the previous user's filtered app list.

This is a cross-principal disclosure, unlike #4486, whose reader is the same signed-in user reading a workspace they are a member of. #4486 keys the seed by active organization, which is the right fix for that card and deliberately does not address this one: two users in the same org produce the same key, so the seed still hits.

The exposure is app DEFINITION metadata (name, label, icon, branding, navigation tree, required-permission names) — not record rows and not credentials. For an org-authored app the navigation tree and permission names do describe that workspace's private model, and the per-user filtering means the list itself carries "what that person had access to".

Fix shape (for whoever takes it)

Sign-out should drop the client-side caches it leaves behind — the objectui:metadata:*sessionStorage entries and ActiveOrganizationStorage — rather than relying on the tab being closed. Worth deciding at the same time whether the seed should additionally be scoped to the user/session identity, which would make the stale entry unreadable by construction instead of depending on a cleanup step being remembered on every future sign-out path. MarketplacePackagePage already has a working precedent for the purge loop (it removes every key with the objectui:metadata: prefix).

Refs #4486 (the org-keying half, in flight).

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions