@swype-org/react-sdk 0.1.82 → 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.cjs +49 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +49 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
@@ -712,7 +712,8 @@ function isSafari() {
|
|
|
712
712
|
}
|
|
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, existingCredentialIds = []) {
|
|
716
717
|
return new Promise((resolve, reject) => {
|
|
717
718
|
const channelId = `swype-pk-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
718
719
|
const payload = { ...options, channelId };
|
|
@@ -729,10 +730,28 @@ function createPasskeyViaPopup(options) {
|
|
|
729
730
|
cleanup();
|
|
730
731
|
reject(new Error("Passkey creation timed out. Please try again."));
|
|
731
732
|
}, POPUP_RESULT_TIMEOUT_MS);
|
|
733
|
+
let closedGraceTimer = null;
|
|
732
734
|
const closedPoll = setInterval(() => {
|
|
733
735
|
if (popup.closed) {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
+
clearInterval(closedPoll);
|
|
737
|
+
closedGraceTimer = setTimeout(() => {
|
|
738
|
+
if (!settled) {
|
|
739
|
+
cleanup();
|
|
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
|
+
});
|
|
753
|
+
}
|
|
754
|
+
}, POPUP_CLOSED_GRACE_MS);
|
|
736
755
|
}
|
|
737
756
|
}, POPUP_CLOSED_POLL_MS);
|
|
738
757
|
function handleResult(data) {
|
|
@@ -760,11 +779,24 @@ function createPasskeyViaPopup(options) {
|
|
|
760
779
|
function cleanup() {
|
|
761
780
|
clearTimeout(timer);
|
|
762
781
|
clearInterval(closedPoll);
|
|
782
|
+
if (closedGraceTimer) clearTimeout(closedGraceTimer);
|
|
763
783
|
window.removeEventListener("message", postMessageHandler);
|
|
764
784
|
channel?.close();
|
|
765
785
|
}
|
|
766
786
|
});
|
|
767
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
|
+
}
|
|
768
800
|
|
|
769
801
|
// src/hooks.ts
|
|
770
802
|
var WALLET_CLIENT_MAX_ATTEMPTS = 15;
|
|
@@ -959,7 +991,9 @@ function buildPasskeyPopupOptions(params) {
|
|
|
959
991
|
residentKey: "preferred",
|
|
960
992
|
userVerification: "required"
|
|
961
993
|
},
|
|
962
|
-
timeout: 6e4
|
|
994
|
+
timeout: 6e4,
|
|
995
|
+
authToken: params.authToken,
|
|
996
|
+
apiBaseUrl: params.apiBaseUrl
|
|
963
997
|
};
|
|
964
998
|
}
|
|
965
999
|
async function deviceHasPasskey(credentialId) {
|
|
@@ -4578,6 +4612,7 @@ function SwypePaymentInner({
|
|
|
4578
4612
|
if (typeof window === "undefined") return null;
|
|
4579
4613
|
return window.localStorage.getItem(ACTIVE_CREDENTIAL_STORAGE_KEY);
|
|
4580
4614
|
});
|
|
4615
|
+
const [knownCredentialIds, setKnownCredentialIds] = useState([]);
|
|
4581
4616
|
const [authInput, setAuthInput] = useState("");
|
|
4582
4617
|
const [verificationTarget, setVerificationTarget] = useState(null);
|
|
4583
4618
|
const [otpCode, setOtpCode] = useState("");
|
|
@@ -4867,6 +4902,7 @@ function SwypePaymentInner({
|
|
|
4867
4902
|
setOneTapLimit(config.defaultAllowance);
|
|
4868
4903
|
}
|
|
4869
4904
|
const allPasskeys = config.passkeys ?? (config.passkey ? [config.passkey] : []);
|
|
4905
|
+
setKnownCredentialIds(allPasskeys.map((p) => p.credentialId));
|
|
4870
4906
|
if (allPasskeys.length === 0) {
|
|
4871
4907
|
setStep("create-passkey");
|
|
4872
4908
|
return;
|
|
@@ -5383,12 +5419,18 @@ function SwypePaymentInner({
|
|
|
5383
5419
|
setRegisteringPasskey(true);
|
|
5384
5420
|
setError(null);
|
|
5385
5421
|
try {
|
|
5422
|
+
const token = await getAccessToken();
|
|
5386
5423
|
const passkeyDisplayName = user?.email?.address ?? user?.google?.name ?? user?.id ?? "Swype User";
|
|
5387
5424
|
const popupOptions = buildPasskeyPopupOptions({
|
|
5388
5425
|
userId: user?.id ?? "unknown",
|
|
5389
|
-
displayName: passkeyDisplayName
|
|
5426
|
+
displayName: passkeyDisplayName,
|
|
5427
|
+
authToken: token ?? void 0,
|
|
5428
|
+
apiBaseUrl
|
|
5390
5429
|
});
|
|
5391
|
-
const { credentialId, publicKey } = await createPasskeyViaPopup(
|
|
5430
|
+
const { credentialId, publicKey } = await createPasskeyViaPopup(
|
|
5431
|
+
popupOptions,
|
|
5432
|
+
knownCredentialIds
|
|
5433
|
+
);
|
|
5392
5434
|
await completePasskeyRegistration(credentialId, publicKey);
|
|
5393
5435
|
} catch (err) {
|
|
5394
5436
|
captureException(err);
|
|
@@ -5396,7 +5438,7 @@ function SwypePaymentInner({
|
|
|
5396
5438
|
} finally {
|
|
5397
5439
|
setRegisteringPasskey(false);
|
|
5398
5440
|
}
|
|
5399
|
-
}, [user, completePasskeyRegistration]);
|
|
5441
|
+
}, [user, completePasskeyRegistration, getAccessToken, apiBaseUrl, knownCredentialIds]);
|
|
5400
5442
|
const handleSelectProvider = useCallback((providerId) => {
|
|
5401
5443
|
setSelectedProviderId(providerId);
|
|
5402
5444
|
setSelectedAccountId(null);
|