@@ -7,6 +7,79 @@ import {
77 ResetPasswordParams ,
88} from "./auth.types" ;
99
10+ const POPUP_AUTH_DOMAIN_REGEX =
11+ / ^ ( p r e v i e w - s a n d b o x - - | p r e v i e w - - | c h e c k p o i n t - - ) [ ^ . ] + \. / ;
12+
13+ function isPopupAuthDomain ( ) : boolean {
14+ if ( typeof window === "undefined" ) return false ;
15+ return POPUP_AUTH_DOMAIN_REGEX . test ( window . location . hostname ) ;
16+ }
17+
18+ /**
19+ * Opens a URL in a centered popup and waits for the backend to postMessage
20+ * the auth result back. On success, redirects the current window to
21+ * redirectUrl with the token params appended, preserving the same behaviour
22+ * as a normal full-page redirect flow.
23+ *
24+ * @param url - The login URL to open in the popup (should include popup_origin).
25+ * @param redirectUrl - Where to redirect after auth (the original fromUrl).
26+ * @param expectedOrigin - The origin we expect the postMessage to come from.
27+ */
28+ function loginViaPopup (
29+ url : string ,
30+ redirectUrl : string ,
31+ expectedOrigin : string
32+ ) : void {
33+ const width = 500 ;
34+ const height = 600 ;
35+ const left = Math . round ( window . screenX + ( window . outerWidth - width ) / 2 ) ;
36+ const top = Math . round ( window . screenY + ( window . outerHeight - height ) / 2 ) ;
37+
38+ const popup = window . open (
39+ url ,
40+ "base44_auth" ,
41+ `width=${ width } ,height=${ height } ,left=${ left } ,top=${ top } ,resizable=yes,scrollbars=yes`
42+ ) ;
43+
44+ if ( ! popup ) {
45+ return ;
46+ }
47+
48+ const cleanup = ( ) => {
49+ window . removeEventListener ( "message" , onMessage ) ;
50+ clearInterval ( pollTimer ) ;
51+ if ( ! popup . closed ) popup . close ( ) ;
52+ } ;
53+
54+ const onMessage = ( event : MessageEvent ) => {
55+ if ( event . origin !== expectedOrigin ) return ;
56+ if ( event . source !== popup ) return ;
57+ if ( ! event . data ?. access_token ) return ;
58+
59+ cleanup ( ) ;
60+
61+ // Append the token params to redirectUrl so the app processes them
62+ // exactly as it would from a normal OAuth callback redirect.
63+ const callbackUrl = new URL ( redirectUrl ) ;
64+ const { access_token, is_new_user } = event . data ;
65+
66+ callbackUrl . searchParams . set ( "access_token" , access_token ) ;
67+
68+ if ( is_new_user != null ) {
69+ callbackUrl . searchParams . set ( "is_new_user" , String ( is_new_user ) ) ;
70+ }
71+
72+ window . location . href = callbackUrl . toString ( ) ;
73+ } ;
74+
75+ // Only used to detect the user closing the popup before auth completes
76+ const pollTimer = setInterval ( ( ) => {
77+ if ( popup . closed ) cleanup ( ) ;
78+ } , 500 ) ;
79+
80+ window . addEventListener ( "message" , onMessage ) ;
81+ }
82+
1083/**
1184 * Creates the auth module for the Base44 SDK.
1285 *
@@ -74,7 +147,14 @@ export function createAuthModule(
74147
75148 const loginUrl = `${ options . appBaseUrl } /api${ authPath } ?${ queryParams } ` ;
76149
77- // Redirect to the provider login page
150+ // On preview/sandbox/checkpoint domains the app runs inside an iframe —
151+ // use a popup to avoid OAuth providers blocking iframe navigation.
152+ if ( isPopupAuthDomain ( ) ) {
153+ const popupLoginUrl = `${ loginUrl } &popup_origin=${ encodeURIComponent ( window . location . origin ) } ` ;
154+ return loginViaPopup ( popupLoginUrl , redirectUrl , window . location . origin ) ;
155+ }
156+
157+ // Default: full-page redirect
78158 window . location . href = loginUrl ;
79159 } ,
80160
0 commit comments