From 6becb362abf20746344a8fc09284235ef9d98339 Mon Sep 17 00:00:00 2001 From: Justin Formentin Date: Fri, 14 Aug 2026 15:40:50 +0000 Subject: [PATCH 1/3] docs: document frames cross-origin hardening breaking change - Add sdks/advanced/frames-cross-origin-hardening.mdx documenting the breaking change in the import frame (now throws ERROR when organizationId or userId are missing), plus the MessageChannel and postMessage hardening across auth, import, and export-and-sign frames. - Add new page to docs.json nav after iframe-stamper entry. The primary user-facing impact: @turnkey/iframe-stamper < 2.0.0 (or custom integrations omitting organizationId/userId) will now receive an ERROR from the import frame. Fix is to upgrade to >= 2.0.0. --- docs.json | 1 + .../frames-cross-origin-hardening.mdx | 105 ++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 sdks/advanced/frames-cross-origin-hardening.mdx diff --git a/docs.json b/docs.json index 9043b2ac..9cf911bd 100644 --- a/docs.json +++ b/docs.json @@ -988,6 +988,7 @@ "sdks/advanced/wallet-stamper", "sdks/advanced/webauthn-stamper", "sdks/advanced/iframe-stamper", + "sdks/advanced/frames-cross-origin-hardening", "sdks/advanced/client-side-signing", "sdks/advanced/indexed-db-stamper" ] diff --git a/sdks/advanced/frames-cross-origin-hardening.mdx b/sdks/advanced/frames-cross-origin-hardening.mdx new file mode 100644 index 00000000..c6a202fb --- /dev/null +++ b/sdks/advanced/frames-cross-origin-hardening.mdx @@ -0,0 +1,105 @@ +--- +title: "Frames cross-origin hardening (breaking change)" +description: "Security hardening in Turnkey's auth, import, and export iframe frames — including a breaking change affecting clients using @turnkey/iframe-stamper < 2.0.0." +sidebarTitle: "Frames cross-origin hardening" +--- + + +**Breaking change:** The import frame now throws an error when `organizationId` or `userId` are missing from the injected import bundle. Clients using `@turnkey/iframe-stamper` **< 2.0.0** (or custom integrations that omit these fields) will receive an `ERROR` response instead of succeeding. + +**Fix:** Upgrade to `@turnkey/iframe-stamper` **>= 2.0.0** (latest: 2.11.0). + + +## Overview + +Turnkey's iframe frames — the pages served at `turnkey.com/auth`, `/import`, and `/export` that the `@turnkey/iframe-stamper` client communicates with — have been hardened against cross-origin attacks. These changes affect all three frames (auth, import, and export-and-sign). + +Most integrators on a recent version of `@turnkey/iframe-stamper` will not be impacted. If you are on an older version or have a custom integration, read the sections below carefully. + +## Breaking change: import frame now rejects missing organizationId / userId + +Previously, when `organizationId` or `userId` were absent from the injected import bundle, the import frame would only log a console warning and continue — silently skipping the org/user binding check. This was a bug that allowed older or incomplete integrations to proceed without proper identity binding. + +**The import frame now throws and returns an `ERROR` message to the parent when either field is absent.** + +### Impact + +| Client | Impact | +|---|---| +| `@turnkey/iframe-stamper` >= 2.0.0 | **Not affected.** These fields have been included since v2.0.0 (commit `5d0bfde`). | +| `@turnkey/iframe-stamper` < 2.0.0 | **Affected.** Import flows will now receive an `ERROR` response instead of completing. | +| Custom integrations omitting `organizationId` or `userId` | **Affected.** Must be updated to include both fields in the injected bundle. | + +### Fix + + + + Upgrade to `@turnkey/iframe-stamper` >= 2.0.0. The latest version (2.11.0) is recommended. + + + ```bash npm + npm install @turnkey/iframe-stamper@latest + ``` + + ```bash pnpm + pnpm add @turnkey/iframe-stamper@latest + ``` + + ```bash yarn + yarn add @turnkey/iframe-stamper@latest + ``` + + + + If you have a hand-rolled integration that posts directly to the import frame, ensure your injected import bundle includes both `organizationId` and `userId` fields. Without them, the frame will return an `ERROR` message. + + + +## Additional hardening: cross-origin MessageChannel and postMessage validation + +The following hardening applies to all three frames (auth, import, and export-and-sign). These changes could affect clients on older versions of `@turnkey/iframe-stamper` or custom integrations. + +### MessageChannel handshake gate + +The `TURNKEY_INIT_MESSAGE_CHANNEL` handshake — used by `@turnkey/iframe-stamper` >= 2.1.0 to establish a secure MessageChannel — now enforces strict validation: + +- `event.source` must be `window.parent` (only the direct parent frame can initiate the channel) +- The origin must be real and non-opaque (not `"null"` or empty) +- Exactly one `MessagePort` must be provided + +Previously, any window that could post the init message with a port could seize the channel. This is no longer possible. + +**Impact:** Clients using `@turnkey/iframe-stamper` >= 2.1.0 that are embedded correctly (as a direct child iframe of the parent) are not affected. + +### Legacy postMessage path hardening + +For clients using `@turnkey/iframe-stamper` < 2.1.0 (before MessageChannel was introduced), the older `postMessage`-based communication path now enforces: + +- The sender must be `window.parent` with a real, non-opaque origin +- The frame binds to the first accepted parent origin and rejects subsequent messages from any other origin +- Outbound responses now target the bound parent origin instead of the wildcard `"*"` + +**Impact:** Custom integrations or very old clients that send postMessages from a window other than the direct parent will be rejected. + +### Per-origin embedded key (auth frame) + +The auth frame's P-256 embedded key is now scoped per parent origin in localStorage (stored under `TURNKEY_EMBEDDED_KEY_V2:`) and bound at the MessageChannel handshake. Previously, a single shared key (`TURNKEY_EMBEDDED_KEY`) was used for all origins. + +This prevents an auth bundle encrypted for one origin from being replayed from a different origin. + + +The old shared key (`TURNKEY_EMBEDDED_KEY`) is purged on first contact — it is not migrated to the new per-origin format. Clients that were mid-flow across an upgrade may need to re-initiate the auth flow. + +Clients using `@turnkey/iframe-stamper` < 2.1.0 (legacy postMessage path) receive an in-memory-only ephemeral key until a real origin is established. + + +## Upgrade recommendations + +| Scenario | Recommended action | +|---|---| +| On `@turnkey/iframe-stamper` < 2.0.0 | Upgrade to >= 2.0.0 immediately to avoid the import `ERROR`. Latest (2.11.0) is strongly recommended. | +| On `@turnkey/iframe-stamper` >= 2.0.0 and < 2.1.0 | Upgrade to >= 2.1.0 to use the secure MessageChannel path and benefit from all hardening. | +| On `@turnkey/iframe-stamper` >= 2.1.0 | No action required. You are on a version that uses MessageChannel and includes all required fields. | +| Custom / hand-rolled integration | Audit your integration: ensure `organizationId` and `userId` are included in import bundles, and that your frames are embedded as direct child iframes of the parent page. | + +For the full changelog and release history, see the [`@turnkey/iframe-stamper` changelog](/changelogs/iframe-stamper/readme). From 8a535390edcc88b90f01aedc1124b8cbe03ca2fc Mon Sep 17 00:00:00 2001 From: Justin Formentin Date: Fri, 14 Aug 2026 15:48:54 +0000 Subject: [PATCH 2/3] docs: add cross-origin hardening warnings to existing iframe pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of a standalone page, add concise callouts directly on the affected existing pages: - sdks/advanced/iframe-stamper.mdx: Warning about cross-origin hardening + import breaking change (ERROR when organizationId/userId missing). Upgrade to >= 2.0.0 required. - snippets/shared/import-wallets.mdx: Warning in the Embedded iframe section — breaking change is most relevant here. - snippets/shared/export-wallets.mdx: Note in the Embedded iframe section — cross-origin hardening, no breaking change, upgrade to >= 2.1.0 recommended. - sdks/advanced/client-side-signing.mdx: Note near top — export-and-sign iframe hardening, upgrade to >= 2.1.0 recommended. - docs.json: Revert nav addition (no standalone page any more). changelogs/iframe-stamper/readme.mdx left untouched (tracks npm package releases, not server-side frame deploys). --- docs.json | 1 - sdks/advanced/client-side-signing.mdx | 4 + .../frames-cross-origin-hardening.mdx | 105 ------------------ sdks/advanced/iframe-stamper.mdx | 4 + snippets/shared/export-wallets.mdx | 4 + snippets/shared/import-wallets.mdx | 4 + 6 files changed, 16 insertions(+), 106 deletions(-) delete mode 100644 sdks/advanced/frames-cross-origin-hardening.mdx diff --git a/docs.json b/docs.json index 9cf911bd..9043b2ac 100644 --- a/docs.json +++ b/docs.json @@ -988,7 +988,6 @@ "sdks/advanced/wallet-stamper", "sdks/advanced/webauthn-stamper", "sdks/advanced/iframe-stamper", - "sdks/advanced/frames-cross-origin-hardening", "sdks/advanced/client-side-signing", "sdks/advanced/indexed-db-stamper" ] diff --git a/sdks/advanced/client-side-signing.mdx b/sdks/advanced/client-side-signing.mdx index c71956f6..7d6927dd 100644 --- a/sdks/advanced/client-side-signing.mdx +++ b/sdks/advanced/client-side-signing.mdx @@ -4,6 +4,10 @@ title: "Client-side signing" This guide covers how to set up client-side signing using Turnkey's `@turnkey/iframe-stamper` package and the `export-and-sign` iframe. This architecture enables secure transaction and message signing directly in the browser without exposing private keys to your application code. Note that mishandling of exported private keys introduces inherent risks; please proceed with caution. + + Turnkey's export-and-sign iframe was recently hardened against cross-origin attacks. Clients on `@turnkey/iframe-stamper` **>= 2.1.0** are unaffected. If you are on an older version or using a custom integration, upgrade to **>= 2.1.0** (latest: 2.11.0). + + ## Overview Client-side signing allows you to: diff --git a/sdks/advanced/frames-cross-origin-hardening.mdx b/sdks/advanced/frames-cross-origin-hardening.mdx deleted file mode 100644 index c6a202fb..00000000 --- a/sdks/advanced/frames-cross-origin-hardening.mdx +++ /dev/null @@ -1,105 +0,0 @@ ---- -title: "Frames cross-origin hardening (breaking change)" -description: "Security hardening in Turnkey's auth, import, and export iframe frames — including a breaking change affecting clients using @turnkey/iframe-stamper < 2.0.0." -sidebarTitle: "Frames cross-origin hardening" ---- - - -**Breaking change:** The import frame now throws an error when `organizationId` or `userId` are missing from the injected import bundle. Clients using `@turnkey/iframe-stamper` **< 2.0.0** (or custom integrations that omit these fields) will receive an `ERROR` response instead of succeeding. - -**Fix:** Upgrade to `@turnkey/iframe-stamper` **>= 2.0.0** (latest: 2.11.0). - - -## Overview - -Turnkey's iframe frames — the pages served at `turnkey.com/auth`, `/import`, and `/export` that the `@turnkey/iframe-stamper` client communicates with — have been hardened against cross-origin attacks. These changes affect all three frames (auth, import, and export-and-sign). - -Most integrators on a recent version of `@turnkey/iframe-stamper` will not be impacted. If you are on an older version or have a custom integration, read the sections below carefully. - -## Breaking change: import frame now rejects missing organizationId / userId - -Previously, when `organizationId` or `userId` were absent from the injected import bundle, the import frame would only log a console warning and continue — silently skipping the org/user binding check. This was a bug that allowed older or incomplete integrations to proceed without proper identity binding. - -**The import frame now throws and returns an `ERROR` message to the parent when either field is absent.** - -### Impact - -| Client | Impact | -|---|---| -| `@turnkey/iframe-stamper` >= 2.0.0 | **Not affected.** These fields have been included since v2.0.0 (commit `5d0bfde`). | -| `@turnkey/iframe-stamper` < 2.0.0 | **Affected.** Import flows will now receive an `ERROR` response instead of completing. | -| Custom integrations omitting `organizationId` or `userId` | **Affected.** Must be updated to include both fields in the injected bundle. | - -### Fix - - - - Upgrade to `@turnkey/iframe-stamper` >= 2.0.0. The latest version (2.11.0) is recommended. - - - ```bash npm - npm install @turnkey/iframe-stamper@latest - ``` - - ```bash pnpm - pnpm add @turnkey/iframe-stamper@latest - ``` - - ```bash yarn - yarn add @turnkey/iframe-stamper@latest - ``` - - - - If you have a hand-rolled integration that posts directly to the import frame, ensure your injected import bundle includes both `organizationId` and `userId` fields. Without them, the frame will return an `ERROR` message. - - - -## Additional hardening: cross-origin MessageChannel and postMessage validation - -The following hardening applies to all three frames (auth, import, and export-and-sign). These changes could affect clients on older versions of `@turnkey/iframe-stamper` or custom integrations. - -### MessageChannel handshake gate - -The `TURNKEY_INIT_MESSAGE_CHANNEL` handshake — used by `@turnkey/iframe-stamper` >= 2.1.0 to establish a secure MessageChannel — now enforces strict validation: - -- `event.source` must be `window.parent` (only the direct parent frame can initiate the channel) -- The origin must be real and non-opaque (not `"null"` or empty) -- Exactly one `MessagePort` must be provided - -Previously, any window that could post the init message with a port could seize the channel. This is no longer possible. - -**Impact:** Clients using `@turnkey/iframe-stamper` >= 2.1.0 that are embedded correctly (as a direct child iframe of the parent) are not affected. - -### Legacy postMessage path hardening - -For clients using `@turnkey/iframe-stamper` < 2.1.0 (before MessageChannel was introduced), the older `postMessage`-based communication path now enforces: - -- The sender must be `window.parent` with a real, non-opaque origin -- The frame binds to the first accepted parent origin and rejects subsequent messages from any other origin -- Outbound responses now target the bound parent origin instead of the wildcard `"*"` - -**Impact:** Custom integrations or very old clients that send postMessages from a window other than the direct parent will be rejected. - -### Per-origin embedded key (auth frame) - -The auth frame's P-256 embedded key is now scoped per parent origin in localStorage (stored under `TURNKEY_EMBEDDED_KEY_V2:`) and bound at the MessageChannel handshake. Previously, a single shared key (`TURNKEY_EMBEDDED_KEY`) was used for all origins. - -This prevents an auth bundle encrypted for one origin from being replayed from a different origin. - - -The old shared key (`TURNKEY_EMBEDDED_KEY`) is purged on first contact — it is not migrated to the new per-origin format. Clients that were mid-flow across an upgrade may need to re-initiate the auth flow. - -Clients using `@turnkey/iframe-stamper` < 2.1.0 (legacy postMessage path) receive an in-memory-only ephemeral key until a real origin is established. - - -## Upgrade recommendations - -| Scenario | Recommended action | -|---|---| -| On `@turnkey/iframe-stamper` < 2.0.0 | Upgrade to >= 2.0.0 immediately to avoid the import `ERROR`. Latest (2.11.0) is strongly recommended. | -| On `@turnkey/iframe-stamper` >= 2.0.0 and < 2.1.0 | Upgrade to >= 2.1.0 to use the secure MessageChannel path and benefit from all hardening. | -| On `@turnkey/iframe-stamper` >= 2.1.0 | No action required. You are on a version that uses MessageChannel and includes all required fields. | -| Custom / hand-rolled integration | Audit your integration: ensure `organizationId` and `userId` are included in import bundles, and that your frames are embedded as direct child iframes of the parent page. | - -For the full changelog and release history, see the [`@turnkey/iframe-stamper` changelog](/changelogs/iframe-stamper/readme). diff --git a/sdks/advanced/iframe-stamper.mdx b/sdks/advanced/iframe-stamper.mdx index 520e5db0..2a5c77a1 100644 --- a/sdks/advanced/iframe-stamper.mdx +++ b/sdks/advanced/iframe-stamper.mdx @@ -10,6 +10,10 @@ It leverages the `postMessage` communication mechanism to send and receive messa By bridging the gap between the iframe's isolated environment and Turnkey's API, the iframe stamper plays a pivotal role in maintaining the integrity and security of the credential while ensuring seamless operation within the iframe context. + + **Security update — upgrade required for import flows.** A recent hardening of Turnkey's iframe frames enforces stricter cross-origin validation. As part of this, the import iframe now returns an `ERROR` (instead of silently continuing) when `organizationId` or `userId` are missing from the injected import bundle. Clients on `@turnkey/iframe-stamper` **< 2.0.0** are affected. **Upgrade to `@turnkey/iframe-stamper` >= 2.0.0** (latest: 2.11.0) to avoid breakage. Custom integrations must also include `organizationId` and `userId` in injected import bundles. + + ## Installing To start using the `@turnkey/iframe-stamper` client, install it as follows: diff --git a/snippets/shared/export-wallets.mdx b/snippets/shared/export-wallets.mdx index d4db0192..8e9b1b02 100644 --- a/snippets/shared/export-wallets.mdx +++ b/snippets/shared/export-wallets.mdx @@ -69,6 +69,10 @@ A full reference implementation is available at ### Embedded iframe + + Turnkey's export iframe was recently hardened against cross-origin attacks. Clients on `@turnkey/iframe-stamper` **>= 2.1.0** are unaffected. If you are on an older version or using a custom integration, upgrade to **>= 2.1.0** (latest: 2.11.0) to ensure compatibility. + + Turnkey hosts a static export page at `export.turnkey.com` designed to be embedded as an iframe in your app. The encrypted bundle returned by Turnkey is injected into the iframe, which decrypts and displays the mnemonic or private key entirely within its own origin — neither your app nor Turnkey diff --git a/snippets/shared/import-wallets.mdx b/snippets/shared/import-wallets.mdx index 60f3116e..d59cbd11 100644 --- a/snippets/shared/import-wallets.mdx +++ b/snippets/shared/import-wallets.mdx @@ -72,6 +72,10 @@ A full reference implementation is available at ### Embedded iframe + + **Breaking change:** The import iframe now returns an `ERROR` when `organizationId` or `userId` are missing from the injected import bundle. Clients using `@turnkey/iframe-stamper` **< 2.0.0** (or custom integrations that omit these fields) must upgrade to **>= 2.0.0** (latest: 2.11.0). + + Turnkey hosts a static import page at `import.turnkey.com` designed to be embedded as an iframe in your app. The iframe handles encryption of the mnemonic or private key entirely within its own origin — neither your app nor Turnkey ever sees the plaintext. Use From f58a2c73c7ff07c21d62970788a83c3e808d4aa5 Mon Sep 17 00:00:00 2001 From: Justin Formentin Date: Fri, 14 Aug 2026 16:13:50 +0000 Subject: [PATCH 3/3] docs: rewrite cross-origin callouts to be timeless and factual --- sdks/advanced/client-side-signing.mdx | 2 +- sdks/advanced/iframe-stamper.mdx | 2 +- snippets/shared/export-wallets.mdx | 2 +- snippets/shared/import-wallets.mdx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdks/advanced/client-side-signing.mdx b/sdks/advanced/client-side-signing.mdx index 7d6927dd..55740239 100644 --- a/sdks/advanced/client-side-signing.mdx +++ b/sdks/advanced/client-side-signing.mdx @@ -5,7 +5,7 @@ title: "Client-side signing" This guide covers how to set up client-side signing using Turnkey's `@turnkey/iframe-stamper` package and the `export-and-sign` iframe. This architecture enables secure transaction and message signing directly in the browser without exposing private keys to your application code. Note that mishandling of exported private keys introduces inherent risks; please proceed with caution. - Turnkey's export-and-sign iframe was recently hardened against cross-origin attacks. Clients on `@turnkey/iframe-stamper` **>= 2.1.0** are unaffected. If you are on an older version or using a custom integration, upgrade to **>= 2.1.0** (latest: 2.11.0). + Versions of `@turnkey/iframe-stamper` below 2.0.0 are vulnerable to cross-origin attacks. If you are using an older version, upgrade to 2.0.0 or later. ## Overview diff --git a/sdks/advanced/iframe-stamper.mdx b/sdks/advanced/iframe-stamper.mdx index 2a5c77a1..aa894e9b 100644 --- a/sdks/advanced/iframe-stamper.mdx +++ b/sdks/advanced/iframe-stamper.mdx @@ -11,7 +11,7 @@ It leverages the `postMessage` communication mechanism to send and receive messa By bridging the gap between the iframe's isolated environment and Turnkey's API, the iframe stamper plays a pivotal role in maintaining the integrity and security of the credential while ensuring seamless operation within the iframe context. - **Security update — upgrade required for import flows.** A recent hardening of Turnkey's iframe frames enforces stricter cross-origin validation. As part of this, the import iframe now returns an `ERROR` (instead of silently continuing) when `organizationId` or `userId` are missing from the injected import bundle. Clients on `@turnkey/iframe-stamper` **< 2.0.0** are affected. **Upgrade to `@turnkey/iframe-stamper` >= 2.0.0** (latest: 2.11.0) to avoid breakage. Custom integrations must also include `organizationId` and `userId` in injected import bundles. + Versions of `@turnkey/iframe-stamper` below 2.0.0 are vulnerable to cross-origin attacks. If you are using an older version, upgrade to 2.0.0 or later. The import flow requires `organizationId` and `userId` in the injected import bundle; bundles that omit these fields are rejected with an `ERROR`. ## Installing diff --git a/snippets/shared/export-wallets.mdx b/snippets/shared/export-wallets.mdx index 8e9b1b02..9058b354 100644 --- a/snippets/shared/export-wallets.mdx +++ b/snippets/shared/export-wallets.mdx @@ -70,7 +70,7 @@ A full reference implementation is available at ### Embedded iframe - Turnkey's export iframe was recently hardened against cross-origin attacks. Clients on `@turnkey/iframe-stamper` **>= 2.1.0** are unaffected. If you are on an older version or using a custom integration, upgrade to **>= 2.1.0** (latest: 2.11.0) to ensure compatibility. + Versions of `@turnkey/iframe-stamper` below 2.0.0 are vulnerable to cross-origin attacks. If you are using an older version, upgrade to 2.0.0 or later. Turnkey hosts a static export page at `export.turnkey.com` designed to be embedded as an iframe in diff --git a/snippets/shared/import-wallets.mdx b/snippets/shared/import-wallets.mdx index d59cbd11..470ba795 100644 --- a/snippets/shared/import-wallets.mdx +++ b/snippets/shared/import-wallets.mdx @@ -73,7 +73,7 @@ A full reference implementation is available at ### Embedded iframe - **Breaking change:** The import iframe now returns an `ERROR` when `organizationId` or `userId` are missing from the injected import bundle. Clients using `@turnkey/iframe-stamper` **< 2.0.0** (or custom integrations that omit these fields) must upgrade to **>= 2.0.0** (latest: 2.11.0). + Versions of `@turnkey/iframe-stamper` below 2.0.0 are vulnerable to cross-origin attacks. If you are using an older version, upgrade to 2.0.0 or later. The import flow requires `organizationId` and `userId` in the injected import bundle; bundles that omit these fields are rejected with an `ERROR`. Turnkey hosts a static import page at `import.turnkey.com` designed to be embedded as an iframe in