# How to integrate your app as iframe in B2CORE

B2CORE supports embedding third-party web applications directly into the client UI via an iframe. When a custom menu item is configured with the **Iframe** behavior, B2CORE loads your application inside the interface, providing a seamless experience for clients without leaving the platform.

This guide covers two aspects: configuring the custom menu item in the B2CORE Back Office, and preparing your application to work correctly inside the B2CORE iframe.

## Prerequisites

Before proceeding, ensure the following:

{% hint style="info" %}

* Your application is accessible via HTTPS.
* You have access to the B2CORE Back Office with the `Update menu` permission (for details, refer to [How to add a user group and grant permissions](/how-to-articles/manage-system-settings/how-to-add-a-user-group-and-grant-permissions.md)).
* You are familiar with the [How to add custom menu items](/how-to-articles/manage-advertising-options/how-to-add-custom-menu-items.md) procedure.
  {% endhint %}

## Step 1. Configure your server to allow iframe embedding

By default, most web servers and frameworks prevent pages from being embedded in iframes on other domains. To allow B2CORE to load your application, you must configure the appropriate HTTP response headers on your server.

You need to set **one or both** of the following headers:

### Content-Security-Policy

The `Content-Security-Policy` header with the `frame-ancestors` directive controls which origins are allowed to embed your page. Set this header to include your B2CORE instance origin:

```
Content-Security-Policy: frame-ancestors 'self' https://portal.example.com
```

Replace `https://portal.example.com` with the actual origin of the B2CORE UI used by your clients. You can specify multiple origins separated by spaces if your application needs to be embedded across several B2CORE instances.

For more information, refer to the [Content-Security-Policy documentation on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy).

### X-Frame-Options

The `X-Frame-Options` header is an older mechanism that achieves a similar result. If you use it, set it to `ALLOW-FROM` with your B2CORE origin:

```
X-Frame-Options: ALLOW-FROM https://portal.example.com
```

{% hint style="warning" %}
The `X-Frame-Options: ALLOW-FROM` directive is not supported by all browsers. It is recommended to use the `Content-Security-Policy` header with the `frame-ancestors` directive as the primary mechanism and include `X-Frame-Options` only as a fallback for older clients.

For more information, refer to the [X-Frame-Options documentation on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Frame-Options).
{% endhint %}

## Step 2. Add a custom menu item in the B2CORE Back Office

To make your application accessible to clients, add a custom menu item with the iframe behavior:

{% stepper %}
{% step %}
Navigate to **Promotion** > **Menu**.
{% endstep %}

{% step %}
Click the **eye** icon in the **General** row to view the menu tree.
{% endstep %}

{% step %}
Click **+Create** in the upper-right page corner.
{% endstep %}

{% step %}
Fill in the required fields:

* In the **Name** field, enter a unique name for the menu item.
* In the **Caption** field, enter the label that clients will see in the menu.
* In the **External URL** field, specify the URL of your application.
* In the **Icon** field, specify the URL of an SVG icon (16x16 px, monochrome, transparent background).
* In the **Custom Behavior** dropdown, select **Iframe**.
  {% endstep %}

{% step %}
Configure optional restrictions if needed:

* To limit visibility to specific verification levels, select the appropriate levels in the **Verification Level Allowance** dropdown.
* To limit visibility to specific client types, select the corresponding types in the **Client Type Allowance** dropdown.
  {% endstep %}

{% step %}
Enable the **Visible** checkbox to make the item appear in the menu.
{% endstep %}

{% step %}
Click **Save** to add the custom menu item.
{% endstep %}
{% endstepper %}

When clients click this menu item, your application will load inside an iframe within the B2CORE UI.

{% hint style="info" %}
Two other behavior options are available for custom menu items: **Same tab** (opens the URL in the current browser tab) and **New tab** (opens the URL in a new browser tab). The iframe option is the only one that embeds your application within the B2CORE interface.
{% endhint %}

## Step 3 (optional). Implement the postMessage communication protocol

If your application needs to identify the authenticated B2CORE user, match the B2CORE UI theme, or follow the language selected by the user, you can implement the `postMessage` communication protocol described below. This step is optional — if your application does not require user authentication, theme synchronization, or language synchronization, you can skip it.

### Message reference

#### Messages from your application to B2CORE

| Message type              | Description                                                          | Payload                               |
| ------------------------- | -------------------------------------------------------------------- | ------------------------------------- |
| `embed-iframe-ready`      | Signals that the iframe has loaded and is ready to receive messages. | `{ type: "embed-iframe-ready" }`      |
| `embed-request-jwt-token` | Requests a JWT authentication token from B2CORE.                     | `{ type: "embed-request-jwt-token" }` |

#### Messages from B2CORE to your application

| Message type            | Description                                                                                     | Payload                                                                       |
| ----------------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `embed-theme-change`    | Sent whenever the B2CORE UI theme changes (and once immediately after `embed-iframe-ready`).    | `{ type: "embed-theme-change", theme: "dark-theme" \| "light-theme" }`        |
| `embed-language-change` | Sent whenever the B2CORE UI language changes (and once immediately after `embed-iframe-ready`). | `{ type: "embed-language-change", lang: "<language-code>" }`                  |
| `embed-jwt-token`       | Successful response to a token request.                                                         | `{ type: "embed-jwt-token", token: "<jwt-string>", expiresAt: "<ISO-8601>" }` |
| `embed-jwt-token-error` | Error response when token generation fails.                                                     | `{ type: "embed-jwt-token-error", error: "<error-message>" }`                 |

The `lang` field contains a lowercase ISO 639-1 language code (for example, `en`, `de`, `ar`) matching the language currently selected in the B2CORE UI.

### Communication flow

The sequence of messages between your application and B2CORE follows this pattern:

```mermaid
sequenceDiagram
    participant App as Your App (iframe)
    participant B2CORE as B2CORE (host)

    App->>B2CORE: embed-iframe-ready
    B2CORE-->>App: embed-theme-change (initial theme)
    B2CORE-->>App: embed-language-change (initial language)
    App->>B2CORE: embed-request-jwt-token
    alt success
        B2CORE-->>App: embed-jwt-token
    else failure
        B2CORE-->>App: embed-jwt-token-error
    end
    Note over App,B2CORE: Token nears expiry...
    App->>B2CORE: embed-request-jwt-token
    B2CORE-->>App: embed-jwt-token
    Note over App,B2CORE: User toggles theme...
    B2CORE-->>App: embed-theme-change
    Note over App,B2CORE: User changes language...
    B2CORE-->>App: embed-language-change
```

{% stepper %}
{% step %}
**Signal readiness.** When your page finishes loading, send the `embed-iframe-ready` message to B2CORE. This tells the host that your application is ready to receive data.
{% endstep %}

{% step %}
**Receive the current theme.** B2CORE responds with `embed-theme-change` containing the current theme (`dark-theme` or `light-theme`). Apply the theme to your UI. You will also receive this message whenever the user switches themes.
{% endstep %}

{% step %}
**Receive the current language.** B2CORE also responds with `embed-language-change` containing the language code currently selected in the B2CORE UI (for example, `en` or `ar`). Apply the corresponding locale to your UI. You will receive this message again whenever the user changes the language.
{% endstep %}

{% step %}
**Request an authentication token.** When you need to identify the current user, send `embed-request-jwt-token`. B2CORE responds with either `embed-jwt-token` (containing the JWT and its expiration time) or `embed-jwt-token-error` if token generation fails.
{% endstep %}

{% step %}
**Refresh the token before expiry.** The JWT has an expiration time provided in the `expiresAt` field (ISO 8601 format). Request a new token before the current one expires to maintain uninterrupted access.
{% endstep %}
{% endstepper %}

### Code example

A complete JavaScript snippet you can include in your application:

```javascript
(function () {
  const B2CORE_ORIGIN = '*'; // Replace with your B2CORE instance origin for production

  let currentToken = null;
  let tokenRefreshTimer = null;

  // --- Send a message to B2CORE ---
  function sendToHost(message) {
    window.parent.postMessage(message, B2CORE_ORIGIN);
  }

  // --- Handle incoming messages from B2CORE ---
  function handleMessage(event) {
    const data = event.data;
    if (!data || !data.type) return;

    switch (data.type) {
      case 'embed-theme-change':
        applyTheme(data.theme);
        break;

      case 'embed-language-change':
        applyLanguage(data.lang);
        break;

      case 'embed-jwt-token':
        handleToken(data.token, data.expiresAt);
        break;

      case 'embed-jwt-token-error':
        console.error('Token error from B2Core:', data.error);
        break;
    }
  }

  // --- Apply theme to your UI ---
  function applyTheme(theme) {
    document.documentElement.setAttribute('data-theme', theme);
  }

  // --- Apply language to your UI ---
  function applyLanguage(lang) {
    document.documentElement.setAttribute('lang', lang);
  }

  // --- Handle received JWT token ---
  function handleToken(token, expiresAt) {
    currentToken = token;

    // Schedule a refresh 2 minutes before expiry
    if (tokenRefreshTimer) {
      clearTimeout(tokenRefreshTimer);
    }

    const refreshIn = new Date(expiresAt).getTime() - Date.now() - 2 * 60 * 1000;

    if (refreshIn > 0) {
      tokenRefreshTimer = setTimeout(requestToken, refreshIn);
    } else {
      requestToken();
    }
  }

  // --- Request a JWT token from B2CORE ---
  function requestToken() {
    sendToHost({ type: 'embed-request-jwt-token' });
  }

  // --- Initialize ---
  window.addEventListener('message', handleMessage);
  sendToHost({ type: 'embed-iframe-ready' });
  requestToken();
})();
```

{% hint style="warning" %}
Replace the `B2CORE_ORIGIN` value with the actual origin of your B2CORE instance (for example, `'https://portal.example.com'`) in production. Using `'*'` is acceptable during development only, as it allows any origin to communicate with your application.
{% endhint %}

### TypeScript type definitions

If your application is built with TypeScript, you can use the following type definitions:

```typescript
type EmbedTheme = 'dark-theme' | 'light-theme';

// Messages: Your App -> B2CORE
interface EmbedIframeReadyMessage {
  readonly type: 'embed-iframe-ready';
}

interface EmbedRequestJwtTokenMessage {
  readonly type: 'embed-request-jwt-token';
}

type EmbedOutboundMessage = EmbedIframeReadyMessage | EmbedRequestJwtTokenMessage;

// Messages: B2CORE -> Your App
interface EmbedThemeChangeMessage {
  readonly type: 'embed-theme-change';
  readonly theme: EmbedTheme;
}

interface EmbedLanguageChangeMessage {
  readonly type: 'embed-language-change';
  readonly lang: string; // ISO 639-1 code, e.g. "en", "de", "ar"
}

interface EmbedJwtTokenMessage {
  readonly type: 'embed-jwt-token';
  readonly token: string;
  readonly expiresAt: string; // ISO 8601
}

interface EmbedJwtTokenErrorMessage {
  readonly type: 'embed-jwt-token-error';
  readonly error: string;
}

type EmbedInboundMessage =
  | EmbedThemeChangeMessage
  | EmbedLanguageChangeMessage
  | EmbedJwtTokenMessage
  | EmbedJwtTokenErrorMessage;
```

## Step 4 (optional). Validate the JWT token

The JWT token issued by B2CORE can be validated against the JSON Web Key Set (JWKS) endpoint exposed by the B2CORE API (Admin Panel backend). This is typically the API/admin domain, not the client-facing UI domain:

```
https://api.<your-domain>/.well-known/jwks.json
```

Use this endpoint to retrieve the public keys needed to verify the token signature. Most JWT libraries support JWKS-based validation out of the box.

{% hint style="info" %}
For manual inspection during development, you can decode and verify JWT tokens using the [JWT decoder tool](https://dinochiesa.github.io/jwt).
{% endhint %}

{% hint style="success" %}
**See also**

[How to add custom menu items](/how-to-articles/manage-advertising-options/how-to-add-custom-menu-items.md)

[How to configure a menu in the B2CORE UI](/how-to-articles/manage-advertising-options/how-to-configure-a-menu-in-the-b2core-ui.md)

[Menu](/back-office-guide/promotion/menu.md)
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.b2core.b2broker.com/how-to-articles/manage-system-settings/how-to-integrate-your-app-as-iframe-in-b2core.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
