subdomain callback, which sets our cookie const AUTH_BASE = 'https://magicloops.dev/api/sso/authorize'; const LOGIN_BASE = 'https://magicloops.dev/login'; const CALLBACK_URL = `https://${window.location.host}/api/auth/callback`; const authorizeUrl = new URL(AUTH_BASE); authorizeUrl.searchParams.set('subdomain', SUBDOMAIN); authorizeUrl.searchParams.set('redirect_to', CALLBACK_URL); const loginUrl = new URL(AUTH_BASE); loginUrl.searchParams.set('subdomain', SUBDOMAIN); loginUrl.searchParams.set('redirect_to', CALLBACK_URL); /** * Make a GET request to /api/auth/verify to see if subdomain_jwt is valid. * Returns { userId, exp } if OK, or 401 if missing/expired. */ async function verifySession() { const resp = await fetch(VERIFY_ENDPOINT, { method: 'GET' }); if (!resp.ok) { throw new Error('Verify failed: ' + resp.status); } return await resp.json(); // { userId, exp } } /** * Perform a "silent" SSO attempt by loading the authorize endpoint * in a hidden iframe. If the main domain session is still valid, * the user won't see a login prompt. On success, our callback sets the cookie. * * We'll wait for the iframe to load & then re-check our subdomain /verify. */ async function doSilentAuth() { return new Promise((resolve, reject) => { const frame = document.getElementById('silentAuthFrame'); frame.src="/?originalUrl=https%3A%2F%2Fmodo.magicloops.app%2FauthorizeUrl.toString()%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2F%2F%2520We"ll listen to "load" event. Once it loads, we check /verify again. frame.onload = async function() { try { const data = await verifySession(); resolve(data); // userId, exp } catch (err) { reject(err); } }; // Optional: handle error event frame.onerror = () => { reject(new Error('Hidden iframe load error')); }; }); } /** * Open a popup with the magicloops.dev authorize URL. * If the user needs to log in, they'll see the Supabase screen. * Once the callback sets the JWT, we poll for closure & re-verify. */ async function doPopupAuth() { return new Promise((resolve, reject) => { const popup = window.open( loginUrl.toString(), 'authPopup', 'width=600,height=700' ); if (!popup) { return reject(new Error('Popup blocked')); } const timer = setInterval(async () => { // If popup closed, check if we have a valid session if (popup.closed) { clearInterval(timer); try { const data = await verifySession(); resolve(data); } catch (err) { reject(err); } } }, 500); }); } /** * The main login flow attempt: * 1) Try verifying subdomain_jwt * 2) If missing/expired, attempt SILENT iframe * 3) If silent fails => fallback to POPUP */ async function ensureLoggedIn() { try { const info = await verifySession(); // Already valid. Return info { userId, exp } return info; } catch (err) { // Not logged in or token expired console.log('No valid subdomain JWT, attempting silent auth...'); } // Attempt silent auth in hidden iframe try { const info = await doSilentAuth(); console.log('Silent auth success:', info); return info; } catch (err) { console.warn('Silent auth failed, opening popup...'); } // Fallback to popup try { // const info = await doPopupAuth(); // console.log('Popup auth success:', info); // return info; } catch (err) { console.error('Popup login failed or canceled:', err); throw err; } } /** * Schedule an automatic token refresh a bit before the token expires. * For example, refresh 1 minute before exp or halfway—your choice. */ function scheduleAutoRefresh(exp) { // Current time in seconds const nowSeconds = Math.floor(Date.now() / 1000); // Refresh 30 seconds before actual expiration, for safety const refreshTime = Math.max((exp - 30) - nowSeconds, 1); const refreshMs = refreshTime * 1000; console.log('Scheduling refresh in', refreshTime, 'seconds'); setTimeout(async () => { console.log('Attempting token refresh...'); try { const info = await doSilentAuth(); console.log('Token silently refreshed:', info); scheduleAutoRefresh(info.exp); } catch (err) { console.warn('Silent refresh failed, fallback to popup...'); // If silent refresh fails, we do popup to re-auth try { // const info = await doPopupAuth(); // console.log('Popup refresh success:', info); // scheduleAutoRefresh(info.exp); } catch (popErr) { console.error('Popup refresh also failed. User not logged in:', popErr); } } }, refreshMs); } // Now, run the main flow on page load (async function main() { try { console.log('Hello Magic App User!'); const info = await ensureLoggedIn(); console.log('User is logged in:', info); // After we confirm a valid JWT, we schedule the refresh scheduleAutoRefresh(info.exp); } catch (err) { console.error('User is not logged in and refused to log in:', err); // Show a message or redirect if needed } })(); })();