Configuration

PostMessage Events Reference

Every event the EmbedAuth iframe emits over window.postMessage — types, payloads, when they fire, the origin check you must perform on all of them, and how to refresh tokens.

3 min readUpdated October 9, 2026

The embedded iframe communicates with your page exclusively through window.postMessage. This page is the complete reference for the events it emits and the payload each one carries.

Subscribing

Register one listener on window and branch on event.data.type:

window.addEventListener('message', (event) => {
  // 1. ALWAYS verify the sender's origin first
  if (event.origin !== 'https://cdn.embedauth.com') return;

  // 2. Then branch on the event type
  switch (event.data.type) {
    case 'authframe:auth-success': /* ... */ break;
    case 'authframe:auth-error':   /* ... */ break;
    // ...
  }
});

Messages come from https://cdn.embedauth.com: the embedauth.com/embed/… URL you put in the iframe redirects to the widget's copy on EmbedAuth's CDN, and a message's origin is the page that sent it.

The origin check is mandatory. Any script running in any frame can post a message into your window; without the event.origin guard, a malicious page could send a forged auth-success and trick your code into treating an attacker as signed in. The origin check — combined with server-side token verification — is what makes the integration safe.

Event catalogue

Every message has the shape { type: string, data: object }.

authframe:auth-success

Fires when the user successfully signs in or signs up, by any method.

{
  type: 'authframe:auth-success',
  data: {
    token: string;          // RS256 JWT — verify on your server
    refreshToken: string;   // single-use; see "Refreshing tokens" below
    user: { id: string; email: string; name: string | null; avatar: string | null };
  }
}

The token is the only value you should trust, and only after your backend verifies it. The user object is a convenience for immediate UI; do not make authorization decisions from it.

Once the app has at least one domain configured, this message is posted with your page's exact origin as its target, and only when that origin is on the app's domain list. A page on any other site that frames the widget never receives it.

authframe:auth-error

Fires when a sign-in succeeded inside the widget but the tokens could not be handed over, because the page framing the widget is not on the app's domain list.

{
  type: 'authframe:auth-error',
  data: { error: string }
}

If you see this on your own site, add the page's domain on the app's Domains page in the dashboard. Ordinary failures — a wrong password, an expired code, a cancelled OAuth popup — are shown to the user inside the widget and are not posted to your page.

authframe:mode-change

Fires when the user switches between forms (for example from sign-in to sign-up).

{ type: 'authframe:mode-change', data: { mode: 'signin' | 'signup' | 'forgot-password' | 'magic-link' | 'otp' } }

Handy if you display a heading outside the frame that should track the current form.

authframe:resize

Fires whenever the form's content height changes — different forms, validation errors, and expanded sections all change it.

{ type: 'authframe:resize', data: { height: number } }

Apply height to your iframe element to eliminate inner scrollbars. See the resize helpers in the React and Vue guides.

authframe:loaded

Fires once the iframe has finished loading and is ready.

{ type: 'authframe:loaded', data: { clientId: string; mode: string } }

Use it to hide your own loading spinner or to confirm the embed initialized with the mode you expected.

Refreshing tokens

The access token expires (after one hour by default). To get a new one without asking the user to sign in again, post the refresh token to EmbedAuth:

const res = await fetch('https://embedauth.com/api/embed/auth/refresh', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ clientId: 'YOUR_CLIENT_ID', refreshToken }),
});
// 200 → { token, refreshToken, user }   401 → sign the user in again

Each refresh token works exactly once. The response carries a new refresh token; store it in place of the old one. Presenting a refresh token that was already used is treated as theft: EmbedAuth revokes every refresh token descended from the same sign-in, and the user has to sign in again. If two tabs share one refresh token, make sure only one of them refreshes it.

The refresh token is a long-lived credential, so keep it out of localStorage. Holding it in memory works for a single page; better still, send it to your own server along with the access token and refresh from there.

Signing out

The sign-in widget has no sign-out button and sends no sign-out event: signing out happens in your app. Discard the tokens you hold and end your own session. To end a session on EmbedAuth's side too — so its refresh token stops working — revoke it from the app's Sessions page in the dashboard, or let users sign their own devices out with the account widget.

Reference table

EventPayloadFires when
authframe:auth-success{ token, refreshToken, user }Sign-in or sign-up succeeds
authframe:auth-error{ error }Tokens withheld from a page not on the app's domain list
authframe:mode-change{ mode }User switches forms
authframe:resize{ height }Content height changes
authframe:loaded{ clientId, mode }Iframe finishes loading

Next steps