Skip to content

Commit 7b5a01f

Browse files
Enhance iframe usage documentation to address cross-origin iframe limitations (#8441)
Enhance iframe usage documentation to address cross-origin iframe limitations and recommend Nested App Authentication for improved compatibility
1 parent b073f32 commit 7b5a01f

4 files changed

Lines changed: 136 additions & 0 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "none",
3+
"comment": "Document cross-origin iframe limitations for the redirect bridge and recommend NAA [#8441](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/8441)",
4+
"packageName": "@azure/msal-browser",
5+
"email": "kshabelko@microsoft.com",
6+
"dependentChangeType": "none"
7+
}

lib/msal-browser/docs/iframe-usage.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,43 @@
11
# Using MSAL in iframed apps
22

3+
> [!IMPORTANT]
4+
> **Cross-origin iframes and popup flows:** Starting with Chromium 115+
5+
> (Chrome, Edge, and other Chromium-based browsers), `BroadcastChannel` (and
6+
> other storage APIs) are partitioned by top-level site. This means that
7+
> `loginPopup()` and `acquireTokenPopup()` **will not work** when your
8+
> application runs in a cross-origin iframe, because the popup opens as its own
9+
> top-level context with a different storage partition than the iframe — the
10+
> redirect bridge's `BroadcastChannel` message never arrives. Other browsers
11+
> are expected to adopt similar partitioning in the future.
12+
>
13+
> See the [Redirect Bridge — Cross-Origin Iframe
14+
> Limitation](./redirect-bridge.md#cross-origin-iframe-limitation) section for
15+
> full details and recommended alternatives.
16+
>
17+
> **Recommended approach:** If the host platform supports it, use
18+
> [Nested App Authentication (NAA)](./initialization.md#nested-app-configuration)
19+
> via `createNestablePublicClientApplication`. NAA delegates authentication to
20+
> the host application at the top level, avoiding cross-window communication
21+
> entirely. NAA is supported by Microsoft hosts such as **Teams**, **Outlook**,
22+
> and **Microsoft 365**. For other hosts, use `loginRedirect()` with
23+
> `system.allowRedirectInIframe: true` as a fallback (subject to IdP iframe
24+
> restrictions — see below).
25+
326
By default, MSAL prevents full-frame redirects to **Azure AD** authentication endpoint when an app is rendered inside an iframe, which means you cannot use [redirect APIs](./initialization.md#redirect-apis) for user interaction with the IdP:
427

528
- This restriction is imposed since **Azure AD** will refuse to render any prompt requiring user interaction (e.g. **credential entry**, **consent**, **logout** etc.) in an iframe by throwing the `X-FRAME OPTIONS SET TO DENY` [error](https://html.spec.whatwg.org/multipage/browsing-the-web.html#the-x-frame-options-header), a measure taken to prevent [clickjacking attacks](https://owasp.org/www-community/attacks/Clickjacking).
629
- Instead, you'll have to rely on MSAL's [popup APIs](./initialization.md#popup-apis) if user interaction is required, and/or silent APIs (`ssoSilent()`, `acquireTokenSilent()`) if user interaction can be avoided.
730
- Similarly, you'll have to use the [logoutPopup()](./logout.md#logoutpopup) API for sign-outs (:warning: if your app is using a version of msal-browser older than v2.13, make sure to upgrade and replace the `logout()` API, as it will attempt a full-frame redirect to Azure AD).
831
- When using [popup APIs](./initialization.md#popup-apis), you need to take into account any [sandboxing](https://html.spec.whatwg.org/multipage/origin.html#sandboxing) restrictions imposed by the parent app. In particular, the parent app needs to set the `allow-popups` flag when the iframe is sandboxed.
932

33+
> [!WARNING]
34+
> Popup APIs from a **cross-origin** iframe are affected by
35+
> [third-party storage partitioning](#third-party-storage-partitioning-and-the-redirect-bridge),
36+
> which breaks the redirect bridge's `BroadcastChannel` communication.
37+
> Silent APIs may also be affected by third-party cookie restrictions.
38+
> If your app is embedded in a cross-origin iframe, see the callout above for
39+
> recommended alternatives.
40+
1041
**Azure AD B2C** offers an [embedded sign-in experience](https://docs.microsoft.com/azure/active-directory-b2c/embedded-login), which allows rendering a custom login UI in an iframe. Since MSAL prevents redirect in iframes by default, you'll need to set the [allowRedirectInIframe](./configuration.md#system-config-options) configuration option to **true** in order to make use of this feature. Note that enabling this option for apps on **Azure AD** is not recommended, due to the above restriction.
1142

1243
## Browser restrictions
@@ -140,3 +171,19 @@ If you would like to minimize communication with IdP that requires user interact
140171
## Single sign-out
141172

142173
You can use MSAL.js with a [front-channel logout URI](https://openid.net/specs/openid-connect-backchannel-1_0.html) to achieve *single sign-out* effect between iframed and parent apps. For instance, if you would like users to automatically logout from iframed apps when they logout from the parent app, you should enable front-channel logout for the iframed apps. To do so, please refer to: [How to configure a front-channel logout URI](./logout.md#front-channel-logout).
174+
175+
## Third-Party Storage Partitioning and the Redirect Bridge
176+
177+
Starting with **Chromium 115+** (Chrome, Edge, and other Chromium-based browsers), [third-party storage partitioning](https://developers.google.com/privacy-sandbox/cookies/storage-partitioning) is enforced. Storage APIs — including `BroadcastChannel`, `localStorage`, `sessionStorage`, `IndexedDB`, `ServiceWorker`, and `SharedWorker` — are partitioned by the **top-level site**, not just by origin. Other browsers are expected to adopt similar partitioning in the future.
178+
179+
The MSAL [redirect bridge](./redirect-bridge.md) uses `BroadcastChannel` to send the authentication response from the popup back to the main application. When your app runs in a cross-origin iframe and opens a popup, the popup becomes its own top-level browsing context — placing it in a **different storage partition** than the iframe. The `BroadcastChannel` message from the redirect bridge never reaches your app, causing the authentication flow to time out with a `timed_out` (`redirect_bridge_timeout`) error.
180+
181+
**Affected flows (BroadcastChannel partition mismatch):**
182+
- `loginPopup()` / `acquireTokenPopup()` — the popup opens as a new top-level context with a different partition than the iframe
183+
184+
### Recommended solutions
185+
186+
1. **Nested App Authentication (NAA)** — The preferred approach for apps embedded in a host platform that supports the NAA bridge (e.g., Teams, Outlook, Microsoft 365). See [Nested App Configuration](./initialization.md#nested-app-configuration).
187+
2. **Redirect flow** — Use `loginRedirect()` with `allowRedirectInIframe: true` if the IdP allows rendering in an iframe (for example, Azure AD B2C with embedded sign-in).
188+
189+
For the full technical explanation and code examples, see the [Redirect Bridge — Cross-Origin Iframe Limitation](./redirect-bridge.md#cross-origin-iframe-limitation).

lib/msal-browser/docs/initialization.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ Please note the below guidance before opting in for Nested app authentication:
5656
- `createNestablePublicClientApplication` will fall back to `createStandardPublicClientApplication` if nested app bridge is unavailable or the Hub is not configured to support nested app authentication.
5757
- If an application does not want to be Nested App, it should use `createStandardPublicClientApplication` instead.
5858
- Certain account lookup APIs are not supported in NAA apps, please refer to [active accounts](./accounts.md#active-account-apis).
59+
- **Cross-origin iframe scenarios:** If your app runs inside a cross-origin iframe, NAA is the **recommended** approach for authentication.
60+
- In browsers that enforce [third-party storage partitioning](https://developers.google.com/privacy-sandbox/cookies/storage-partitioning) (Chromium 115+, with other browsers expected to follow), popup flows (`loginPopup`, `acquireTokenPopup`) will fail because the popup opens as its own top-level context, placing it in a different `BroadcastChannel` partition than the iframe — the [redirect bridge's](./redirect-bridge.md) response never arrives. NAA avoids these limitations by delegating authentication to the host application at the top level.
61+
- For more details, see [Redirect Bridge — Cross-Origin Iframe Limitation](./redirect-bridge.md#cross-origin-iframe-limitation) and [Using MSAL in iframed apps](./iframe-usage.md).
5962

6063
## Initializing the PublicClientApplication object
6164

lib/msal-browser/docs/redirect-bridge.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,85 @@ This guide provides framework-specific instructions for setting up the redirect
3939
> redirect bridge with your application or serve it from your own
4040
> infrastructure.
4141
42+
## Cross-Origin Iframe Limitation
43+
44+
The redirect bridge relies on the [BroadcastChannel API](https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel) to send the authentication response from the popup back to the main application window. Starting with **Chromium 115+** (Chrome, Edge, and other Chromium-based browsers), [third-party storage partitioning](https://developers.google.com/privacy-sandbox/cookies/storage-partitioning) is enforced, which means `BroadcastChannel` is **partitioned by top-level site**, not just by origin. Other browsers are expected to adopt similar partitioning in the future.
45+
46+
This causes **popup-based flows** to fail when your application runs inside a **cross-origin iframe**:
47+
48+
| Context | Top-level site | BroadcastChannel partition |
49+
|---|---|---|
50+
| Your app in the iframe | `hostplatform.com` | `hostplatform.com` |
51+
| Popup opened by the iframe | `yourapp.com` (popup is its own top-level context) | `yourapp.com` |
52+
53+
Although both the iframe and the popup share the same **origin** (`yourapp.com`), the popup is in a **different storage partition**. The redirect bridge sends the response on a `BroadcastChannel` in the popup's partition, but the iframe is listening on a channel in the host platform's partition — the message never arrives, and the authentication flow times out.
54+
55+
> [!IMPORTANT]
56+
> This is a browser-level constraint — not a bug in MSAL. There is no secure
57+
> client-side workaround that avoids this partitioning while preserving the
58+
> security guarantees of the redirect bridge.
59+
60+
### Recommended alternatives for cross-origin iframe scenarios
61+
62+
#### Option 1: Nested App Authentication (recommended)
63+
64+
If the host platform supports it, use [Nested App Authentication (NAA)](./initialization.md#nested-app-configuration).
65+
With NAA, the iframe delegates authentication entirely to the host application
66+
via `createNestablePublicClientApplication`. The host authenticates the user at
67+
the top level (where there are no partitioning issues) and provides tokens to
68+
the nested app through the NAA bridge. This avoids popup and silent flows from
69+
within the iframe altogether.
70+
71+
```javascript
72+
import { createNestablePublicClientApplication } from "@azure/msal-browser";
73+
74+
const pca = await createNestablePublicClientApplication({
75+
auth: {
76+
clientId: "your-client-id",
77+
authority: "https://login.microsoftonline.com/your-tenant-id",
78+
},
79+
});
80+
```
81+
82+
NAA is supported out of the box by Microsoft host platforms such as **Teams**,
83+
**Outlook**, and **Microsoft 365**. For custom host platforms, the host must
84+
implement the [NAA bridge protocol](./initialization.md#nested-app-configuration).
85+
If the NAA bridge is not available, `createNestablePublicClientApplication`
86+
automatically falls back to a standard `PublicClientApplication`.
87+
88+
#### Option 2: Redirect flow within the iframe (fallback)
89+
90+
If NAA is not available, use `loginRedirect()` / `acquireTokenRedirect()`
91+
instead of popup APIs. The redirect flow happens entirely within the iframe's
92+
browsing context and does not use `BroadcastChannel`, so the redirect bridge
93+
partition mismatch does not apply. You will need to set
94+
`system.allowRedirectInIframe: true`:
95+
96+
```javascript
97+
const msalConfig = {
98+
auth: {
99+
clientId: "your-client-id",
100+
authority: "https://login.microsoftonline.com/your-tenant-id",
101+
redirectUri: "/redirect",
102+
},
103+
system: {
104+
allowRedirectInIframe: true,
105+
},
106+
};
107+
```
108+
109+
> [!NOTE]
110+
> Redirect flows in iframes require the identity provider to allow rendering in
111+
> an iframe. **Azure AD / Microsoft Entra ID** will refuse to render interactive
112+
> prompts (credential entry, consent, etc.) inside an iframe and will return an
113+
> `X-FRAME-OPTIONS: DENY` error. This means redirect flows in iframes are
114+
> primarily supported for **Azure AD B2C** with the
115+
> [embedded sign-in experience](https://docs.microsoft.com/azure/active-directory-b2c/embedded-login),
116+
> or for non-interactive redirect-in-iframe scenarios where no user interaction is required. This is distinct from MSAL's silent token renewal APIs (`ssoSilent` / `acquireTokenSilent`), which use a hidden iframe with `prompt=none` and are subject to third-party cookie and tracking-prevention restrictions.
117+
118+
For more information on running MSAL in iframes, see
119+
[Using MSAL in iframed apps](./iframe-usage.md).
120+
42121
## Angular
43122

44123
1. **Create the redirect bridge component** (`src/app/redirect/redirect.component.ts`):

0 commit comments

Comments
 (0)