@@ -180,6 +180,29 @@ function createWindow() {
180180 mainWindow . loadFile ( loadPath ) . catch ( err => log ( 'Error loading file:' , err ) )
181181 }
182182
183+ // In production, intercept OAuth redirects to localhost and reload the app with auth tokens
184+ if ( ! isDev ) {
185+ mainWindow . webContents . on ( 'will-navigate' , ( event , navUrl ) => {
186+ if ( navUrl . startsWith ( 'http://localhost' ) && navUrl . includes ( 'access_token' ) ) {
187+ log ( 'Intercepting OAuth redirect in main window:' , navUrl . substring ( 0 , 80 ) + '...' )
188+ event . preventDefault ( )
189+
190+ // Extract hash/query from the redirect URL
191+ const url = new URL ( navUrl )
192+ const hashFragment = url . hash || ''
193+ const queryString = url . search || ''
194+
195+ // Load the production HTML file with the auth tokens in hash
196+ const prodPath = path . join ( __dirname , '../dist/index.html' )
197+ const normalizedPath = prodPath . replace ( / \\ / g, '/' )
198+ const fileUrl = `file:///${ normalizedPath } ${ queryString } ${ hashFragment } `
199+ log ( 'Reloading with file URL:' , fileUrl . substring ( 0 , 100 ) + '...' )
200+
201+ mainWindow ?. loadURL ( fileUrl )
202+ }
203+ } )
204+ }
205+
183206 mainWindow . webContents . setWindowOpenHandler ( ( { url } ) => {
184207 shell . openExternal ( url )
185208 return { action : 'deny' }
@@ -339,13 +362,52 @@ ipcMain.handle('auth:open-oauth-window', async (_, url: string) => {
339362 } )
340363
341364 // Listen for redirect back to our app with auth tokens
365+ let handled = false
342366 const handleAuthRedirect = ( redirectUrl : string ) => {
367+ // Prevent duplicate handling
368+ if ( handled ) return false
369+
343370 // Check if this is our callback URL (contains access_token or code)
344371 if ( redirectUrl . startsWith ( 'http://localhost' ) &&
345372 ( redirectUrl . includes ( 'access_token' ) || redirectUrl . includes ( 'code=' ) || redirectUrl . includes ( '#' ) ) ) {
373+ handled = true
346374 log ( 'OAuth redirect detected:' , redirectUrl . substring ( 0 , 100 ) + '...' )
347- // Load the callback URL in main window so Supabase can process the tokens
348- mainWindow ?. loadURL ( redirectUrl )
375+
376+ // In production, we can't load localhost - extract tokens and send to renderer
377+ if ( isDev ) {
378+ // In dev, localhost server is running, so load the URL directly
379+ mainWindow ?. loadURL ( redirectUrl )
380+ } else {
381+ // In production, extract tokens from the redirect URL and send to renderer
382+ const url = new URL ( redirectUrl )
383+ const hashFragment = url . hash || ''
384+
385+ // Parse the hash fragment to extract tokens
386+ const hashParams = new URLSearchParams ( hashFragment . substring ( 1 ) )
387+ const accessToken = hashParams . get ( 'access_token' )
388+ const refreshToken = hashParams . get ( 'refresh_token' )
389+ const expiresIn = hashParams . get ( 'expires_in' )
390+ const expiresAt = hashParams . get ( 'expires_at' )
391+
392+ if ( accessToken && refreshToken ) {
393+ log ( 'Extracted tokens, sending to renderer...' )
394+ // Send tokens to renderer to set session
395+ mainWindow ?. webContents . send ( 'auth:set-session' , {
396+ access_token : accessToken ,
397+ refresh_token : refreshToken ,
398+ expires_in : expiresIn ? parseInt ( expiresIn ) : 3600 ,
399+ expires_at : expiresAt ? parseInt ( expiresAt ) : undefined
400+ } )
401+ } else {
402+ log ( 'No tokens found in redirect, loading file with hash...' )
403+ // Fallback: try loading file with hash
404+ const prodPath = path . join ( __dirname , '../dist/index.html' )
405+ const normalizedPath = prodPath . replace ( / \\ / g, '/' )
406+ const fileUrl = `file:///${ normalizedPath } ${ hashFragment } `
407+ mainWindow ?. loadURL ( fileUrl )
408+ }
409+ }
410+
349411 authWindow . close ( )
350412 resolve ( { success : true } )
351413 return true
0 commit comments