@turnkey/iframe-stamper
Advanced tools
Comparing version
@@ -67,2 +67,5 @@ 'use strict'; | ||
})(exports.KeyFormat || (exports.KeyFormat = {})); | ||
function generateUUID() { | ||
return crypto.randomUUID(); | ||
} | ||
/** | ||
@@ -108,3 +111,38 @@ * Stamper to use with `@turnkey/http`'s `TurnkeyClient` | ||
this.messageChannel = new MessageChannel(); | ||
// Initialize a pending requests tracker | ||
this.pendingRequests = new Map(); | ||
} | ||
onMessageHandler(event) { | ||
const { type, value, requestId } = event.data || {}; | ||
// Handle messages without requestId (like PUBLIC_KEY_READY) | ||
if (!requestId) { | ||
if (type === exports.IframeEventType.PublicKeyReady) { | ||
this.iframePublicKey = value; | ||
return; | ||
} | ||
return; | ||
} | ||
const pendingRequest = this.pendingRequests.get(requestId); | ||
if (!pendingRequest) { | ||
console.warn(`Received response for unknown request: ${requestId}`); | ||
return; | ||
} | ||
// Remove from pending requests | ||
this.pendingRequests.delete(requestId); | ||
if (type === exports.IframeEventType.Error) { | ||
pendingRequest.reject(new Error(value)); | ||
return; | ||
} | ||
// Handle specific response types | ||
switch (type) { | ||
case exports.IframeEventType.Stamp: | ||
pendingRequest.resolve({ | ||
stampHeaderName, | ||
stampHeaderValue: value, | ||
}); | ||
break; | ||
default: | ||
pendingRequest.resolve(value); | ||
} | ||
} | ||
/** | ||
@@ -114,28 +152,19 @@ * Inserts the iframe on the page and returns a promise resolving to the iframe's public key | ||
async init() { | ||
this.container.appendChild(this.iframe); | ||
/** | ||
* Once the iframe is loaded, we send a message to the iframe to hand over the | ||
* MessageChannel's second port, port2, and establish the secure communication channel. | ||
* The iframe will use this port to send messages back to the parent page. | ||
* See https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/postMessage#transfer | ||
*/ | ||
this.iframe.addEventListener("load", () => { | ||
if (!this.iframe.contentWindow || | ||
!this.iframe.contentWindow.postMessage) { | ||
throw new Error("contentWindow or contentWindow.postMessage does not exist"); | ||
} | ||
this.iframe.contentWindow.postMessage({ type: exports.IframeEventType.TurnkeyInitMessageChannel }, this.iframeOrigin, [this.messageChannel.port2]); | ||
}); | ||
return new Promise((resolve, _reject) => { | ||
/** | ||
* The MessageChannel port1 property is the port that gets attached | ||
* to the context that instantiated the MessageChannel. This class, the IframeStamper, | ||
* instantied the MessageChannel and will use port1 to send messages to the iframe. | ||
* See https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel/port1 | ||
*/ | ||
return new Promise((resolve, reject) => { | ||
this.container.appendChild(this.iframe); | ||
this.iframe.addEventListener("load", () => { | ||
if (!this.iframe.contentWindow?.postMessage) { | ||
reject(new Error("contentWindow or contentWindow.postMessage does not exist")); | ||
return; | ||
} | ||
this.iframe.contentWindow.postMessage({ type: exports.IframeEventType.TurnkeyInitMessageChannel }, this.iframeOrigin, [this.messageChannel.port2]); | ||
}); | ||
this.messageChannel.port1.onmessage = (event) => { | ||
// Handle initial PublicKeyReady event | ||
if (event.data?.type === exports.IframeEventType.PublicKeyReady) { | ||
this.iframePublicKey = event.data["value"]; | ||
resolve(event.data["value"]); | ||
this.iframePublicKey = event.data.value; | ||
resolve(event.data.value); | ||
} | ||
// Handle all other messages | ||
this.onMessageHandler(event); | ||
}; | ||
@@ -151,2 +180,3 @@ }); | ||
this.iframe.remove(); | ||
this.pendingRequests.clear(); | ||
} | ||
@@ -160,26 +190,22 @@ /** | ||
/** | ||
* Adds a message handler to the iframe's message channel | ||
* Generic function to abstract away request creation | ||
* @param type | ||
* @param payload | ||
* @returns expected shape <T> | ||
*/ | ||
addMessageHandler() { | ||
createRequest(type, payload = {}) { | ||
return new Promise((resolve, reject) => { | ||
this.messageChannel.port1.onmessage = (event) => { | ||
this.onMessageHandler(event, resolve, reject); | ||
}; | ||
const requestId = generateUUID(); | ||
this.pendingRequests.set(requestId, { | ||
resolve, | ||
reject, | ||
requestId, | ||
}); | ||
this.messageChannel.port1.postMessage({ | ||
type, | ||
requestId, | ||
...payload, | ||
}); | ||
}); | ||
} | ||
onMessageHandler(event, resolve, reject) { | ||
switch (event.data?.type) { | ||
case exports.IframeEventType.Stamp: | ||
resolve({ | ||
stampHeaderName: stampHeaderName, | ||
stampHeaderValue: event.data["value"], | ||
}); | ||
break; | ||
case exports.IframeEventType.Error: | ||
reject(event.data["value"]); | ||
break; | ||
default: | ||
resolve(event.data["value"]); | ||
} | ||
} | ||
/** | ||
@@ -192,10 +218,4 @@ * Function to inject a new credential into the iframe | ||
async injectCredentialBundle(bundle) { | ||
return new Promise((resolve, reject) => { | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.InjectCredentialBundle, | ||
value: bundle, | ||
}); | ||
this.messageChannel.port1.onmessage = (event) => { | ||
this.onMessageHandler(event, resolve, reject); | ||
}; | ||
return this.createRequest(exports.IframeEventType.InjectCredentialBundle, { | ||
value: bundle, | ||
}); | ||
@@ -211,4 +231,3 @@ } | ||
async injectKeyExportBundle(bundle, organizationId, keyFormat) { | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.InjectKeyExportBundle, | ||
return this.createRequest(exports.IframeEventType.InjectKeyExportBundle, { | ||
value: bundle, | ||
@@ -218,3 +237,2 @@ keyFormat, | ||
}); | ||
return this.addMessageHandler(); | ||
} | ||
@@ -228,8 +246,6 @@ /** | ||
async injectWalletExportBundle(bundle, organizationId) { | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.InjectWalletExportBundle, | ||
return this.createRequest(exports.IframeEventType.InjectWalletExportBundle, { | ||
value: bundle, | ||
organizationId, | ||
}); | ||
return this.addMessageHandler(); | ||
} | ||
@@ -241,4 +257,3 @@ /** | ||
async injectImportBundle(bundle, organizationId, userId) { | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.InjectImportBundle, | ||
return this.createRequest(exports.IframeEventType.InjectImportBundle, { | ||
value: bundle, | ||
@@ -248,3 +263,2 @@ organizationId, | ||
}); | ||
return this.addMessageHandler(); | ||
} | ||
@@ -258,6 +272,3 @@ /** | ||
async extractWalletEncryptedBundle() { | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.ExtractWalletEncryptedBundle, | ||
}); | ||
return this.addMessageHandler(); | ||
return this.createRequest(exports.IframeEventType.ExtractWalletEncryptedBundle); | ||
} | ||
@@ -272,7 +283,3 @@ /** | ||
async extractKeyEncryptedBundle(keyFormat) { | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.ExtractKeyEncryptedBundle, | ||
keyFormat: keyFormat, | ||
}); | ||
return this.addMessageHandler(); | ||
return this.createRequest(exports.IframeEventType.ExtractKeyEncryptedBundle, { keyFormat }); | ||
} | ||
@@ -284,8 +291,5 @@ /** | ||
async applySettings(settings) { | ||
const settingsStr = JSON.stringify(settings); | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.ApplySettings, | ||
value: settingsStr, | ||
return this.createRequest(exports.IframeEventType.ApplySettings, { | ||
value: JSON.stringify(settings), | ||
}); | ||
return this.addMessageHandler(); | ||
} | ||
@@ -299,7 +303,5 @@ /** | ||
} | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.StampRequest, | ||
return this.createRequest(exports.IframeEventType.StampRequest, { | ||
value: payload, | ||
}); | ||
return this.addMessageHandler(); | ||
} | ||
@@ -306,0 +308,0 @@ } |
# @turnkey/iframe-stamper | ||
## 2.2.0 | ||
### Minor Changes | ||
- a216a47: Add request ID to iframe requests | ||
## 2.1.0 | ||
@@ -4,0 +10,0 @@ |
@@ -68,2 +68,3 @@ /// <reference lib="dom" /> | ||
messageChannel: MessageChannel; | ||
private pendingRequests; | ||
/** | ||
@@ -74,2 +75,3 @@ * Creates a new iframe stamper. This function _does not_ insert the iframe in the DOM. | ||
constructor(config: TIframeStamperConfig); | ||
onMessageHandler(event: MessageEvent): void; | ||
/** | ||
@@ -88,6 +90,8 @@ * Inserts the iframe on the page and returns a promise resolving to the iframe's public key | ||
/** | ||
* Adds a message handler to the iframe's message channel | ||
* Generic function to abstract away request creation | ||
* @param type | ||
* @param payload | ||
* @returns expected shape <T> | ||
*/ | ||
addMessageHandler(): Promise<any>; | ||
onMessageHandler(event: MessageEvent, resolve: any, reject: any): void; | ||
private createRequest; | ||
/** | ||
@@ -94,0 +98,0 @@ * Function to inject a new credential into the iframe |
@@ -67,2 +67,5 @@ 'use strict'; | ||
})(exports.KeyFormat || (exports.KeyFormat = {})); | ||
function generateUUID() { | ||
return crypto.randomUUID(); | ||
} | ||
/** | ||
@@ -108,3 +111,38 @@ * Stamper to use with `@turnkey/http`'s `TurnkeyClient` | ||
this.messageChannel = new MessageChannel(); | ||
// Initialize a pending requests tracker | ||
this.pendingRequests = new Map(); | ||
} | ||
onMessageHandler(event) { | ||
const { type, value, requestId } = event.data || {}; | ||
// Handle messages without requestId (like PUBLIC_KEY_READY) | ||
if (!requestId) { | ||
if (type === exports.IframeEventType.PublicKeyReady) { | ||
this.iframePublicKey = value; | ||
return; | ||
} | ||
return; | ||
} | ||
const pendingRequest = this.pendingRequests.get(requestId); | ||
if (!pendingRequest) { | ||
console.warn(`Received response for unknown request: ${requestId}`); | ||
return; | ||
} | ||
// Remove from pending requests | ||
this.pendingRequests.delete(requestId); | ||
if (type === exports.IframeEventType.Error) { | ||
pendingRequest.reject(new Error(value)); | ||
return; | ||
} | ||
// Handle specific response types | ||
switch (type) { | ||
case exports.IframeEventType.Stamp: | ||
pendingRequest.resolve({ | ||
stampHeaderName, | ||
stampHeaderValue: value, | ||
}); | ||
break; | ||
default: | ||
pendingRequest.resolve(value); | ||
} | ||
} | ||
/** | ||
@@ -114,28 +152,19 @@ * Inserts the iframe on the page and returns a promise resolving to the iframe's public key | ||
async init() { | ||
this.container.appendChild(this.iframe); | ||
/** | ||
* Once the iframe is loaded, we send a message to the iframe to hand over the | ||
* MessageChannel's second port, port2, and establish the secure communication channel. | ||
* The iframe will use this port to send messages back to the parent page. | ||
* See https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/postMessage#transfer | ||
*/ | ||
this.iframe.addEventListener("load", () => { | ||
if (!this.iframe.contentWindow || | ||
!this.iframe.contentWindow.postMessage) { | ||
throw new Error("contentWindow or contentWindow.postMessage does not exist"); | ||
} | ||
this.iframe.contentWindow.postMessage({ type: exports.IframeEventType.TurnkeyInitMessageChannel }, this.iframeOrigin, [this.messageChannel.port2]); | ||
}); | ||
return new Promise((resolve, _reject) => { | ||
/** | ||
* The MessageChannel port1 property is the port that gets attached | ||
* to the context that instantiated the MessageChannel. This class, the IframeStamper, | ||
* instantied the MessageChannel and will use port1 to send messages to the iframe. | ||
* See https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel/port1 | ||
*/ | ||
return new Promise((resolve, reject) => { | ||
this.container.appendChild(this.iframe); | ||
this.iframe.addEventListener("load", () => { | ||
if (!this.iframe.contentWindow?.postMessage) { | ||
reject(new Error("contentWindow or contentWindow.postMessage does not exist")); | ||
return; | ||
} | ||
this.iframe.contentWindow.postMessage({ type: exports.IframeEventType.TurnkeyInitMessageChannel }, this.iframeOrigin, [this.messageChannel.port2]); | ||
}); | ||
this.messageChannel.port1.onmessage = (event) => { | ||
// Handle initial PublicKeyReady event | ||
if (event.data?.type === exports.IframeEventType.PublicKeyReady) { | ||
this.iframePublicKey = event.data["value"]; | ||
resolve(event.data["value"]); | ||
this.iframePublicKey = event.data.value; | ||
resolve(event.data.value); | ||
} | ||
// Handle all other messages | ||
this.onMessageHandler(event); | ||
}; | ||
@@ -151,2 +180,3 @@ }); | ||
this.iframe.remove(); | ||
this.pendingRequests.clear(); | ||
} | ||
@@ -160,26 +190,22 @@ /** | ||
/** | ||
* Adds a message handler to the iframe's message channel | ||
* Generic function to abstract away request creation | ||
* @param type | ||
* @param payload | ||
* @returns expected shape <T> | ||
*/ | ||
addMessageHandler() { | ||
createRequest(type, payload = {}) { | ||
return new Promise((resolve, reject) => { | ||
this.messageChannel.port1.onmessage = (event) => { | ||
this.onMessageHandler(event, resolve, reject); | ||
}; | ||
const requestId = generateUUID(); | ||
this.pendingRequests.set(requestId, { | ||
resolve, | ||
reject, | ||
requestId, | ||
}); | ||
this.messageChannel.port1.postMessage({ | ||
type, | ||
requestId, | ||
...payload, | ||
}); | ||
}); | ||
} | ||
onMessageHandler(event, resolve, reject) { | ||
switch (event.data?.type) { | ||
case exports.IframeEventType.Stamp: | ||
resolve({ | ||
stampHeaderName: stampHeaderName, | ||
stampHeaderValue: event.data["value"], | ||
}); | ||
break; | ||
case exports.IframeEventType.Error: | ||
reject(event.data["value"]); | ||
break; | ||
default: | ||
resolve(event.data["value"]); | ||
} | ||
} | ||
/** | ||
@@ -192,10 +218,4 @@ * Function to inject a new credential into the iframe | ||
async injectCredentialBundle(bundle) { | ||
return new Promise((resolve, reject) => { | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.InjectCredentialBundle, | ||
value: bundle, | ||
}); | ||
this.messageChannel.port1.onmessage = (event) => { | ||
this.onMessageHandler(event, resolve, reject); | ||
}; | ||
return this.createRequest(exports.IframeEventType.InjectCredentialBundle, { | ||
value: bundle, | ||
}); | ||
@@ -211,4 +231,3 @@ } | ||
async injectKeyExportBundle(bundle, organizationId, keyFormat) { | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.InjectKeyExportBundle, | ||
return this.createRequest(exports.IframeEventType.InjectKeyExportBundle, { | ||
value: bundle, | ||
@@ -218,3 +237,2 @@ keyFormat, | ||
}); | ||
return this.addMessageHandler(); | ||
} | ||
@@ -228,8 +246,6 @@ /** | ||
async injectWalletExportBundle(bundle, organizationId) { | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.InjectWalletExportBundle, | ||
return this.createRequest(exports.IframeEventType.InjectWalletExportBundle, { | ||
value: bundle, | ||
organizationId, | ||
}); | ||
return this.addMessageHandler(); | ||
} | ||
@@ -241,4 +257,3 @@ /** | ||
async injectImportBundle(bundle, organizationId, userId) { | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.InjectImportBundle, | ||
return this.createRequest(exports.IframeEventType.InjectImportBundle, { | ||
value: bundle, | ||
@@ -248,3 +263,2 @@ organizationId, | ||
}); | ||
return this.addMessageHandler(); | ||
} | ||
@@ -258,6 +272,3 @@ /** | ||
async extractWalletEncryptedBundle() { | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.ExtractWalletEncryptedBundle, | ||
}); | ||
return this.addMessageHandler(); | ||
return this.createRequest(exports.IframeEventType.ExtractWalletEncryptedBundle); | ||
} | ||
@@ -272,7 +283,3 @@ /** | ||
async extractKeyEncryptedBundle(keyFormat) { | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.ExtractKeyEncryptedBundle, | ||
keyFormat: keyFormat, | ||
}); | ||
return this.addMessageHandler(); | ||
return this.createRequest(exports.IframeEventType.ExtractKeyEncryptedBundle, { keyFormat }); | ||
} | ||
@@ -284,8 +291,5 @@ /** | ||
async applySettings(settings) { | ||
const settingsStr = JSON.stringify(settings); | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.ApplySettings, | ||
value: settingsStr, | ||
return this.createRequest(exports.IframeEventType.ApplySettings, { | ||
value: JSON.stringify(settings), | ||
}); | ||
return this.addMessageHandler(); | ||
} | ||
@@ -299,7 +303,5 @@ /** | ||
} | ||
this.messageChannel.port1.postMessage({ | ||
type: exports.IframeEventType.StampRequest, | ||
return this.createRequest(exports.IframeEventType.StampRequest, { | ||
value: payload, | ||
}); | ||
return this.addMessageHandler(); | ||
} | ||
@@ -306,0 +308,0 @@ } |
{ | ||
"name": "@turnkey/iframe-stamper", | ||
"version": "2.1.0", | ||
"version": "2.2.0", | ||
"main": "./dist/index.js", | ||
@@ -5,0 +5,0 @@ "module": "./dist/index.mjs", |
@@ -33,3 +33,3 @@ # @turnkey/iframe-stamper | ||
{ baseUrl: "https://api.turnkey.com" }, | ||
iframeStamper | ||
iframeStamper, | ||
); | ||
@@ -36,0 +36,0 @@ ``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1027
0.98%78077
-1.28%