fix(plugin): clear brokered session state on logout

sessionStorage outlives a logout inside the same tab, so the keys stay readable
after the next user signs in. They are namespaced by user id, which stops the new
user's plugins from reading them, but the data is still sitting in the tab — the
same reason logout already drops the appearance snapshot, the SW caches and the
user-scoped offline DB. Purge them there too.

Three things around it, from reading the handlers next to the new ones:

- The inactive-plugin purge ran before the store was updated, so a sessionStorage
  failure cost us the plugin list we had just fetched. Commit first, purge after.
- The session handlers answered without checking requestId, unlike confirm and
  geolocation — an answer the frame can never match is worth skipping.
- The mock host threw plain Errors while the real bridge rejects with .code set,
  and checked the key before the trip scope where the host does the reverse, so a
  plugin's error handling could pass its tests and still break in the app.
This commit is contained in:
Maurice
2026-07-26 11:39:00 +02:00
committed by Maurice
parent 2068133de2
commit c8369bd14b
6 changed files with 87 additions and 12 deletions
@@ -416,6 +416,9 @@ export default function PluginFrame({ pluginId, tripId = null, placeId = null, d
break
}
case 'trek:session:clear': {
// Same guard the other request/response handlers apply: without an id
// the frame could never match our answer to its call.
if (typeof msg.requestId !== 'string' || !msg.requestId) break
const scope = msg.scope === 'trip' ? 'trip' : 'plugin'
// tripId required if we are on a trip-scoped key.
@@ -437,6 +440,7 @@ export default function PluginFrame({ pluginId, tripId = null, placeId = null, d
case 'trek:session:get':
case 'trek:session:set':
case 'trek:session:remove': {
if (typeof msg.requestId !== 'string' || !msg.requestId) break
const scope = msg.scope === 'trip' ? 'trip' : 'plugin'
if (scope === 'trip' && tripId == null) {
post({ type: 'trek:error', requestId: msg.requestId, code: 'NO_TRIP_CONTEXT', message: 'trip session storage requires a trip context' })
@@ -552,7 +556,7 @@ export default function PluginFrame({ pluginId, tripId = null, placeId = null, d
// The frame is going away (or re-bridging) — never leave a live GPS watch behind.
if (geoWatchRef.current != null) { navigator.geolocation.clearWatch(geoWatchRef.current); geoWatchRef.current = null }
}
}, [pluginId, tripId, fill, navigate, postFrame, buildContext])
}, [pluginId, tripId, fill, navigate, postFrame, buildContext, userId])
// Hosts swap pluginId in place (tab bar, /plugins/:id route) — the iframe below
// is keyed so the document is a fresh first load, and the per-plugin bridge