diff --git a/.changeset/oauth-popup-localstorage-residue.md b/.changeset/oauth-popup-localstorage-residue.md new file mode 100644 index 000000000..443f6ffa5 --- /dev/null +++ b/.changeset/oauth-popup-localstorage-residue.md @@ -0,0 +1,9 @@ +--- +"executor": patch +--- + +**The OAuth popup clears its result out of `localStorage` after handing it over** + +The popup writes its result to `localStorage` as the fallback completion channel, because `postMessage` is severed when a provider's consent page sets COOP and `BroadcastChannel` can be partitioned or raced by the auto-close. Nothing removed that entry afterwards, so the payload — which carries the identity label, an email, and on failure the error preview — stayed parked in the user's browser profile. + +The entry is now cleared once the handover has had time to land. This cannot cost a listener the result: a `storage` event captures `newValue` at dispatch, so an opener that has been notified already holds it. diff --git a/packages/core/api/src/oauth-popup.test.ts b/packages/core/api/src/oauth-popup.test.ts index 12efa6971..77278a2fa 100644 --- a/packages/core/api/src/oauth-popup.test.ts +++ b/packages/core/api/src/oauth-popup.test.ts @@ -149,6 +149,80 @@ describe("popupDocument", () => { expect(script).toContain("channel\\u003c/script\\u003e"); }); + // The assertions below RUN the generated script against stub globals rather + // than matching its source text. A string check would pass on a script that + // never executes — and the property at stake here is what the browser is left + // holding, which only running it can show. + const runPopupScript = (html: string) => { + const script = / `; };