Auth - Token

Token authentication for iframe-embedded widgets using a backend proxy and the postMessage API. The user never sees a login screen - your page handles the token exchange behind the scenes.

Authentication Flow

The token authentication flow has four parts:

  1. Your backend endpoint proxies the Returning.AI signin API (keeps the API key secret).
  2. Your frontend script calls the backend endpoint on page load and stores the token.
  3. The widget iframe requests the token via postMessage when it needs to authenticate.
  4. Your page responds with the stored token via postMessage.

Step 1 - Create a backend proxy endpoint

Create a server-side endpoint that calls the Returning.AI signin API with your secret API key and the authenticated user's email. This endpoint should be protected by your existing authentication middleware.

Keep your API key server-side

Never expose WIDGET_API_KEY in client-side code. It should only exist in environment variables on your backend. If compromised, rotate it immediately in the admin panel.

server.js
app.post('/api/widget-auth', async (req, res) => {
  const userEmail = req.user.email;

  try {
    const response = await fetch(
      'https://prod-widgets.returning.ai/widget/{community_id}/signin',
      {
        method: 'POST',
        headers: {
          'returningai-api-key': process.env.WIDGET_API_KEY,
          'email': userEmail,
          'Content-Type': 'application/json'
        }
      }
    );
    const data = await response.json();
    res.json({ token: data.token });
  } catch (error) {
    res.status(500).json({ error: 'Authentication failed' });
  }
})

Step 2 - Add the frontend authentication script

This script runs on page load. It calls your backend endpoint to obtain a token, stores it in localStorage, and listens for postMessage token requests from the widget iframe.

widget-auth.js
async function authenticateWidget() {
  try {
    const response = await fetch('/api/widget-auth', {
      method: 'POST',
      credentials: 'include'
    });
    const data = await response.json();

    if (data.token) {
      localStorage.setItem('returning-ai-widget-token', data.token);
      console.log('Widget authenticated successfully');
    }
  } catch (error) {
    console.error('Widget authentication failed:', error);
  }
}

window.addEventListener('message', (event) => {
  if (event.origin === 'https://prod-widgets.returning.ai') {
    const { type } = event.data;
    if (type === 'RETURNINGAI_WIDGET_REQUEST_TOKEN') {
      const token = localStorage.getItem('returning-ai-widget-token');
      event.source.postMessage(
        { type: 'RETURNINGAI_WIDGET_TOKEN', value: { token, widgetId: 'YOUR_WIDGET_ID' } },
        event.origin
      );
    }
  }
});

document.addEventListener('DOMContentLoaded', authenticateWidget);

Step 3 - Listen for postMessage token requests

The widget iframe sends a RETURNINGAI_WIDGET_REQUEST_TOKEN message when it needs to authenticate or when the current token expires. The frontend script above already handles this - it validates the origin, retrieves the stored token, and responds with a RETURNINGAI_WIDGET_TOKEN message.

postMessage protocol
// The postMessage exchange looks like this:

// 1. Widget iframe → Your page:
{ type: 'RETURNINGAI_WIDGET_REQUEST_TOKEN' }

// 2. Your page → Widget iframe:
{
  type: 'RETURNINGAI_WIDGET_TOKEN',
  value: {
    token: 'eyJhbGciOiJIUzI1NiIs...',
    widgetId: 'YOUR_WIDGET_ID'
  }
}

Origin validation

Always check that event.origin matches https://prod-widgets.returning.ai before responding to postMessage events. This prevents other iframes or windows from intercepting the token.

Step 4 - Token storage

The token is stored in localStorage under the key returning-ai-widget-token. This allows the token to persist across page navigations within the same session.

BehaviorHow it works
Initial loadFrontend script calls your backend, stores the token in localStorage.
Widget requestWidget iframe sends postMessage, your script responds with the stored token.
Token expiryWidget sends another RETURNINGAI_WIDGET_REQUEST_TOKEN message. Your script should re-fetch from the backend and update localStorage.
User logoutClear the token from localStorage when the user logs out of your platform.
// Clear token on user logout
function onUserLogout() {
  localStorage.removeItem('returning-ai-widget-token');
  // Optionally reload the iframe to reset widget state
  document.getElementById('returningAiWidgetIframe')?.contentWindow?.location.reload();
}

Security Considerations

Security checklist

  • API key: Keep it server-side only. Never include it in frontend JavaScript, HTML attributes, or URL parameters.
  • Origin check: Always validate event.origin in your postMessage listener.
  • Backend auth: Protect your proxy endpoint with your existing session/token middleware so only authenticated users can request widget tokens.
  • HTTPS: Serve your page over HTTPS. The widget iframe is served over HTTPS, and mixed content will be blocked by browsers.