diff --git a/packages/iframe-stamper/src/index.ts b/packages/iframe-stamper/src/index.ts index 666bcde19..3447211bd 100644 --- a/packages/iframe-stamper/src/index.ts +++ b/packages/iframe-stamper/src/index.ts @@ -109,6 +109,8 @@ export class IframeStamper { iframe: HTMLIFrameElement; iframeOrigin: string; iframePublicKey: string | null; + trustedParentOrigin: string; + messageChannel: MessageChannel; /** * Creates a new iframe stamper. This function _does not_ insert the iframe in the DOM. @@ -132,9 +134,11 @@ export class IframeStamper { let iframe = window.document.createElement("iframe"); + this.trustedParentOrigin = window.origin; // See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox // We do not need any other permission than running scripts for import/export/auth frames. iframe.setAttribute("sandbox", "allow-scripts allow-same-origin"); + iframe.setAttribute("data-trusted-origin", this.trustedParentOrigin) iframe.id = config.iframeElementId; iframe.src = config.iframeUrl; @@ -145,6 +149,8 @@ export class IframeStamper { // This is populated once the iframe is ready. Call `.init()` to kick off DOM insertion! this.iframePublicKey = null; + + this.messageChannel = new MessageChannel(); } /** @@ -152,22 +158,18 @@ export class IframeStamper { */ async init(): Promise { this.container.appendChild(this.iframe); + this.iframe.addEventListener("load", () => { + // Send a message to the iframe to initialize the message channel + this.iframe.contentWindow?.postMessage("init", this.iframeOrigin, [this.messageChannel.port2]); + }) + return new Promise((resolve, _reject) => { - window.addEventListener( - "message", - (event) => { - if (event.origin !== this.iframeOrigin) { - // There might be other things going on in the window, for example: react dev tools, other extensions, etc. - // Instead of erroring out - return; - } - if (event.data?.type === IframeEventType.PublicKeyReady) { - this.iframePublicKey = event.data["value"]; - resolve(event.data["value"]); - } - }, - false - ); + this.messageChannel.port1.onmessage = (event) => { + if (event.data?.type === IframeEventType.PublicKeyReady) { + this.iframePublicKey = event.data["value"]; + resolve(event.data["value"]); + } + } }); } @@ -193,31 +195,18 @@ export class IframeStamper { */ async injectCredentialBundle(bundle: string): Promise { return new Promise((resolve, reject) => { - this.iframe.contentWindow?.postMessage( - { - type: IframeEventType.InjectCredentialBundle, - value: bundle, - }, - "*" - ); - - window.addEventListener( - "message", - (event) => { - if (event.origin !== this.iframeOrigin) { - // There might be other things going on in the window, for example: react dev tools, other extensions, etc. - // Instead of erroring out we simply return. Not our event! - return; - } - if (event.data?.type === IframeEventType.BundleInjected) { - resolve(event.data["value"]); - } - if (event.data?.type === IframeEventType.Error) { - reject(event.data["value"]); - } - }, - false - ); + this.messageChannel.port1.postMessage({ + type: IframeEventType.InjectCredentialBundle, + value: bundle, + }) + this.messageChannel.port1.onmessage = (event) => { + if (event.data?.type === IframeEventType.BundleInjected) { + resolve(event.data["value"]); + } + if (event.data?.type === IframeEventType.Error) { + reject(event.data["value"]); + } + } }); } @@ -240,27 +229,18 @@ export class IframeStamper { keyFormat, organizationId, }, - "*" + this.iframeOrigin ); return new Promise((resolve, reject) => { - window.addEventListener( - "message", - (event) => { - if (event.origin !== this.iframeOrigin) { - // There might be other things going on in the window, for example: react dev tools, other extensions, etc. - // Instead of erroring out we simply return. Not our event! - return; - } - if (event.data?.type === IframeEventType.BundleInjected) { - resolve(event.data["value"]); - } - if (event.data?.type === IframeEventType.Error) { - reject(event.data["value"]); - } - }, - false - ); + this.messageChannel.port1.onmessage = (event) => { + if (event.data?.type === IframeEventType.BundleInjected) { + resolve(event.data["value"]); + } + if (event.data?.type === IframeEventType.Error) { + reject(event.data["value"]); + } + } }); } @@ -274,33 +254,21 @@ export class IframeStamper { bundle: string, organizationId: string ): Promise { - this.iframe.contentWindow?.postMessage( - { - type: IframeEventType.InjectWalletExportBundle, - value: bundle, - organizationId, - }, - "*" - ); + this.messageChannel.port1.postMessage({ + type: IframeEventType.InjectWalletExportBundle, + value: bundle, + organizationId, + }) return new Promise((resolve, reject) => { - window.addEventListener( - "message", - (event) => { - if (event.origin !== this.iframeOrigin) { - // There might be other things going on in the window, for example: react dev tools, other extensions, etc. - // Instead of erroring out we simply return. Not our event! - return; - } - if (event.data?.type === IframeEventType.BundleInjected) { - resolve(event.data["value"]); - } - if (event.data?.type === IframeEventType.Error) { - reject(event.data["value"]); - } - }, - false - ); + this.messageChannel.port1.onmessage = (event) => { + if (event.data?.type === IframeEventType.BundleInjected) { + resolve(event.data["value"]); + } + if (event.data?.type === IframeEventType.Error) { + reject(event.data["value"]); + } + } }); } @@ -313,34 +281,24 @@ export class IframeStamper { organizationId: string, userId: string ): Promise { - this.iframe.contentWindow?.postMessage( + this.messageChannel.port1.postMessage( { type: IframeEventType.InjectImportBundle, value: bundle, organizationId, userId, }, - "*" ); return new Promise((resolve, reject) => { - window.addEventListener( - "message", - (event) => { - if (event.origin !== this.iframeOrigin) { - // There might be other things going on in the window, for example: react dev tools, other extensions, etc. - // Instead of erroring out we simply return. Not our event! - return; - } - if (event.data?.type === IframeEventType.BundleInjected) { - resolve(event.data["value"]); - } - if (event.data?.type === IframeEventType.Error) { - reject(event.data["value"]); - } - }, - false - ); + this.messageChannel.port1.onmessage = (event) => { + if (event.data?.type === IframeEventType.BundleInjected) { + resolve(event.data["value"]); + } + if (event.data?.type === IframeEventType.Error) { + reject(event.data["value"]); + } + } }); } @@ -351,31 +309,21 @@ export class IframeStamper { * This is used during the wallet import flow. */ async extractWalletEncryptedBundle(): Promise { - this.iframe.contentWindow?.postMessage( + this.messageChannel.port1.postMessage( { type: IframeEventType.ExtractWalletEncryptedBundle, }, - "*" ); return new Promise((resolve, reject) => { - window.addEventListener( - "message", - (event) => { - if (event.origin !== this.iframeOrigin) { - // There might be other things going on in the window, for example: react dev tools, other extensions, etc. - // Instead of erroring out we simply return. Not our event! - return; - } - if (event.data?.type === IframeEventType.EncryptedBundleExtracted) { - resolve(event.data["value"]); - } - if (event.data?.type === IframeEventType.Error) { - reject(event.data["value"]); - } - }, - false - ); + this.messageChannel.port1.onmessage = (event) => { + if (event.data?.type === IframeEventType.EncryptedBundleExtracted) { + resolve(event.data["value"]); + } + if (event.data?.type === IframeEventType.Error) { + reject(event.data["value"]); + } + } }); } @@ -387,32 +335,20 @@ export class IframeStamper { * This is used during the private key import flow. */ async extractKeyEncryptedBundle(keyFormat?: KeyFormat): Promise { - this.iframe.contentWindow?.postMessage( - { - type: IframeEventType.ExtractKeyEncryptedBundle, - keyFormat: keyFormat, - }, - "*" - ); + this.messageChannel.port1.postMessage({ + type: IframeEventType.ExtractKeyEncryptedBundle, + keyFormat: keyFormat, + }); return new Promise((resolve, reject) => { - window.addEventListener( - "message", - (event) => { - if (event.origin !== this.iframeOrigin) { - // There might be other things going on in the window, for example: react dev tools, other extensions, etc. - // Instead of erroring out we simply return. Not our event! - return; - } - if (event.data?.type === IframeEventType.EncryptedBundleExtracted) { - resolve(event.data["value"]); - } - if (event.data?.type === IframeEventType.Error) { - reject(event.data["value"]); - } - }, - false - ); + this.messageChannel.port1.onmessage = (event) => { + if (event.data?.type === IframeEventType.EncryptedBundleExtracted) { + resolve(event.data["value"]); + } + if (event.data?.type === IframeEventType.Error) { + reject(event.data["value"]); + } + } }); } @@ -422,33 +358,21 @@ export class IframeStamper { */ async applySettings(settings: TIframeSettings): Promise { const settingsStr = JSON.stringify(settings); - this.iframe.contentWindow?.postMessage( - { - type: IframeEventType.ApplySettings, - value: settingsStr, - }, - "*" - ); + this.messageChannel.port1.postMessage({ + type: IframeEventType.ApplySettings, + value: settingsStr, + }) return new Promise((resolve, reject) => { - window.addEventListener( - "message", - (event) => { - if (event.origin !== this.iframeOrigin) { - // There might be other things going on in the window, for example: react dev tools, other extensions, etc. - // Instead of erroring out we simply return. Not our event! - return; - } - if (event.data?.type === IframeEventType.SettingsApplied) { - resolve(event.data["value"]); - } - if (event.data?.type === IframeEventType.Error) { - reject(event.data["value"]); - } - }, - false - ); - }); + this.messageChannel.port1.onmessage = (event) => { + if (event.data?.type === IframeEventType.SettingsApplied) { + resolve(event.data["value"]); + } + if (event.data?.type === IframeEventType.Error) { + reject(event.data["value"]); + } + } + }) } /** @@ -461,37 +385,23 @@ export class IframeStamper { ); } - const iframeOrigin = this.iframeOrigin; - - this.iframe.contentWindow?.postMessage( - { - type: IframeEventType.StampRequest, - value: payload, - }, - "*" - ); - - return new Promise(function (resolve, reject) { - window.addEventListener( - "message", - (event) => { - if (event.origin !== iframeOrigin) { - // There might be other things going on in the window, for example: react dev tools, other extensions, etc. - // Instead of erroring out we simply return. Not our event! - return; - } - if (event.data?.type === IframeEventType.Stamp) { - resolve({ - stampHeaderName: stampHeaderName, - stampHeaderValue: event.data["value"], - }); - } - if (event.data?.type === IframeEventType.Error) { - reject(event.data["value"]); - } - }, - false - ); + this.messageChannel.port1.postMessage({ + type: IframeEventType.StampRequest, + value: payload, + }) + + return new Promise((resolve, reject) => { + this.messageChannel.port1.onmessage = (event) => { + if (event.data?.type === IframeEventType.Stamp) { + resolve({ + stampHeaderName: stampHeaderName, + stampHeaderValue: event.data["value"], + }); + } + if (event.data?.type === IframeEventType.Error) { + reject(event.data["value"]); + } + } }); } } diff --git a/packages/sdk-browser/package.json b/packages/sdk-browser/package.json index 3477dc887..6526fb360 100644 --- a/packages/sdk-browser/package.json +++ b/packages/sdk-browser/package.json @@ -13,7 +13,7 @@ }, "types": "./dist/index.d.ts", "license": "Apache-2.0", - "description": "Javascript Browser SDK", + "description": "JavaScript Browser SDK", "keywords": [ "Turnkey" ], diff --git a/packages/sdk-browser/src/__types__/base.ts b/packages/sdk-browser/src/__types__/base.ts index 54f33ae39..e7ee2db74 100644 --- a/packages/sdk-browser/src/__types__/base.ts +++ b/packages/sdk-browser/src/__types__/base.ts @@ -91,6 +91,7 @@ export interface TurnkeySDKBrowserConfig { defaultOrganizationId: string; rpId?: string; serverSignUrl?: string; + iframeUrl?: string; } export type queryOverrideParams = { diff --git a/packages/sdk-react/src/contexts/TurnkeyContext.tsx b/packages/sdk-react/src/contexts/TurnkeyContext.tsx index e185a7d85..15565fc41 100644 --- a/packages/sdk-react/src/contexts/TurnkeyContext.tsx +++ b/packages/sdk-react/src/contexts/TurnkeyContext.tsx @@ -123,7 +123,7 @@ export const TurnkeyProvider: React.FC = ({ iframeContainer: document.getElementById( TurnkeyAuthIframeContainerId ), - iframeUrl: "https://auth.turnkey.com", + iframeUrl: config.iframeUrl || "https://auth.turnkey.com", iframeElementId: TurnkeyAuthIframeElementId, }); setAuthIframeClient(newAuthIframeClient); diff --git a/packages/sdk-server/package.json b/packages/sdk-server/package.json index c2b0ee236..b58642748 100644 --- a/packages/sdk-server/package.json +++ b/packages/sdk-server/package.json @@ -13,7 +13,7 @@ }, "types": "./dist/index.d.ts", "license": "Apache-2.0", - "description": "Javascript Server SDK", + "description": "JavaScript Server SDK", "keywords": [ "Turnkey" ],