@@ -428,6 +428,158 @@ describe('Callback URL handling', () => {
428428
429429 expect ( client ) . toBeDefined ( )
430430 } )
431+
432+ it ( 'should use custom detectSessionInUrl function to filter out non-Supabase OAuth callbacks' , async ( ) => {
433+ // Simulate Facebook OAuth redirect with access_token in fragment
434+ window . location . href =
435+ 'http://localhost:9999/facebook/redirect#access_token=facebook-token&data_access_expiration_time=1658889585'
436+
437+ // Custom predicate to ignore Facebook OAuth redirects
438+ const detectSessionInUrlFn = jest . fn ( ( url : URL , params : { [ key : string ] : string } ) => {
439+ // Ignore Facebook OAuth redirects
440+ if ( url . pathname === '/facebook/redirect' ) return false
441+ // Default behavior for other URLs
442+ return Boolean ( params . access_token || params . error_description )
443+ } )
444+
445+ const client = new ( require ( '../src/GoTrueClient' ) . default ) ( {
446+ url : 'http://localhost:9999' ,
447+ detectSessionInUrl : detectSessionInUrlFn ,
448+ autoRefreshToken : false ,
449+ storage : mockStorage ,
450+ } )
451+
452+ await client . initialize ( )
453+
454+ // The custom function should have been called
455+ expect ( detectSessionInUrlFn ) . toHaveBeenCalled ( )
456+ expect ( detectSessionInUrlFn ) . toHaveBeenCalledWith (
457+ expect . any ( URL ) ,
458+ expect . objectContaining ( { access_token : 'facebook-token' } )
459+ )
460+
461+ // Session should be null because we filtered out the Facebook callback
462+ const { data } = await client . getSession ( )
463+ expect ( data . session ) . toBeNull ( )
464+ } )
465+
466+ it ( 'should process Supabase callbacks when custom detectSessionInUrl returns true' , async ( ) => {
467+ // Simulate Supabase OAuth redirect
468+ window . location . href =
469+ 'http://localhost:9999/auth/callback#access_token=supabase-token&refresh_token=test-refresh&expires_in=3600&token_type=bearer&type=implicit'
470+
471+ // Mock fetch for user info
472+ mockFetch . mockImplementation ( ( url : string ) => {
473+ if ( url . includes ( '/user' ) ) {
474+ return Promise . resolve ( {
475+ ok : true ,
476+ json : ( ) =>
477+ Promise . resolve ( {
478+ id : 'test-user' ,
479+ 480+ created_at : new Date ( ) . toISOString ( ) ,
481+ } ) ,
482+ } )
483+ }
484+ return Promise . resolve ( {
485+ ok : true ,
486+ json : ( ) =>
487+ Promise . resolve ( {
488+ access_token : 'supabase-token' ,
489+ refresh_token : 'test-refresh' ,
490+ expires_in : 3600 ,
491+ token_type : 'bearer' ,
492+ user : { id : 'test-user' } ,
493+ } ) ,
494+ } )
495+ } )
496+
497+ // Custom predicate that allows Supabase callbacks but not Facebook
498+ const detectSessionInUrlFn = jest . fn ( ( url : URL , params : { [ key : string ] : string } ) => {
499+ if ( url . pathname === '/facebook/redirect' ) return false
500+ return Boolean ( params . access_token || params . error_description )
501+ } )
502+
503+ const client = new ( require ( '../src/GoTrueClient' ) . default ) ( {
504+ url : 'http://localhost:9999' ,
505+ detectSessionInUrl : detectSessionInUrlFn ,
506+ autoRefreshToken : false ,
507+ storage : mockStorage ,
508+ } )
509+
510+ await client . initialize ( )
511+
512+ // The custom function should have been called and returned true
513+ expect ( detectSessionInUrlFn ) . toHaveBeenCalled ( )
514+ expect ( detectSessionInUrlFn . mock . results [ 0 ] . value ) . toBe ( true )
515+
516+ // Session should be set because we allowed this callback
517+ const { data } = await client . getSession ( )
518+ expect ( data . session ) . toBeDefined ( )
519+ expect ( data . session ?. access_token ) . toBe ( 'supabase-token' )
520+ } )
521+
522+ it ( 'should return error when custom detectSessionInUrl function throws' , async ( ) => {
523+ window . location . href = 'http://localhost:9999/callback#access_token=test-token'
524+
525+ // Reset storage state from previous tests
526+ storedSession = null
527+
528+ // Custom predicate that throws an error
529+ const detectSessionInUrlFn = jest . fn ( ( ) => {
530+ throw new Error ( 'Custom predicate error' )
531+ } )
532+
533+ const client = new ( require ( '../src/GoTrueClient' ) . default ) ( {
534+ url : 'http://localhost:9999' ,
535+ detectSessionInUrl : detectSessionInUrlFn ,
536+ autoRefreshToken : false ,
537+ storage : mockStorage ,
538+ } )
539+
540+ // initialize() catches errors and returns them wrapped in AuthUnknownError
541+ const { error } = await client . initialize ( )
542+
543+ expect ( detectSessionInUrlFn ) . toHaveBeenCalled ( )
544+ expect ( error ) . toBeDefined ( )
545+ expect ( error ?. message ) . toBe ( 'Unexpected error during initialization' )
546+ expect ( error ?. originalError ?. message ) . toBe ( 'Custom predicate error' )
547+ } )
548+
549+ it ( 'should use default behavior when detectSessionInUrl is true (boolean)' , async ( ) => {
550+ window . location . href =
551+ 'http://localhost:9999/callback#access_token=test-token&refresh_token=test-refresh&expires_in=3600&token_type=bearer&type=implicit'
552+
553+ // Mock fetch for user info
554+ mockFetch . mockImplementation ( ( url : string ) => {
555+ if ( url . includes ( '/user' ) ) {
556+ return Promise . resolve ( {
557+ ok : true ,
558+ json : ( ) =>
559+ Promise . resolve ( {
560+ id : 'test-user' ,
561+ 562+ created_at : new Date ( ) . toISOString ( ) ,
563+ } ) ,
564+ } )
565+ }
566+ return Promise . resolve ( { ok : true , json : ( ) => Promise . resolve ( { } ) } )
567+ } )
568+
569+ const client = new ( require ( '../src/GoTrueClient' ) . default ) ( {
570+ url : 'http://localhost:9999' ,
571+ detectSessionInUrl : true , // Boolean true - use default behavior
572+ autoRefreshToken : false ,
573+ storage : mockStorage ,
574+ } )
575+
576+ await client . initialize ( )
577+
578+ // Should process the callback with default behavior
579+ const { data } = await client . getSession ( )
580+ expect ( data . session ) . toBeDefined ( )
581+ expect ( data . session ?. access_token ) . toBe ( 'test-token' )
582+ } )
431583} )
432584
433585describe ( 'GoTrueClient BroadcastChannel' , ( ) => {
0 commit comments