# Add the sign-in window Download [mementom-browser.mjs](/developers/downloads/mementom-browser.mjs) into your app's browser assets. This component handles opening, focus, duplicate clicks, completion, cancellation, manual closure, timeouts and cleanup. The `@mementom/sdk/browser` package export provides the same component in a workspace installation. No server secret belongs in browser code. ## Connect your button ```js import { createAuthorizationWindow } from './mementom-browser.mjs'; const signIn = createAuthorizationWindow(); button.addEventListener('click', async () => { try { const result = await signIn.open(async ({ requestId, signal }) => { const response = await fetch('/api/sign-in/start', { method: 'POST', credentials: 'same-origin', signal, headers: { 'content-type': 'application/json' }, body: JSON.stringify({ windowRequestId: requestId }), }); if (!response.ok) throw Error('Could not start sign-in. Try again.'); return response.json(); // { authorizationUrl: issued.consumerUrl } }); // Re-read your server-owned session after any return or window closure. // A window status alone never grants access or identifies an account. await refreshSession(); if (result.status === 'timed_out') showMessage('Sign-in timed out. Try again.'); } catch (error) { showMessage(error.message); } }); // Use these for your own waiting UI and route cleanup: // signIn.focus(); signIn.cancel(); signIn.dispose(); ``` Use your app's actual route instead of the example `/api/sign-in/start`. Call `open()` directly from the click handler, before awaiting a network request; it reserves the window while the browser still recognizes the user gesture. The default timeout is ten minutes. Cancel aborts issuance and cleans up listeners; a delayed response cannot navigate a replacement window. ## Return to your app Your server stores `windowRequestId` alongside its own random OAuth state and browser session when it starts sign-in. Validate the usual state, callback and request bindings, exchange the result, and establish your app's verified session. Then your same-origin return page calls: ```js import { returnFromAuthorization } from './mementom-browser.mjs'; returnFromAuthorization({ requestId: savedWindowRequestId, // From this attempt's server-owned state. status: 'complete', // Or 'cancelled' / 'ended'. }); // If the browser refuses to close, keep this completion page in place. showMessage('You can close this window and return to your app.'); ``` The example names `refreshSession`, `showMessage`, and `savedWindowRequestId` are your app’s UI/session hooks, not SDK methods. The window component checks the return origin, source window, message type and attempt ID. Only the opaque status and public attempt ID cross windows. Keep codes, encrypted responses, account data and server credentials out of messages. Every attempt reserves a fresh popup with a unique `mementom-authorization-…` handle. The optional `windowName` selects logical controller ownership, never a reusable browser target. The component keeps the handle and attempt ID together; ordinary app navigation belongs in the requesting window. Do not navigate a ceremony popup to your homepage, account screen or journey after completion. The return helper attempts to close even when its opener is missing. If closing is blocked, leave only completion text and a Close window button in that context. The requested geometry is at most 580 × 820, centered and bounded by the available screen. New ceremonies use fresh browsing contexts. Browser settings still control zoom and whether a requested popup is displayed as a tab, especially on mobile; web code cannot override those choices. Never fall back to replacing the current app tab when popup creation is blocked. Ask the person to allow the window and retry. Browser focus or closure prompts a server-session refresh, never proof of sign-in. For local development, explicitly set the trusted `authorizationOrigin` when creating the controller. Do not accept a provider origin from query parameters or arbitrary network responses. Your server issues the ceremony; this component does not embed or reproduce Mementom credential forms. ## Backend independence The controller works with any backend language. New code can import `MementomBrowser` and call `MementomBrowser.createAuthorizationWindow` or `MementomBrowser.returnFromAuthorization`; existing named imports remain supported. Only backend endpoints exchange codes, manage sessions and verify protected results. See the [shared backend contract](/developers/guides/backend-contract.md).