Clear the OAuth popup result from localStorage after handover - #1570
Open
GeiserX wants to merge 2 commits into
Open
Clear the OAuth popup result from localStorage after handover#1570GeiserX wants to merge 2 commits into
GeiserX wants to merge 2 commits into
Conversation
The callback page writes its result to localStorage as the reliable same-origin completion channel, and the opener removes it on pickup. When nothing is listening — an abandoned flow, a reloaded opener, any caller not using the React helper — nobody ever removes it, and the payload carries the identity label (an email) and, on failure, the error preview. It sat in the user's browser profile indefinitely. The page now clears its own entry: on success just before the existing auto-close, and on failure after a delay, since a failed flow deliberately keeps the window up. This cannot cost a listener the result — a `storage` event captures `newValue` at dispatch, so an opener that was notified already holds it, and the only other reader polls `popup.closed`, not storage. The tests RUN the generated script against stub globals rather than matching its source, because a string assertion passes just as well on a script that never executes, and what is at stake here is what the browser is left holding.
This was referenced Aug 12, 2026
Author
|
Context for this one: #1585 explains why this PR and twelve others exist — they came out of a single pass over credential handling, asking for each credential where it ends up, how long it stays, and who can read it once it's there. This PR stands alone and doesn't depend on any of the others. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
The OAuth popup writes its result to
localStorageas a fallback completion channel and never removes it. The payload carries the user's identity label — an email — and, on failure, the error preview. It stays parked in the browser profile.This clears the entry once the handover has had time to land.
Why the entry exists at all
The popup has three ways to hand its result back to the opener, because each one fails in a different situation:
postMessageis severed when the provider's consent page sets COOP —window.openerbecomesnull.BroadcastChannelcan be partitioned, and can be raced by the popup auto-closing.localStorageraises astorageevent on the opener, and is the reliable fallback when the other two are gone.So the write is doing real work and should stay.
What this changes
The entry is removed after the handover: shortly before the window closes on success, and after a longer delay on failure, where the window stays open for the user to read the error.
Clearing cannot cost a listener the result. A
storageevent capturesnewValueat dispatch, so an opener that has been notified already holds the payload in its handler — removing the key afterwards does not reach back and empty it.Why it matters
Leaving it parks identity data in the user's browser profile indefinitely whenever nobody is listening — which is every abandoned flow and every opener-less flow, exactly the cases where no one will ever come back to consume it.
Tests
The added tests assert the clear happens on both the success and the failure path, and that it is scheduled after the message has been dispatched rather than alongside it.
Scope
Fully independent — one file, plus tests. It touches nothing else in this series.