diff --git a/sdk/demos/groups/script.js b/sdk/demos/groups/script.js index a9538c07..ed6b5b4e 100644 --- a/sdk/demos/groups/script.js +++ b/sdk/demos/groups/script.js @@ -57,17 +57,8 @@ async function v3Post(action, params = {}) { return res.json() } -authButton.onclick = () => window.web10.openAuthPortal(AUTH_ORIGIN) -window.web10.authListen(() => initApp()) - -function initApp() { - authButton.innerHTML = "Log out" - authButton.onclick = () => { w.signOut(); window.location.reload() } - const t = w.readToken() - message.innerHTML = `Signed in as ${t["provider"]}/${t["username"]}` - app.classList.remove('hidden') - - // Request app contract via auth UI (not direct API) +authButton.onclick = () => { + window.web10.openAuthPortal(AUTH_ORIGIN) w.contractRequest([{ kind: 'app', app_origin: window.location.origin, @@ -87,6 +78,15 @@ function initApp() { loadManageGroups() }) } +window.web10.authListen(() => initApp()) + +function initApp() { + authButton.innerHTML = "Log out" + authButton.onclick = () => { w.signOut(); window.location.reload() } + const t = w.readToken() + message.innerHTML = `Signed in as ${t["provider"]}/${t["username"]}` + app.classList.remove('hidden') +} // Self-register fetch(`${API_ORIGIN}/v3/apps/register`, { diff --git a/sdk/demos/hello/script.js b/sdk/demos/hello/script.js index d4912be3..70fa7046 100644 --- a/sdk/demos/hello/script.js +++ b/sdk/demos/hello/script.js @@ -6,16 +6,8 @@ const isLocal = host === 'localhost' || host === '127.0.0.1' || host.endsWith('. const AUTH_ORIGIN = isLocal ? 'http://auth.localhost' : isDev ? 'https://auth.dev.web10.app' : 'https://auth.web10.app' const w = window.web10.createV3Client({ apiOrigin: isLocal ? 'http://api.localhost' : isDev ? 'https://api.dev.web10.app' : 'https://api.web10.app' }) -authButton.onclick = () => window.web10.openAuthPortal(AUTH_ORIGIN) -window.web10.authListen(() => initApp()) - -function initApp() { - authButton.innerHTML = 'log out' - authButton.onclick = () => { w.signOut(); window.location.reload() } - const t = w.readToken() - message.innerHTML = `hello ${t.provider}/${t.username},
` - - // Request app contract for basic read access +authButton.onclick = () => { + window.web10.openAuthPortal(AUTH_ORIGIN) w.contractRequest([{ kind: 'app', app_origin: window.location.origin, @@ -32,5 +24,13 @@ function initApp() { } }) } +window.web10.authListen(() => initApp()) + +function initApp() { + authButton.innerHTML = 'log out' + authButton.onclick = () => { w.signOut(); window.location.reload() } + const t = w.readToken() + message.innerHTML = `hello ${t.provider}/${t.username},
` +} if (w.isSignedIn()) initApp() \ No newline at end of file diff --git a/sdk/demos/mailer/script.js b/sdk/demos/mailer/script.js index d660bb71..c436f0db 100644 --- a/sdk/demos/mailer/script.js +++ b/sdk/demos/mailer/script.js @@ -8,16 +8,8 @@ const w = window.web10.createV3Client({ apiOrigin: isLocal ? 'http://api.localho const COLLECTION = 'mail' -authButton.onclick = () => window.web10.openAuthPortal(AUTH_ORIGIN) -window.web10.authListen(() => initApp()) - -function initApp() { - authButton.innerHTML = 'log out' - authButton.onclick = () => { w.signOut(); window.location.reload() } - const t = w.readToken() - message.innerHTML = `hello ${t.provider}/${t.username},
` - - // Request app contract for mail collection +authButton.onclick = () => { + window.web10.openAuthPortal(AUTH_ORIGIN) w.contractRequest([{ kind: 'app', app_origin: window.location.origin, @@ -35,6 +27,14 @@ function initApp() { } }) } +window.web10.authListen(() => initApp()) + +function initApp() { + authButton.innerHTML = 'log out' + authButton.onclick = () => { w.signOut(); window.location.reload() } + const t = w.readToken() + message.innerHTML = `hello ${t.provider}/${t.username},
` +} async function readMail() { try { diff --git a/sdk/demos/notes/script.js b/sdk/demos/notes/script.js index 0689f2e0..1b7e71d5 100644 --- a/sdk/demos/notes/script.js +++ b/sdk/demos/notes/script.js @@ -9,16 +9,8 @@ const w = window.web10.createV3Client({ apiOrigin: isLocal ? 'http://api.localho const COLLECTION = 'notes' let NOTES_GROUP = null -authButton.onclick = () => window.web10.openAuthPortal(AUTH_ORIGIN) -window.web10.authListen(() => initApp()) - -function initApp() { - authButton.innerHTML = 'log out' - authButton.onclick = () => { w.signOut(); window.location.reload() } - const t = w.readToken() - message.innerHTML = `hello ${t.provider}/${t.username},
` - - // Request app contract for notes collection +authButton.onclick = () => { + window.web10.openAuthPortal(AUTH_ORIGIN) w.contractRequest([{ kind: 'app', app_origin: window.location.origin, @@ -36,6 +28,14 @@ function initApp() { } }) } +window.web10.authListen(() => initApp()) + +function initApp() { + authButton.innerHTML = 'log out' + authButton.onclick = () => { w.signOut(); window.location.reload() } + const t = w.readToken() + message.innerHTML = `hello ${t.provider}/${t.username},
` +} function ensureNotesGroup(username, provider) { const groupName = `notes-${username}` diff --git a/ui/src/interfaces/Interface.tsx b/ui/src/interfaces/Interface.tsx index 02f708fd..805b98f8 100644 --- a/ui/src/interfaces/Interface.tsx +++ b/ui/src/interfaces/Interface.tsx @@ -186,25 +186,25 @@ function useInterface() { // v3 contract listening — direct postMessage, no wapiAuth wrapper I.initAuthenticator = function () { if (typeof window === 'undefined') return; + let contractReceived = false; window.addEventListener('message', (e) => { - // Contract requests are inherently cross-origin (app → auth UI). - // Only reject obviously malicious sources (null origin from sandboxed iframe). if (!e.origin) return; if (e.data?.type === 'acr' || e.data?.type === 'contract') { I.setPendingContracts(normalizeContracts(e.data, e.source)); + contractReceived = true; } }); - // Signal readiness to opener — broadcast periodically so the SDK can - // always sync before sending a contract, even when reusing an open popup - // that was opened seconds ago (user logged in, then clicked "Create Group"). if (window.opener) { + // Broadcast auth_ready until contract arrives — the app may call + // contractRequest after login, so we keep signaling readiness. const broadcastReady = () => { + if (contractReceived) return; try { window.opener.postMessage({ type: 'auth_ready' }, '*'); } catch { /* cross-origin */ } }; broadcastReady(); - const interval = setInterval(broadcastReady, 1000); + const interval = setInterval(broadcastReady, 500); window.addEventListener('beforeunload', () => clearInterval(interval)); } } @@ -722,9 +722,6 @@ function useInterface() { I.denyACR = (c: any) => I.denyContract({ ...c, kind: c.kind || 'app' }); I.removePendingACR = (c: any) => I.removePendingContract({ ...c, kind: c.kind || 'app' }); - // Return to the requesting app, logging it in. Send the current v3 token - // directly to the opener — no tiered token needed for v3. - // Do NOT close the popup — keep it alive so contractRequest can reuse it. I.goToApp = function () { const token = I.v3.state?.token; if (token && window.opener) { @@ -733,10 +730,12 @@ function useInterface() { const target = referrer ? new URL(referrer).origin : '*'; I.setStatus("Connecting…"); window.opener.postMessage({ type: 'auth', token }, target); - // Don't close — SDK needs this popup for contractRequest + window.close(); } catch { I.setStatus("Failed to connect to app."); } + } else if (window.opener) { + window.close(); } }