@swype-org/react-sdk 0.1.86 → 0.1.87

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.d.cts CHANGED
@@ -189,11 +189,13 @@ interface UserConfig {
189
189
  passkey?: {
190
190
  credentialId: string;
191
191
  publicKey: string;
192
+ lastVerificationToken?: string | null;
192
193
  } | null;
193
194
  /** All registered WebAuthn passkey credentials for this user */
194
195
  passkeys?: {
195
196
  credentialId: string;
196
197
  publicKey: string;
198
+ lastVerificationToken?: string | null;
197
199
  }[];
198
200
  }
199
201
  /** Theme mode */
@@ -466,6 +468,12 @@ interface PasskeyVerifyPopupOptions {
466
468
  rpId: string;
467
469
  /** Populated by `findDevicePasskeyViaPopup`; not set by callers. */
468
470
  channelId?: string;
471
+ /** Populated by `findDevicePasskeyViaPopup`; not set by callers. */
472
+ verificationToken?: string;
473
+ /** Privy JWT so the popup can report verification server-side. */
474
+ authToken?: string;
475
+ /** Core API base URL for server-side passkey activity reporting. */
476
+ apiBaseUrl?: string;
469
477
  }
470
478
  /**
471
479
  * Opens a same-origin pop-up window on the Swype domain to check whether
@@ -475,6 +483,11 @@ interface PasskeyVerifyPopupOptions {
475
483
  * inside a cross-origin iframe. The popup runs on the Swype domain where
476
484
  * the rpId matches, so WebAuthn works.
477
485
  *
486
+ * When `authToken` and `apiBaseUrl` are provided, the popup also writes a
487
+ * verification token to the backend. If Safari's ITP blocks both
488
+ * BroadcastChannel and window.opener.postMessage, the opener falls back to
489
+ * checking the server for the matching token after the popup closes.
490
+ *
478
491
  * Must be called from a user-gesture handler (e.g. button click) to
479
492
  * avoid the browser's pop-up blocker.
480
493
  *
package/dist/index.d.ts CHANGED
@@ -189,11 +189,13 @@ interface UserConfig {
189
189
  passkey?: {
190
190
  credentialId: string;
191
191
  publicKey: string;
192
+ lastVerificationToken?: string | null;
192
193
  } | null;
193
194
  /** All registered WebAuthn passkey credentials for this user */
194
195
  passkeys?: {
195
196
  credentialId: string;
196
197
  publicKey: string;
198
+ lastVerificationToken?: string | null;
197
199
  }[];
198
200
  }
199
201
  /** Theme mode */
@@ -466,6 +468,12 @@ interface PasskeyVerifyPopupOptions {
466
468
  rpId: string;
467
469
  /** Populated by `findDevicePasskeyViaPopup`; not set by callers. */
468
470
  channelId?: string;
471
+ /** Populated by `findDevicePasskeyViaPopup`; not set by callers. */
472
+ verificationToken?: string;
473
+ /** Privy JWT so the popup can report verification server-side. */
474
+ authToken?: string;
475
+ /** Core API base URL for server-side passkey activity reporting. */
476
+ apiBaseUrl?: string;
469
477
  }
470
478
  /**
471
479
  * Opens a same-origin pop-up window on the Swype domain to check whether
@@ -475,6 +483,11 @@ interface PasskeyVerifyPopupOptions {
475
483
  * inside a cross-origin iframe. The popup runs on the Swype domain where
476
484
  * the rpId matches, so WebAuthn works.
477
485
  *
486
+ * When `authToken` and `apiBaseUrl` are provided, the popup also writes a
487
+ * verification token to the backend. If Safari's ITP blocks both
488
+ * BroadcastChannel and window.opener.postMessage, the opener falls back to
489
+ * checking the server for the matching token after the popup closes.
490
+ *
478
491
  * Must be called from a user-gesture handler (e.g. button click) to
479
492
  * avoid the browser's pop-up blocker.
480
493
  *
package/dist/index.js CHANGED
@@ -801,7 +801,12 @@ var VERIFY_POPUP_TIMEOUT_MS = 6e4;
801
801
  function findDevicePasskeyViaPopup(options) {
802
802
  return new Promise((resolve, reject) => {
803
803
  const channelId = `swype-pv-${Date.now()}-${Math.random().toString(36).slice(2)}`;
804
- const payload = { ...options, channelId };
804
+ const verificationToken = crypto.randomUUID();
805
+ const payload = {
806
+ ...options,
807
+ channelId,
808
+ verificationToken
809
+ };
805
810
  const encoded = btoa(JSON.stringify(payload));
806
811
  const popupUrl = `${window.location.origin}/passkey-verify#${encoded}`;
807
812
  const popup = window.open(popupUrl, "swype-passkey-verify");
@@ -821,7 +826,15 @@ function findDevicePasskeyViaPopup(options) {
821
826
  setTimeout(() => {
822
827
  if (!settled) {
823
828
  cleanup();
824
- resolve(null);
829
+ checkServerForVerifiedPasskey(
830
+ options.authToken,
831
+ options.apiBaseUrl,
832
+ verificationToken
833
+ ).then((credentialId) => {
834
+ resolve(credentialId);
835
+ }).catch(() => {
836
+ resolve(null);
837
+ });
825
838
  }
826
839
  }, POPUP_CLOSED_GRACE_MS);
827
840
  }
@@ -857,6 +870,17 @@ function findDevicePasskeyViaPopup(options) {
857
870
  }
858
871
  });
859
872
  }
873
+ async function checkServerForVerifiedPasskey(authToken, apiBaseUrl, verificationToken) {
874
+ if (!authToken || !apiBaseUrl) return null;
875
+ const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
876
+ headers: { Authorization: `Bearer ${authToken}` }
877
+ });
878
+ if (!res.ok) return null;
879
+ const body = await res.json();
880
+ const passkeys = body.config.passkeys ?? [];
881
+ const matched = passkeys.find((p) => p.lastVerificationToken === verificationToken);
882
+ return matched?.credentialId ?? null;
883
+ }
860
884
  async function checkServerForNewPasskey(authToken, apiBaseUrl, existingCredentialIds) {
861
885
  if (!authToken || !apiBaseUrl) return null;
862
886
  const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
@@ -5588,14 +5612,16 @@ function SwypePaymentInner({
5588
5612
  setVerifyingPasskeyPopup(true);
5589
5613
  setError(null);
5590
5614
  try {
5615
+ const token = await getAccessToken();
5591
5616
  const matched = await findDevicePasskeyViaPopup({
5592
5617
  credentialIds: knownCredentialIds,
5593
- rpId: resolvePasskeyRpId()
5618
+ rpId: resolvePasskeyRpId(),
5619
+ authToken: token ?? void 0,
5620
+ apiBaseUrl
5594
5621
  });
5595
5622
  if (matched) {
5596
5623
  setActiveCredentialId(matched);
5597
5624
  window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, matched);
5598
- const token = await getAccessToken();
5599
5625
  if (token) {
5600
5626
  reportPasskeyActivity(apiBaseUrl, token, matched).catch(() => {
5601
5627
  });