@@ -51,15 +51,73 @@ function reauth(immediate, useFullScopes) {
5151 if ( useFullScopes ) {
5252 path += "&scopes=full" ;
5353 }
54- // Need to do a login to get a cookie for this user; do it in a popup
55- window . addEventListener ( 'message' , function ( e ) {
54+
55+ // Track whether we've already resolved to avoid double-resolution
56+ var resolved = false ;
57+ function resolveOnce ( method ) {
58+ if ( ! resolved ) {
59+ console . log ( "INFO: Popup login resolved by: " , method ) ;
60+ resolved = true ;
61+ // NOTE(joe): A useful thing to do for testing is to comment out this
62+ // cleanup(), and check which of the 3 methods are returning success
63+ // here. cleanup() will stop others from triggering.
64+ cleanup ( ) ;
65+ d . resolve ( reauth ( true , useFullScopes ) ) ;
66+ }
67+ else {
68+ console . log ( "INFO: Popup login resolved again (ignored): " , method ) ;
69+ }
70+ }
71+
72+ // Cleanup function to remove all listeners
73+ var channel = null ;
74+ function cleanup ( ) {
75+ window . removeEventListener ( 'message' , messageHandler ) ;
76+ window . removeEventListener ( 'storage' , storageHandler ) ;
77+ try { localStorage . removeItem ( 'pyret_auth_complete' ) ; } catch ( err ) { }
78+ if ( channel ) {
79+ try { channel . close ( ) ; }
80+ finally { channel = null ; }
81+ }
82+ }
83+
84+ // Method 1: Traditional postMessage (works when COOP allows window.opener)
85+ function messageHandler ( e ) {
5686 // e.domain appears to not be defined in Firefox
5787 if ( ( e . domain || e . origin ) === document . location . origin ) {
58- d . resolve ( reauth ( true , useFullScopes ) ) ;
59- } else {
60- d . resolve ( null ) ;
88+ resolveOnce ( "postMessage" ) ;
6189 }
62- } ) ;
90+ }
91+ window . addEventListener ( 'message' , messageHandler ) ;
92+
93+ // Method 2: BroadcastChannel (works even when COOP severs window.opener)
94+ // This is the fallback for environments like GoGuardian that inject COOP headers
95+ if ( typeof BroadcastChannel !== 'undefined' ) {
96+ try {
97+ channel = new BroadcastChannel ( 'pyret_auth' ) ;
98+ channel . onmessage = function ( e ) {
99+ if ( e . data && e . data . type === 'auth_complete' ) {
100+ resolveOnce ( "Broadcast" ) ;
101+ }
102+ } ;
103+ } catch ( e ) {
104+ console . warn ( "BroadcastChannel setup failed:" , e ) ;
105+ }
106+ }
107+
108+ // Method 3: localStorage fallback for very old browsers without BroadcastChannel
109+ function storageHandler ( e ) {
110+ if ( e . key === 'pyret_auth_complete' ) {
111+ resolveOnce ( "localStorage" ) ;
112+ // Clean up the flag
113+ try { localStorage . removeItem ( 'pyret_auth_complete' ) ; } catch ( err ) { }
114+ }
115+ }
116+ // Clear any stale auth flag before opening popup
117+ try { localStorage . removeItem ( 'pyret_auth_complete' ) ; } catch ( e ) { }
118+ window . addEventListener ( 'storage' , storageHandler ) ;
119+
120+ // Need to do a login to get a cookie for this user; do it in a popup
63121 window . open ( path ) ;
64122 } else {
65123 // The user is logged in, but needs an access token from our server
0 commit comments