@swype-org/react-sdk 0.1.83 → 0.1.84

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
@@ -422,6 +422,10 @@ interface PasskeyPopupOptions {
422
422
  timeout?: number;
423
423
  /** Populated by `createPasskeyViaPopup`; not set by callers. */
424
424
  channelId?: string;
425
+ /** Privy JWT so the popup can register the passkey server-side. */
426
+ authToken?: string;
427
+ /** Core API base URL for server-side passkey registration. */
428
+ apiBaseUrl?: string;
425
429
  }
426
430
  /**
427
431
  * Opens a same-origin pop-up window on the Swype domain to perform
@@ -433,10 +437,19 @@ interface PasskeyPopupOptions {
433
437
  * Falls back to `window.postMessage` for browsers that preserve the
434
438
  * opener reference.
435
439
  *
440
+ * When both client-side channels are blocked (Safari ITP partitions
441
+ * BroadcastChannel by top-level origin), the popup registers the
442
+ * passkey directly with the backend. On popup close, this function
443
+ * checks the server for newly registered passkeys as a final fallback.
444
+ *
436
445
  * Must be called from a user-gesture handler (e.g. button click) to
437
446
  * avoid the browser's pop-up blocker.
447
+ *
448
+ * @param existingCredentialIds - Credential IDs known before the popup
449
+ * opens. Used to diff against server state when the popup closes
450
+ * without delivering a client-side result.
438
451
  */
439
- declare function createPasskeyViaPopup(options: PasskeyPopupOptions): Promise<{
452
+ declare function createPasskeyViaPopup(options: PasskeyPopupOptions, existingCredentialIds?: string[]): Promise<{
440
453
  credentialId: string;
441
454
  publicKey: string;
442
455
  }>;
@@ -464,10 +477,16 @@ declare function createPasskeyCredential(params: {
464
477
  * Builds the {@link PasskeyPopupOptions} for a user. Called by the UI
465
478
  * layer when it needs to open the passkey creation pop-up after
466
479
  * `createPasskeyCredential` throws {@link PasskeyIframeBlockedError}.
480
+ *
481
+ * @param params.authToken - Privy JWT so the popup can register the
482
+ * passkey server-side (Safari fallback when BroadcastChannel is blocked).
483
+ * @param params.apiBaseUrl - Core API base URL for server-side registration.
467
484
  */
468
485
  declare function buildPasskeyPopupOptions(params: {
469
486
  userId: string;
470
487
  displayName: string;
488
+ authToken?: string;
489
+ apiBaseUrl?: string;
471
490
  }): PasskeyPopupOptions;
472
491
  /**
473
492
  * @deprecated Use {@link findDevicePasskey} instead, which checks all
package/dist/index.d.ts CHANGED
@@ -422,6 +422,10 @@ interface PasskeyPopupOptions {
422
422
  timeout?: number;
423
423
  /** Populated by `createPasskeyViaPopup`; not set by callers. */
424
424
  channelId?: string;
425
+ /** Privy JWT so the popup can register the passkey server-side. */
426
+ authToken?: string;
427
+ /** Core API base URL for server-side passkey registration. */
428
+ apiBaseUrl?: string;
425
429
  }
426
430
  /**
427
431
  * Opens a same-origin pop-up window on the Swype domain to perform
@@ -433,10 +437,19 @@ interface PasskeyPopupOptions {
433
437
  * Falls back to `window.postMessage` for browsers that preserve the
434
438
  * opener reference.
435
439
  *
440
+ * When both client-side channels are blocked (Safari ITP partitions
441
+ * BroadcastChannel by top-level origin), the popup registers the
442
+ * passkey directly with the backend. On popup close, this function
443
+ * checks the server for newly registered passkeys as a final fallback.
444
+ *
436
445
  * Must be called from a user-gesture handler (e.g. button click) to
437
446
  * avoid the browser's pop-up blocker.
447
+ *
448
+ * @param existingCredentialIds - Credential IDs known before the popup
449
+ * opens. Used to diff against server state when the popup closes
450
+ * without delivering a client-side result.
438
451
  */
439
- declare function createPasskeyViaPopup(options: PasskeyPopupOptions): Promise<{
452
+ declare function createPasskeyViaPopup(options: PasskeyPopupOptions, existingCredentialIds?: string[]): Promise<{
440
453
  credentialId: string;
441
454
  publicKey: string;
442
455
  }>;
@@ -464,10 +477,16 @@ declare function createPasskeyCredential(params: {
464
477
  * Builds the {@link PasskeyPopupOptions} for a user. Called by the UI
465
478
  * layer when it needs to open the passkey creation pop-up after
466
479
  * `createPasskeyCredential` throws {@link PasskeyIframeBlockedError}.
480
+ *
481
+ * @param params.authToken - Privy JWT so the popup can register the
482
+ * passkey server-side (Safari fallback when BroadcastChannel is blocked).
483
+ * @param params.apiBaseUrl - Core API base URL for server-side registration.
467
484
  */
468
485
  declare function buildPasskeyPopupOptions(params: {
469
486
  userId: string;
470
487
  displayName: string;
488
+ authToken?: string;
489
+ apiBaseUrl?: string;
471
490
  }): PasskeyPopupOptions;
472
491
  /**
473
492
  * @deprecated Use {@link findDevicePasskey} instead, which checks all
package/dist/index.js CHANGED
@@ -713,7 +713,7 @@ function isSafari() {
713
713
  var POPUP_RESULT_TIMEOUT_MS = 12e4;
714
714
  var POPUP_CLOSED_POLL_MS = 500;
715
715
  var POPUP_CLOSED_GRACE_MS = 1e3;
716
- function createPasskeyViaPopup(options) {
716
+ function createPasskeyViaPopup(options, existingCredentialIds = []) {
717
717
  return new Promise((resolve, reject) => {
718
718
  const channelId = `swype-pk-${Date.now()}-${Math.random().toString(36).slice(2)}`;
719
719
  const payload = { ...options, channelId };
@@ -737,7 +737,19 @@ function createPasskeyViaPopup(options) {
737
737
  closedGraceTimer = setTimeout(() => {
738
738
  if (!settled) {
739
739
  cleanup();
740
- reject(new Error("Passkey setup window was closed before completing."));
740
+ checkServerForNewPasskey(
741
+ options.authToken,
742
+ options.apiBaseUrl,
743
+ existingCredentialIds
744
+ ).then((result) => {
745
+ if (result) {
746
+ resolve(result);
747
+ } else {
748
+ reject(new Error("Passkey setup window was closed before completing."));
749
+ }
750
+ }).catch(() => {
751
+ reject(new Error("Passkey setup window was closed before completing."));
752
+ });
741
753
  }
742
754
  }, POPUP_CLOSED_GRACE_MS);
743
755
  }
@@ -773,6 +785,18 @@ function createPasskeyViaPopup(options) {
773
785
  }
774
786
  });
775
787
  }
788
+ async function checkServerForNewPasskey(authToken, apiBaseUrl, existingCredentialIds) {
789
+ if (!authToken || !apiBaseUrl) return null;
790
+ const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
791
+ headers: { Authorization: `Bearer ${authToken}` }
792
+ });
793
+ if (!res.ok) return null;
794
+ const body = await res.json();
795
+ const passkeys = body.config.passkeys ?? [];
796
+ const existingSet = new Set(existingCredentialIds);
797
+ const newPasskey = passkeys.find((p) => !existingSet.has(p.credentialId));
798
+ return newPasskey ?? null;
799
+ }
776
800
 
777
801
  // src/hooks.ts
778
802
  var WALLET_CLIENT_MAX_ATTEMPTS = 15;
@@ -967,7 +991,9 @@ function buildPasskeyPopupOptions(params) {
967
991
  residentKey: "preferred",
968
992
  userVerification: "required"
969
993
  },
970
- timeout: 6e4
994
+ timeout: 6e4,
995
+ authToken: params.authToken,
996
+ apiBaseUrl: params.apiBaseUrl
971
997
  };
972
998
  }
973
999
  async function deviceHasPasskey(credentialId) {
@@ -4586,6 +4612,7 @@ function SwypePaymentInner({
4586
4612
  if (typeof window === "undefined") return null;
4587
4613
  return window.localStorage.getItem(ACTIVE_CREDENTIAL_STORAGE_KEY);
4588
4614
  });
4615
+ const [knownCredentialIds, setKnownCredentialIds] = useState([]);
4589
4616
  const [authInput, setAuthInput] = useState("");
4590
4617
  const [verificationTarget, setVerificationTarget] = useState(null);
4591
4618
  const [otpCode, setOtpCode] = useState("");
@@ -4875,6 +4902,7 @@ function SwypePaymentInner({
4875
4902
  setOneTapLimit(config.defaultAllowance);
4876
4903
  }
4877
4904
  const allPasskeys = config.passkeys ?? (config.passkey ? [config.passkey] : []);
4905
+ setKnownCredentialIds(allPasskeys.map((p) => p.credentialId));
4878
4906
  if (allPasskeys.length === 0) {
4879
4907
  setStep("create-passkey");
4880
4908
  return;
@@ -5391,12 +5419,18 @@ function SwypePaymentInner({
5391
5419
  setRegisteringPasskey(true);
5392
5420
  setError(null);
5393
5421
  try {
5422
+ const token = await getAccessToken();
5394
5423
  const passkeyDisplayName = user?.email?.address ?? user?.google?.name ?? user?.id ?? "Swype User";
5395
5424
  const popupOptions = buildPasskeyPopupOptions({
5396
5425
  userId: user?.id ?? "unknown",
5397
- displayName: passkeyDisplayName
5426
+ displayName: passkeyDisplayName,
5427
+ authToken: token ?? void 0,
5428
+ apiBaseUrl
5398
5429
  });
5399
- const { credentialId, publicKey } = await createPasskeyViaPopup(popupOptions);
5430
+ const { credentialId, publicKey } = await createPasskeyViaPopup(
5431
+ popupOptions,
5432
+ knownCredentialIds
5433
+ );
5400
5434
  await completePasskeyRegistration(credentialId, publicKey);
5401
5435
  } catch (err) {
5402
5436
  captureException(err);
@@ -5404,7 +5438,7 @@ function SwypePaymentInner({
5404
5438
  } finally {
5405
5439
  setRegisteringPasskey(false);
5406
5440
  }
5407
- }, [user, completePasskeyRegistration]);
5441
+ }, [user, completePasskeyRegistration, getAccessToken, apiBaseUrl, knownCredentialIds]);
5408
5442
  const handleSelectProvider = useCallback((providerId) => {
5409
5443
  setSelectedProviderId(providerId);
5410
5444
  setSelectedAccountId(null);