@swype-org/react-sdk 0.1.45 → 0.1.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -654,6 +654,59 @@ function normalizeSignature(sig) {
654
654
  );
655
655
  }
656
656
 
657
+ // src/passkey-delegation.ts
658
+ function isInCrossOriginIframe() {
659
+ if (typeof window === "undefined") return false;
660
+ if (window.parent === window) return false;
661
+ try {
662
+ void window.parent.location.origin;
663
+ return false;
664
+ } catch {
665
+ return true;
666
+ }
667
+ }
668
+ var delegationCounter = 0;
669
+ function delegatePasskeyCreate(options) {
670
+ return new Promise((resolve, reject) => {
671
+ const id = `pc-${++delegationCounter}-${Date.now()}`;
672
+ const handler = (event) => {
673
+ const data = event.data;
674
+ if (!data || typeof data !== "object") return;
675
+ if (data.type !== "swype:passkey-create-response" || data.id !== id) return;
676
+ window.removeEventListener("message", handler);
677
+ if (data.error) {
678
+ reject(new Error(data.error));
679
+ } else if (data.result) {
680
+ resolve(data.result);
681
+ } else {
682
+ reject(new Error("Invalid passkey create response."));
683
+ }
684
+ };
685
+ window.addEventListener("message", handler);
686
+ window.parent.postMessage({ type: "swype:passkey-create-request", id, options }, "*");
687
+ });
688
+ }
689
+ function delegatePasskeyGet(options) {
690
+ return new Promise((resolve, reject) => {
691
+ const id = `pg-${++delegationCounter}-${Date.now()}`;
692
+ const handler = (event) => {
693
+ const data = event.data;
694
+ if (!data || typeof data !== "object") return;
695
+ if (data.type !== "swype:passkey-get-response" || data.id !== id) return;
696
+ window.removeEventListener("message", handler);
697
+ if (data.error) {
698
+ reject(new Error(data.error));
699
+ } else if (data.result) {
700
+ resolve(data.result);
701
+ } else {
702
+ reject(new Error("Invalid passkey get response."));
703
+ }
704
+ };
705
+ window.addEventListener("message", handler);
706
+ window.parent.postMessage({ type: "swype:passkey-get-request", id, options }, "*");
707
+ });
708
+ }
709
+
657
710
  // src/hooks.ts
658
711
  var WALLET_CLIENT_MAX_ATTEMPTS = 15;
659
712
  var WALLET_CLIENT_POLL_MS = 200;
@@ -775,6 +828,26 @@ async function createPasskeyCredential(params) {
775
828
  const challenge = new Uint8Array(32);
776
829
  crypto.getRandomValues(challenge);
777
830
  const rpId = resolvePasskeyRpId();
831
+ if (isInCrossOriginIframe()) {
832
+ return delegatePasskeyCreate({
833
+ challenge: toBase64(challenge),
834
+ rpId,
835
+ rpName: "Swype",
836
+ userId: toBase64(new TextEncoder().encode(params.userId)),
837
+ userName: params.displayName,
838
+ userDisplayName: params.displayName,
839
+ pubKeyCredParams: [
840
+ { alg: -7, type: "public-key" },
841
+ { alg: -257, type: "public-key" }
842
+ ],
843
+ authenticatorSelection: {
844
+ authenticatorAttachment: "platform",
845
+ residentKey: "preferred",
846
+ userVerification: "required"
847
+ },
848
+ timeout: 6e4
849
+ });
850
+ }
778
851
  await waitForDocumentFocus();
779
852
  const credential = await navigator.credentials.create({
780
853
  publicKey: {
@@ -818,6 +891,16 @@ async function findDevicePasskey(credentialIds) {
818
891
  try {
819
892
  const challenge = new Uint8Array(32);
820
893
  crypto.getRandomValues(challenge);
894
+ if (isInCrossOriginIframe()) {
895
+ const result = await delegatePasskeyGet({
896
+ challenge: toBase64(challenge),
897
+ rpId: resolvePasskeyRpId(),
898
+ allowCredentials: credentialIds.map((id) => ({ type: "public-key", id })),
899
+ userVerification: "discouraged",
900
+ timeout: 3e4
901
+ });
902
+ return result.credentialId;
903
+ }
821
904
  await waitForDocumentFocus();
822
905
  const assertion = await navigator.credentials.get({
823
906
  publicKey: {
@@ -1315,31 +1398,49 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
1315
1398
  throw new Error("Timed out waiting for sign payload. Please try again.");
1316
1399
  }
1317
1400
  const hashBytes = hexToBytes(payload.userOpHash);
1318
- const allowCredentials = payload.passkeyCredentialId ? [{
1319
- type: "public-key",
1320
- id: base64ToBytes(payload.passkeyCredentialId)
1321
- }] : void 0;
1322
- await waitForDocumentFocus();
1323
- const assertion = await navigator.credentials.get({
1324
- publicKey: {
1325
- challenge: hashBytes,
1401
+ let signedUserOp;
1402
+ if (isInCrossOriginIframe()) {
1403
+ const delegatedResult = await delegatePasskeyGet({
1404
+ challenge: toBase64(hashBytes),
1326
1405
  rpId: resolvePasskeyRpId(),
1327
- allowCredentials,
1406
+ allowCredentials: payload.passkeyCredentialId ? [{ type: "public-key", id: payload.passkeyCredentialId }] : void 0,
1328
1407
  userVerification: "required",
1329
1408
  timeout: 6e4
1409
+ });
1410
+ signedUserOp = {
1411
+ ...payload.userOp,
1412
+ credentialId: delegatedResult.credentialId,
1413
+ signature: delegatedResult.signature,
1414
+ authenticatorData: delegatedResult.authenticatorData,
1415
+ clientDataJSON: delegatedResult.clientDataJSON
1416
+ };
1417
+ } else {
1418
+ const allowCredentials = payload.passkeyCredentialId ? [{
1419
+ type: "public-key",
1420
+ id: base64ToBytes(payload.passkeyCredentialId)
1421
+ }] : void 0;
1422
+ await waitForDocumentFocus();
1423
+ const assertion = await navigator.credentials.get({
1424
+ publicKey: {
1425
+ challenge: hashBytes,
1426
+ rpId: resolvePasskeyRpId(),
1427
+ allowCredentials,
1428
+ userVerification: "required",
1429
+ timeout: 6e4
1430
+ }
1431
+ });
1432
+ if (!assertion) {
1433
+ throw new Error("Passkey authentication was cancelled.");
1330
1434
  }
1331
- });
1332
- if (!assertion) {
1333
- throw new Error("Passkey authentication was cancelled.");
1435
+ const response = assertion.response;
1436
+ signedUserOp = {
1437
+ ...payload.userOp,
1438
+ credentialId: toBase64(assertion.rawId),
1439
+ signature: toBase64(response.signature),
1440
+ authenticatorData: toBase64(response.authenticatorData),
1441
+ clientDataJSON: toBase64(response.clientDataJSON)
1442
+ };
1334
1443
  }
1335
- const response = assertion.response;
1336
- const signedUserOp = {
1337
- ...payload.userOp,
1338
- credentialId: toBase64(assertion.rawId),
1339
- signature: toBase64(response.signature),
1340
- authenticatorData: toBase64(response.authenticatorData),
1341
- clientDataJSON: toBase64(response.clientDataJSON)
1342
- };
1343
1444
  return await signTransfer(
1344
1445
  apiBaseUrl,
1345
1446
  token ?? "",
@@ -3869,6 +3970,7 @@ function SwypePayment({
3869
3970
  const [mobileFlow, setMobileFlow] = useState(false);
3870
3971
  const pollingTransferIdRef = useRef(null);
3871
3972
  const mobileSigningTransferIdRef = useRef(null);
3973
+ const mobileSetupFlowRef = useRef(false);
3872
3974
  const processingStartedAtRef = useRef(null);
3873
3975
  const [selectSourceChainName, setSelectSourceChainName] = useState("");
3874
3976
  const [selectSourceTokenSymbol, setSelectSourceTokenSymbol] = useState("");
@@ -3879,7 +3981,7 @@ function SwypePayment({
3879
3981
  const transferSigning = useTransferSigning();
3880
3982
  const sourceType = connectingNewAccount ? "providerId" : selectedWalletId ? "walletId" : selectedAccountId ? "accountId" : "providerId";
3881
3983
  const sourceId = connectingNewAccount ? selectedProviderId ?? "" : selectedWalletId ? selectedWalletId : selectedAccountId ? selectedAccountId : selectedProviderId ?? "";
3882
- useCallback(async () => {
3984
+ const reloadAccounts = useCallback(async () => {
3883
3985
  const token = await getAccessToken();
3884
3986
  if (!token || !activeCredentialId) return;
3885
3987
  const [accts, prov] = await Promise.all([
@@ -4110,6 +4212,16 @@ function SwypePayment({
4110
4212
  if (!mobileFlow) return;
4111
4213
  const polledTransfer = polling.transfer;
4112
4214
  if (!polledTransfer || polledTransfer.status !== "AUTHORIZED") return;
4215
+ if (mobileSetupFlowRef.current) {
4216
+ mobileSetupFlowRef.current = false;
4217
+ setMobileFlow(false);
4218
+ polling.stopPolling();
4219
+ setTransfer(polledTransfer);
4220
+ reloadAccounts().catch(() => {
4221
+ });
4222
+ setStep("deposit");
4223
+ return;
4224
+ }
4113
4225
  if (transferSigning.signing) return;
4114
4226
  if (mobileSigningTransferIdRef.current === polledTransfer.id) return;
4115
4227
  mobileSigningTransferIdRef.current = polledTransfer.id;
@@ -4125,7 +4237,7 @@ function SwypePayment({
4125
4237
  }
4126
4238
  };
4127
4239
  void sign();
4128
- }, [mobileFlow, polling.transfer, transferSigning, onError]);
4240
+ }, [mobileFlow, polling.transfer, polling.stopPolling, transferSigning, onError, reloadAccounts]);
4129
4241
  useEffect(() => {
4130
4242
  if (!mobileFlow) return;
4131
4243
  const transferIdToResume = pollingTransferIdRef.current ?? transfer?.id;
@@ -4227,13 +4339,13 @@ function SwypePayment({
4227
4339
  }
4228
4340
  return count;
4229
4341
  }, [accounts]);
4230
- const handlePay = useCallback(async (depositAmount2) => {
4342
+ const handlePay = useCallback(async (depositAmount2, sourceOverrides) => {
4231
4343
  const parsedAmount = depositAmount2;
4232
4344
  if (isNaN(parsedAmount) || parsedAmount < MIN_SEND_AMOUNT_USD) {
4233
4345
  setError(`Minimum amount is $${MIN_SEND_AMOUNT_USD.toFixed(2)}.`);
4234
4346
  return;
4235
4347
  }
4236
- if (!sourceId) {
4348
+ if (!sourceOverrides?.sourceId && !sourceId) {
4237
4349
  setError("No account or provider selected.");
4238
4350
  return;
4239
4351
  }
@@ -4242,16 +4354,25 @@ function SwypePayment({
4242
4354
  setStep("create-passkey");
4243
4355
  return;
4244
4356
  }
4245
- setStep("processing");
4357
+ const isSetupRedirect = mobileSetupFlowRef.current;
4358
+ if (!isSetupRedirect) {
4359
+ setStep("processing");
4360
+ }
4246
4361
  processingStartedAtRef.current = Date.now();
4247
4362
  setError(null);
4248
4363
  setCreatingTransfer(true);
4249
4364
  setMobileFlow(false);
4250
4365
  try {
4366
+ if (transfer?.status === "AUTHORIZED") {
4367
+ const signedTransfer2 = await transferSigning.signTransfer(transfer.id);
4368
+ setTransfer(signedTransfer2);
4369
+ polling.startPolling(transfer.id);
4370
+ return;
4371
+ }
4251
4372
  const token = await getAccessToken();
4252
4373
  if (!token) throw new Error("Not authenticated");
4253
- let effectiveSourceType = sourceType;
4254
- let effectiveSourceId = sourceId;
4374
+ let effectiveSourceType = sourceOverrides?.sourceType ?? sourceType;
4375
+ let effectiveSourceId = sourceOverrides?.sourceId ?? sourceId;
4255
4376
  if (effectiveSourceType === "accountId") {
4256
4377
  const acct = accounts.find((a) => a.id === effectiveSourceId);
4257
4378
  const activeWallet = acct?.wallets.find((w) => w.status === "ACTIVE");
@@ -4329,7 +4450,8 @@ function SwypePayment({
4329
4450
  onError,
4330
4451
  useWalletConnector,
4331
4452
  idempotencyKey,
4332
- merchantAuthorization
4453
+ merchantAuthorization,
4454
+ transfer
4333
4455
  ]);
4334
4456
  const handleRegisterPasskey = useCallback(async () => {
4335
4457
  setRegisteringPasskey(true);
@@ -4373,8 +4495,18 @@ function SwypePayment({
4373
4495
  setSelectedProviderId(providerId);
4374
4496
  setSelectedAccountId(null);
4375
4497
  setConnectingNewAccount(true);
4376
- setStep("deposit");
4377
- }, []);
4498
+ const isMobile = !shouldUseWalletConnector({
4499
+ useWalletConnector,
4500
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
4501
+ });
4502
+ if (isMobile) {
4503
+ mobileSetupFlowRef.current = true;
4504
+ const amount2 = depositAmount ?? 5;
4505
+ handlePay(amount2, { sourceType: "providerId", sourceId: providerId });
4506
+ } else {
4507
+ setStep("deposit");
4508
+ }
4509
+ }, [useWalletConnector, depositAmount, handlePay]);
4378
4510
  const handleContinueConnection = useCallback(
4379
4511
  (accountId) => {
4380
4512
  const acct = accounts.find((a) => a.id === accountId);