-
Notifications
You must be signed in to change notification settings - Fork 49
[ai-assisted] Work around COOP issues with BroadcastChannel and local… #616
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -51,15 +51,73 @@ function reauth(immediate, useFullScopes) { | |
| if(useFullScopes) { | ||
| path += "&scopes=full"; | ||
| } | ||
| // Need to do a login to get a cookie for this user; do it in a popup | ||
| window.addEventListener('message', function(e) { | ||
|
|
||
| // Track whether we've already resolved to avoid double-resolution | ||
| var resolved = false; | ||
| function resolveOnce(method) { | ||
| if (!resolved) { | ||
| console.log("INFO: Popup login resolved by: ", method); | ||
| resolved = true; | ||
| // NOTE(joe): A useful thing to do for testing is to comment out this | ||
| // cleanup(), and check which of the 3 methods are returning success | ||
| // here. cleanup() will stop others from triggering. | ||
| cleanup(); | ||
| d.resolve(reauth(true, useFullScopes)); | ||
| } | ||
| else { | ||
| console.log("INFO: Popup login resolved again (ignored): ", method); | ||
| } | ||
| } | ||
|
|
||
| // Cleanup function to remove all listeners | ||
| var channel = null; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This cleanup function doesn't fully cleanup the channel (setting it back to null) |
||
| function cleanup() { | ||
| window.removeEventListener('message', messageHandler); | ||
| window.removeEventListener('storage', storageHandler); | ||
| try { localStorage.removeItem('pyret_auth_complete'); } catch (err) {} | ||
| if (channel) { | ||
| try { channel.close(); } | ||
| finally { channel = null; } | ||
| } | ||
| } | ||
|
|
||
| // Method 1: Traditional postMessage (works when COOP allows window.opener) | ||
| function messageHandler(e) { | ||
| // e.domain appears to not be defined in Firefox | ||
| if ((e.domain || e.origin) === document.location.origin) { | ||
| d.resolve(reauth(true, useFullScopes)); | ||
| } else { | ||
| d.resolve(null); | ||
| resolveOnce("postMessage"); | ||
| } | ||
| }); | ||
| } | ||
| window.addEventListener('message', messageHandler); | ||
|
|
||
| // Method 2: BroadcastChannel (works even when COOP severs window.opener) | ||
| // This is the fallback for environments like GoGuardian that inject COOP headers | ||
| if (typeof BroadcastChannel !== 'undefined') { | ||
| try { | ||
| channel = new BroadcastChannel('pyret_auth'); | ||
| channel.onmessage = function(e) { | ||
| if (e.data && e.data.type === 'auth_complete') { | ||
| resolveOnce("Broadcast"); | ||
| } | ||
| }; | ||
| } catch (e) { | ||
| console.warn("BroadcastChannel setup failed:", e); | ||
| } | ||
| } | ||
|
|
||
| // Method 3: localStorage fallback for very old browsers without BroadcastChannel | ||
| function storageHandler(e) { | ||
| if (e.key === 'pyret_auth_complete') { | ||
| resolveOnce("localStorage"); | ||
| // Clean up the flag | ||
| try { localStorage.removeItem('pyret_auth_complete'); } catch (err) {} | ||
| } | ||
| } | ||
| // Clear any stale auth flag before opening popup | ||
| try { localStorage.removeItem('pyret_auth_complete'); } catch (e) {} | ||
| window.addEventListener('storage', storageHandler); | ||
|
|
||
| // Need to do a login to get a cookie for this user; do it in a popup | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this code set up three methods in all circumstances? If so, does cleanup actually clean them all up?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nearly! You were right that it needed to set |
||
| window.open(path); | ||
| } else { | ||
| // The user is logged in, but needs an access token from our server | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This appears to have different semantics than the if we had before, of
d.resolve(reauth(...)) or d.result(null)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's true. But the resolving-to-null case essentially never happened (that would happen if someone managed to postMessage to the page from some non-Pyret origin).
And resolving to null now makes less sense because the page is waiting for any of the mechanisms to send the response back.
A separate cancellable timer that resolves null would be the right thing. I'd be happy to add that in a separate pass (we don't have anything like that right now)