diff --git a/examples/email-auth/src/components/Auth.tsx b/examples/email-auth/src/components/Auth.tsx index 34c6ebc4f..51e50d2b9 100644 --- a/examples/email-auth/src/components/Auth.tsx +++ b/examples/email-auth/src/components/Auth.tsx @@ -21,7 +21,7 @@ export function Auth(props: AuthProps) { if (!iframeStamper) { const iframeStamper = new IframeStamper({ iframeUrl: props.iframeUrl, - iframeContainerId: TurnkeyIframeContainerId, + iframeContainer: document.getElementById(TurnkeyIframeContainerId), iframeElementId: TurnkeyIframeElementId, }); iframeStamper.init().then(() => { diff --git a/examples/email-recovery/src/components/Recovery.tsx b/examples/email-recovery/src/components/Recovery.tsx index 77169986f..4b298efba 100644 --- a/examples/email-recovery/src/components/Recovery.tsx +++ b/examples/email-recovery/src/components/Recovery.tsx @@ -21,7 +21,7 @@ export function Recovery(props: RecoveryProps) { if (!iframeStamper) { const iframeStamper = new IframeStamper({ iframeUrl: props.iframeUrl, - iframeContainerId: TurnkeyIframeContainerId, + iframeContainer: document.getElementById(TurnkeyIframeContainerId), iframeElementId: TurnkeyIframeElementId, }); iframeStamper.init().then(() => { diff --git a/examples/wallet-export/src/components/Export.tsx b/examples/wallet-export/src/components/Export.tsx new file mode 100644 index 000000000..97f61b15e --- /dev/null +++ b/examples/wallet-export/src/components/Export.tsx @@ -0,0 +1,70 @@ +"use client"; + +import { IframeStamper } from "@turnkey/iframe-stamper"; +import { Dispatch, SetStateAction, useEffect, useState } from "react"; + +interface ExportProps { + iframeUrl: string; + turnkeyBaseUrl: string; + iframeDisplay: string; + setIframeStamper: Dispatch>; +} + +const TurnkeyIframeContainerId = "turnkey-iframe-container-id"; +const TurnkeyIframeElementId = "turnkey-iframe-element-id"; + +export function Export(props: ExportProps) { + const [iframeStamper, setIframeStamper] = useState( + null + ); + const [iframeDisplay, setIframeDisplay] = useState("none"); + + useEffect(() => { + setIframeDisplay(props.iframeDisplay); + return () => { + if (iframeDisplay === "block") { + setIframeDisplay("none"); + } + }; + }, [props.iframeDisplay]); + + useEffect(() => { + if (!iframeStamper) { + const iframeStamper = new IframeStamper({ + iframeUrl: props.iframeUrl, + iframeContainer: document.getElementById(TurnkeyIframeContainerId), + iframeElementId: TurnkeyIframeElementId, + }); + iframeStamper.init().then(() => { + setIframeStamper(iframeStamper); + props.setIframeStamper(iframeStamper); + }); + } + + return () => { + if (iframeStamper) { + iframeStamper.clear(); + setIframeStamper(null); + } + }; + }, [props.setIframeStamper, iframeStamper, setIframeStamper]); + + const iframeCss = ` + iframe { + box-sizing: border-box; + width: 400px; + height: 120px; + border-radius: 8px; + border-width: 1px; + border-style: solid; + border-color: rgba(216, 219, 227, 1); + padding: 20px; + } + `; + + return ( +
+ +
+ ); +} diff --git a/examples/wallet-export/src/pages/index.module.css b/examples/wallet-export/src/pages/index.module.css index 38925db50..28207698c 100644 --- a/examples/wallet-export/src/pages/index.module.css +++ b/examples/wallet-export/src/pages/index.module.css @@ -140,30 +140,26 @@ color: #fff; } -.copyKey { +.reveal { display: flex; flex-direction: column; align-items: center; + width: 600px; } -.copyKey h2 { +.reveal h2 { text-align: center; font-family: "Inter"; padding-top: 40px; } -.copyKey ul { +.reveal ul { margin-top: 0; + text-align: justify; } -.copyKey p { +.reveal p { font-family: "Inter"; font-size: 14px; color: #555b64; } - -.walletIframe iframe { - border: none; - width: 600px; - height: 600px; -} diff --git a/examples/wallet-export/src/pages/index.tsx b/examples/wallet-export/src/pages/index.tsx index 5de98ec82..92794d41b 100644 --- a/examples/wallet-export/src/pages/index.tsx +++ b/examples/wallet-export/src/pages/index.tsx @@ -5,42 +5,19 @@ import { IframeStamper } from "@turnkey/iframe-stamper"; import * as React from "react"; import { useEffect, useState } from "react"; import axios from "axios"; +import { Export } from "@/components/Export"; import { WalletsTable } from "@/components/WalletsTable"; type TWallet = TurnkeyApiTypes["v1Wallet"]; -const TurnkeyIframeContainerId = "turnkey-iframe-container-id"; -const TurnkeyIframeElementId = "turnkey-iframe-element-id"; - export default function ExportPage() { const [wallets, setWallets] = useState([]); const [iframeStamper, setIframeStamper] = useState( null ); - const [showWallet, setShowWallet] = useState(false); + const [iframeDisplay, setIframeDisplay] = useState("none"); const [selectedWallet, setSelectedWallet] = useState(null); - // Initialize the iframeStamper - useEffect(() => { - if (!iframeStamper) { - const iframeStamper = new IframeStamper({ - iframeUrl: process.env.NEXT_PUBLIC_EXPORT_IFRAME_URL!, - iframeContainerId: TurnkeyIframeContainerId, - iframeElementId: TurnkeyIframeElementId, - }); - iframeStamper.init().then(() => { - setIframeStamper(iframeStamper); - }); - } - - return () => { - if (iframeStamper) { - iframeStamper.clear(); - setIframeStamper(null); - } - }; - }, [iframeStamper]); - // Get wallets useEffect(() => { getWallets(); @@ -72,7 +49,7 @@ export default function ExportPage() { throw new Error("unexpected error while injecting export bundle"); } - setShowWallet(true); + setIframeDisplay("block"); }; return ( @@ -97,21 +74,42 @@ export default function ExportPage() { )} {selectedWallet && ( -
-

Wallet seedphrase

-

- You are about to reveal your wallet seedphrase. By revealing this - seedphrase you understand that: -

+
+

Before you continue

+

By revealing the private key, you understand and agree that:

  • -

    Your seedphrase is the only way to recover your funds.

    +

    + You should never share your private key with anyone, including + the Turnkey team. Turnkey will never ask you for your private + key. +

    +
  • +
  • +

    + You are responsible for the security of this private key and any + assets associated with it, and Turnkey cannot help recover it on + your behalf. Failure to properly secure your private key may + result in total loss of the associated assets. +

  • -

    Do not let anyone see your seedphrase.

    +

    + Turnkey is not responsible for any other wallet you may use with + this private key, and Turnkey does not represent that any other + software or hardware will be compatible with or protect your + private key. +

  • -

    Never share your seedphrase with anyone, including Turnkey.

    +

    + You have read and agree to{" "} + + Turnkey{"'"}s Terms of Service + + , including the risks related to exporting your private key + disclosed therein. +

@@ -126,11 +124,13 @@ export default function ExportPage() {
)} -
+ + ); } diff --git a/packages/iframe-stamper/CHANGELOG.md b/packages/iframe-stamper/CHANGELOG.md index e981433eb..7a0a8e894 100644 --- a/packages/iframe-stamper/CHANGELOG.md +++ b/packages/iframe-stamper/CHANGELOG.md @@ -1,5 +1,11 @@ # @turnkey/iframe-stamper +## 1.0.0 + +### Major Changes + +- This breaking change uses an HTML element instead of an ID to reference the iframe's container. + ## 0.4.1 ### Patch Changes diff --git a/packages/iframe-stamper/README.md b/packages/iframe-stamper/README.md index 6bf94971e..6399b01c0 100644 --- a/packages/iframe-stamper/README.md +++ b/packages/iframe-stamper/README.md @@ -2,7 +2,7 @@ [![npm](https://img.shields.io/npm/v/@turnkey/iframe-stamper?color=%234C48FF)](https://www.npmjs.com/package/@turnkey/iframe-stamper) -This package contains functions to stamp a Turnkey request through credentials contained in an iframe. It is meant to be used with [`@turnkey/http`](https://www.npmjs.com/package/@turnkey/http) to build flows. +This package contains functions to stamp a Turnkey request through credentials contained in an iframe. It is meant to be used with [`@turnkey/http`](https://www.npmjs.com/package/@turnkey/http) to build flows. To stamp the request, use the Recovery and Auth flows to request and inject a credential bundle. Usage: @@ -17,7 +17,7 @@ const TurnkeyIframeElementId = "turnkey-iframe"; const iframeStamper = new IframeStamper({ iframeUrl: process.env.IFRAME_URL!, - iframeContainerId: TurnkeyIframeContainerId, + iframeContainer: document.getElementById(TurnkeyIframeContainerId), iframeElementId: TurnkeyIframeElementId, }); @@ -45,13 +45,13 @@ const TurnkeyIframeElementId = "turnkey-iframe"; const iframeStamper = new IframeStamper({ iframeUrl: process.env.IFRAME_URL!, - iframeContainerId: TurnkeyIframeContainerId, + iframeContainer: document.getElementById(TurnkeyIframeContainerId), iframeElementId: TurnkeyIframeElementId, }); // This inserts the iframe in the DOM and returns the public key const publicKey = await iframeStamper.init(); -// Injects a new private key in the iframe -const injected = await iframeStamper.injectKeyExportBundle(exportBundle); +// Injects a new wallet in the iframe +const injected = await iframeStamper.injectWalletExportBundle(exportBundle); ``` diff --git a/packages/iframe-stamper/src/__tests__/iframe-test.ts b/packages/iframe-stamper/src/__tests__/iframe-test.ts index feff3e406..e3b59d547 100644 --- a/packages/iframe-stamper/src/__tests__/iframe-test.ts +++ b/packages/iframe-stamper/src/__tests__/iframe-test.ts @@ -5,7 +5,7 @@ test("throws when instantiated outside of a browser environment", async function expect(() => { new IframeStamper({ iframeUrl: "https://recovery.tkhqlabs.xyz", - iframeContainerId: "my-container-id", + iframeContainer: null, iframeElementId: "my-iframe-id", }); }).toThrow("Cannot initialize iframe in non-browser environment"); diff --git a/packages/iframe-stamper/src/index.ts b/packages/iframe-stamper/src/index.ts index 48733b0b2..1f4f1dbef 100644 --- a/packages/iframe-stamper/src/index.ts +++ b/packages/iframe-stamper/src/index.ts @@ -39,7 +39,7 @@ type TStamp = { export type TIframeStamperConfig = { iframeUrl: string; iframeElementId: string; - iframeContainerId: string; + iframeContainer: HTMLElement | null | undefined; }; /** @@ -61,19 +61,16 @@ export class IframeStamper { throw new Error("Cannot initialize iframe in non-browser environment"); } - if (document.getElementById(config.iframeElementId)) { - throw new Error( - `Iframe element with ID ${config.iframeElementId} already exists` - ); + if (!config.iframeContainer) { + throw new Error("Iframe container cannot be found"); } + this.container = config.iframeContainer; - const container = document.getElementById(config.iframeContainerId); - if (!container) { + if (this.container.querySelector(`#${config.iframeElementId}`)) { throw new Error( - `Cannot create iframe stamper: no container with ID ${config.iframeContainerId} exists in the current document` + `Iframe element with ID ${config.iframeElementId} already exists` ); } - this.container = container; let iframe = window.document.createElement("iframe"); iframe.id = config.iframeElementId;