Skip to content

Commit d4873a3

Browse files
roymilohclaude
andcommitted
Use popup for OAuth on preview/sandbox/checkpoint domains
On preview and sandbox domains the app runs inside an iframe, which causes OAuth providers to block the normal full-page redirect via X-Frame-Options. Instead, open the provider flow in a popup, poll until it lands back on our origin, then mirror that callback URL (including the access_token query param) in the iframe so it processes the token exactly as a normal redirect would. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent cea3a43 commit d4873a3

File tree

1 file changed

+64
-1
lines changed

1 file changed

+64
-1
lines changed

src/modules/auth.ts

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,63 @@ import {
77
ResetPasswordParams,
88
} from "./auth.types";
99

10+
const POPUP_AUTH_DOMAIN_REGEX =
11+
/^(preview-sandbox--|preview--|checkpoint--)[^.]+\.base44\.app$/;
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, once the OAuth provider redirects
20+
* back to our origin, mirrors that callback URL in the current window so the
21+
* iframe processes the access_token query param exactly as a normal redirect
22+
* would.
23+
*
24+
* @param url - The URL to open in the popup.
25+
*/
26+
function loginViaPopup(url: string): void {
27+
const width = 500;
28+
const height = 600;
29+
const left = Math.round(window.screenX + (window.outerWidth - width) / 2);
30+
const top = Math.round(window.screenY + (window.outerHeight - height) / 2);
31+
32+
const popup = window.open(
33+
url,
34+
"base44_auth",
35+
`width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`
36+
);
37+
38+
if (!popup) {
39+
return;
40+
}
41+
42+
const pollTimer = setInterval(() => {
43+
if (popup.closed) {
44+
clearInterval(pollTimer);
45+
return;
46+
}
47+
48+
try {
49+
// Accessing popup.location.href throws a cross-origin error while the
50+
// OAuth provider's pages are open — that's expected and means the flow
51+
// is still in progress. Once it stops throwing, the popup has landed
52+
// back on our origin with the callback URL (e.g. ?access_token=...).
53+
const callbackUrl = popup.location.href;
54+
if (new URL(callbackUrl).origin === window.location.origin) {
55+
clearInterval(pollTimer);
56+
popup.close();
57+
// Redirect the iframe to the same URL the popup landed on so it
58+
// processes the token from the query params as it normally would.
59+
window.location.href = callbackUrl;
60+
}
61+
} catch {
62+
// Still on the OAuth provider's domain — keep polling
63+
}
64+
}, 300);
65+
}
66+
1067
/**
1168
* Creates the auth module for the Base44 SDK.
1269
*
@@ -68,7 +125,13 @@ export function createAuthModule(
68125
redirectUrl
69126
)}`;
70127

71-
// Redirect to the provider login page
128+
// On preview/sandbox/checkpoint domains the app runs inside an iframe —
129+
// use a popup to avoid OAuth providers blocking iframe navigation.
130+
if (isPopupAuthDomain()) {
131+
return loginViaPopup(loginUrl);
132+
}
133+
134+
// Default: full-page redirect
72135
window.location.href = loginUrl;
73136
},
74137

0 commit comments

Comments
 (0)