@swype-org/react-sdk 0.1.49 → 0.1.52
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 +163 -138
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +63 -1
- package/dist/index.d.ts +63 -1
- package/dist/index.js +161 -139
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -379,6 +379,59 @@ interface SwypePaymentProps {
|
|
|
379
379
|
}
|
|
380
380
|
declare function SwypePayment(props: SwypePaymentProps): react_jsx_runtime.JSX.Element;
|
|
381
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Cross-origin iframe passkey delegation via postMessage.
|
|
384
|
+
*
|
|
385
|
+
* When the webview-app runs inside a cross-origin iframe, WebAuthn
|
|
386
|
+
* ceremonies cannot execute from within the iframe on Safari. For passkey
|
|
387
|
+
* creation, we first attempt a direct `navigator.credentials.create()`
|
|
388
|
+
* call (works in Chrome/Firefox with the iframe permissions policy). If
|
|
389
|
+
* that fails (Safari), we open a same-origin pop-up window on the Swype
|
|
390
|
+
* domain to perform the ceremony, then relay the result back via
|
|
391
|
+
* `postMessage`.
|
|
392
|
+
*
|
|
393
|
+
* Passkey *assertion* (`navigator.credentials.get`) still delegates to
|
|
394
|
+
* the parent page via postMessage as before.
|
|
395
|
+
*/
|
|
396
|
+
/**
|
|
397
|
+
* Thrown when `navigator.credentials.create()` fails inside a
|
|
398
|
+
* cross-origin iframe (Safari). The UI layer should catch this and
|
|
399
|
+
* offer the user a button that opens the Swype passkey pop-up.
|
|
400
|
+
*/
|
|
401
|
+
declare class PasskeyIframeBlockedError extends Error {
|
|
402
|
+
constructor(message?: string);
|
|
403
|
+
}
|
|
404
|
+
interface PasskeyPopupOptions {
|
|
405
|
+
challenge: string;
|
|
406
|
+
rpId: string;
|
|
407
|
+
rpName: string;
|
|
408
|
+
userId: string;
|
|
409
|
+
userName: string;
|
|
410
|
+
userDisplayName: string;
|
|
411
|
+
pubKeyCredParams: Array<{
|
|
412
|
+
alg: number;
|
|
413
|
+
type: string;
|
|
414
|
+
}>;
|
|
415
|
+
authenticatorSelection?: {
|
|
416
|
+
authenticatorAttachment?: string;
|
|
417
|
+
residentKey?: string;
|
|
418
|
+
userVerification?: string;
|
|
419
|
+
};
|
|
420
|
+
timeout?: number;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Opens a same-origin pop-up window on the Swype domain to perform
|
|
424
|
+
* passkey creation. Used as a fallback when Safari blocks
|
|
425
|
+
* `navigator.credentials.create()` inside a cross-origin iframe.
|
|
426
|
+
*
|
|
427
|
+
* Must be called from a user-gesture handler (e.g. button click) to
|
|
428
|
+
* avoid the browser's pop-up blocker.
|
|
429
|
+
*/
|
|
430
|
+
declare function createPasskeyViaPopup(options: PasskeyPopupOptions): Promise<{
|
|
431
|
+
credentialId: string;
|
|
432
|
+
publicKey: string;
|
|
433
|
+
}>;
|
|
434
|
+
|
|
382
435
|
type AccessTokenGetter = () => Promise<string | null | undefined>;
|
|
383
436
|
/**
|
|
384
437
|
* Creates a WebAuthn passkey credential.
|
|
@@ -398,6 +451,15 @@ declare function createPasskeyCredential(params: {
|
|
|
398
451
|
credentialId: string;
|
|
399
452
|
publicKey: string;
|
|
400
453
|
}>;
|
|
454
|
+
/**
|
|
455
|
+
* Builds the {@link PasskeyPopupOptions} for a user. Called by the UI
|
|
456
|
+
* layer when it needs to open the passkey creation pop-up after
|
|
457
|
+
* `createPasskeyCredential` throws {@link PasskeyIframeBlockedError}.
|
|
458
|
+
*/
|
|
459
|
+
declare function buildPasskeyPopupOptions(params: {
|
|
460
|
+
userId: string;
|
|
461
|
+
displayName: string;
|
|
462
|
+
}): PasskeyPopupOptions;
|
|
401
463
|
/**
|
|
402
464
|
* @deprecated Use {@link findDevicePasskey} instead, which checks all
|
|
403
465
|
* credentials in a single WebAuthn call.
|
|
@@ -596,4 +658,4 @@ interface SelectSourceScreenProps {
|
|
|
596
658
|
}
|
|
597
659
|
declare function SelectSourceScreen({ choices, selectedChainName, selectedTokenSymbol, recommended, onChainChange, onTokenChange, onConfirm, onLogout, }: SelectSourceScreenProps): react_jsx_runtime.JSX.Element;
|
|
598
660
|
|
|
599
|
-
export { type Account, type ActionExecutionResult, type AdvancedSettings, type Amount, type AuthorizationAction, type AuthorizationSession, type AuthorizationSessionDetail, type Chain, type Destination, type ErrorResponse, IconCircle, type ListResponse, type MerchantAuthorization, type MerchantPublicKey, OutlineButton, type PaymentStep, PoweredByFooter, PrimaryButton, type Provider, ScreenHeader, ScreenLayout, SelectSourceScreen, SettingsMenu, SetupScreen, type SourceOption, type SourceSelection, type SourceType, Spinner, type StepItem, StepList, SwypePayment, type SwypePaymentProps, SwypeProvider, type SwypeProviderProps, type ThemeMode, type ThemeTokens, type TokenBalance, type Transfer, type TransferDestination, type UserConfig, type Wallet, type WalletSource, type WalletToken, createPasskeyCredential, darkTheme, deviceHasPasskey, findDevicePasskey, getTheme, lightTheme, api as swypeApi, useAuthorizationExecutor, useSwypeConfig, useSwypeDepositAmount, useTransferPolling, useTransferSigning };
|
|
661
|
+
export { type Account, type ActionExecutionResult, type AdvancedSettings, type Amount, type AuthorizationAction, type AuthorizationSession, type AuthorizationSessionDetail, type Chain, type Destination, type ErrorResponse, IconCircle, type ListResponse, type MerchantAuthorization, type MerchantPublicKey, OutlineButton, PasskeyIframeBlockedError, type PaymentStep, PoweredByFooter, PrimaryButton, type Provider, ScreenHeader, ScreenLayout, SelectSourceScreen, SettingsMenu, SetupScreen, type SourceOption, type SourceSelection, type SourceType, Spinner, type StepItem, StepList, SwypePayment, type SwypePaymentProps, SwypeProvider, type SwypeProviderProps, type ThemeMode, type ThemeTokens, type TokenBalance, type Transfer, type TransferDestination, type UserConfig, type Wallet, type WalletSource, type WalletToken, buildPasskeyPopupOptions, createPasskeyCredential, createPasskeyViaPopup, darkTheme, deviceHasPasskey, findDevicePasskey, getTheme, lightTheme, api as swypeApi, useAuthorizationExecutor, useSwypeConfig, useSwypeDepositAmount, useTransferPolling, useTransferSigning };
|
package/dist/index.d.ts
CHANGED
|
@@ -379,6 +379,59 @@ interface SwypePaymentProps {
|
|
|
379
379
|
}
|
|
380
380
|
declare function SwypePayment(props: SwypePaymentProps): react_jsx_runtime.JSX.Element;
|
|
381
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Cross-origin iframe passkey delegation via postMessage.
|
|
384
|
+
*
|
|
385
|
+
* When the webview-app runs inside a cross-origin iframe, WebAuthn
|
|
386
|
+
* ceremonies cannot execute from within the iframe on Safari. For passkey
|
|
387
|
+
* creation, we first attempt a direct `navigator.credentials.create()`
|
|
388
|
+
* call (works in Chrome/Firefox with the iframe permissions policy). If
|
|
389
|
+
* that fails (Safari), we open a same-origin pop-up window on the Swype
|
|
390
|
+
* domain to perform the ceremony, then relay the result back via
|
|
391
|
+
* `postMessage`.
|
|
392
|
+
*
|
|
393
|
+
* Passkey *assertion* (`navigator.credentials.get`) still delegates to
|
|
394
|
+
* the parent page via postMessage as before.
|
|
395
|
+
*/
|
|
396
|
+
/**
|
|
397
|
+
* Thrown when `navigator.credentials.create()` fails inside a
|
|
398
|
+
* cross-origin iframe (Safari). The UI layer should catch this and
|
|
399
|
+
* offer the user a button that opens the Swype passkey pop-up.
|
|
400
|
+
*/
|
|
401
|
+
declare class PasskeyIframeBlockedError extends Error {
|
|
402
|
+
constructor(message?: string);
|
|
403
|
+
}
|
|
404
|
+
interface PasskeyPopupOptions {
|
|
405
|
+
challenge: string;
|
|
406
|
+
rpId: string;
|
|
407
|
+
rpName: string;
|
|
408
|
+
userId: string;
|
|
409
|
+
userName: string;
|
|
410
|
+
userDisplayName: string;
|
|
411
|
+
pubKeyCredParams: Array<{
|
|
412
|
+
alg: number;
|
|
413
|
+
type: string;
|
|
414
|
+
}>;
|
|
415
|
+
authenticatorSelection?: {
|
|
416
|
+
authenticatorAttachment?: string;
|
|
417
|
+
residentKey?: string;
|
|
418
|
+
userVerification?: string;
|
|
419
|
+
};
|
|
420
|
+
timeout?: number;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Opens a same-origin pop-up window on the Swype domain to perform
|
|
424
|
+
* passkey creation. Used as a fallback when Safari blocks
|
|
425
|
+
* `navigator.credentials.create()` inside a cross-origin iframe.
|
|
426
|
+
*
|
|
427
|
+
* Must be called from a user-gesture handler (e.g. button click) to
|
|
428
|
+
* avoid the browser's pop-up blocker.
|
|
429
|
+
*/
|
|
430
|
+
declare function createPasskeyViaPopup(options: PasskeyPopupOptions): Promise<{
|
|
431
|
+
credentialId: string;
|
|
432
|
+
publicKey: string;
|
|
433
|
+
}>;
|
|
434
|
+
|
|
382
435
|
type AccessTokenGetter = () => Promise<string | null | undefined>;
|
|
383
436
|
/**
|
|
384
437
|
* Creates a WebAuthn passkey credential.
|
|
@@ -398,6 +451,15 @@ declare function createPasskeyCredential(params: {
|
|
|
398
451
|
credentialId: string;
|
|
399
452
|
publicKey: string;
|
|
400
453
|
}>;
|
|
454
|
+
/**
|
|
455
|
+
* Builds the {@link PasskeyPopupOptions} for a user. Called by the UI
|
|
456
|
+
* layer when it needs to open the passkey creation pop-up after
|
|
457
|
+
* `createPasskeyCredential` throws {@link PasskeyIframeBlockedError}.
|
|
458
|
+
*/
|
|
459
|
+
declare function buildPasskeyPopupOptions(params: {
|
|
460
|
+
userId: string;
|
|
461
|
+
displayName: string;
|
|
462
|
+
}): PasskeyPopupOptions;
|
|
401
463
|
/**
|
|
402
464
|
* @deprecated Use {@link findDevicePasskey} instead, which checks all
|
|
403
465
|
* credentials in a single WebAuthn call.
|
|
@@ -596,4 +658,4 @@ interface SelectSourceScreenProps {
|
|
|
596
658
|
}
|
|
597
659
|
declare function SelectSourceScreen({ choices, selectedChainName, selectedTokenSymbol, recommended, onChainChange, onTokenChange, onConfirm, onLogout, }: SelectSourceScreenProps): react_jsx_runtime.JSX.Element;
|
|
598
660
|
|
|
599
|
-
export { type Account, type ActionExecutionResult, type AdvancedSettings, type Amount, type AuthorizationAction, type AuthorizationSession, type AuthorizationSessionDetail, type Chain, type Destination, type ErrorResponse, IconCircle, type ListResponse, type MerchantAuthorization, type MerchantPublicKey, OutlineButton, type PaymentStep, PoweredByFooter, PrimaryButton, type Provider, ScreenHeader, ScreenLayout, SelectSourceScreen, SettingsMenu, SetupScreen, type SourceOption, type SourceSelection, type SourceType, Spinner, type StepItem, StepList, SwypePayment, type SwypePaymentProps, SwypeProvider, type SwypeProviderProps, type ThemeMode, type ThemeTokens, type TokenBalance, type Transfer, type TransferDestination, type UserConfig, type Wallet, type WalletSource, type WalletToken, createPasskeyCredential, darkTheme, deviceHasPasskey, findDevicePasskey, getTheme, lightTheme, api as swypeApi, useAuthorizationExecutor, useSwypeConfig, useSwypeDepositAmount, useTransferPolling, useTransferSigning };
|
|
661
|
+
export { type Account, type ActionExecutionResult, type AdvancedSettings, type Amount, type AuthorizationAction, type AuthorizationSession, type AuthorizationSessionDetail, type Chain, type Destination, type ErrorResponse, IconCircle, type ListResponse, type MerchantAuthorization, type MerchantPublicKey, OutlineButton, PasskeyIframeBlockedError, type PaymentStep, PoweredByFooter, PrimaryButton, type Provider, ScreenHeader, ScreenLayout, SelectSourceScreen, SettingsMenu, SetupScreen, type SourceOption, type SourceSelection, type SourceType, Spinner, type StepItem, StepList, SwypePayment, type SwypePaymentProps, SwypeProvider, type SwypeProviderProps, type ThemeMode, type ThemeTokens, type TokenBalance, type Transfer, type TransferDestination, type UserConfig, type Wallet, type WalletSource, type WalletToken, buildPasskeyPopupOptions, createPasskeyCredential, createPasskeyViaPopup, darkTheme, deviceHasPasskey, findDevicePasskey, getTheme, lightTheme, api as swypeApi, useAuthorizationExecutor, useSwypeConfig, useSwypeDepositAmount, useTransferPolling, useTransferSigning };
|
package/dist/index.js
CHANGED
|
@@ -655,6 +655,12 @@ function normalizeSignature(sig) {
|
|
|
655
655
|
}
|
|
656
656
|
|
|
657
657
|
// src/passkey-delegation.ts
|
|
658
|
+
var PasskeyIframeBlockedError = class extends Error {
|
|
659
|
+
constructor(message = "Passkey creation is not supported in this browser context.") {
|
|
660
|
+
super(message);
|
|
661
|
+
this.name = "PasskeyIframeBlockedError";
|
|
662
|
+
}
|
|
663
|
+
};
|
|
658
664
|
function isInCrossOriginIframe() {
|
|
659
665
|
if (typeof window === "undefined") return false;
|
|
660
666
|
if (window.parent === window) return false;
|
|
@@ -665,57 +671,47 @@ function isInCrossOriginIframe() {
|
|
|
665
671
|
return true;
|
|
666
672
|
}
|
|
667
673
|
}
|
|
668
|
-
var
|
|
669
|
-
var
|
|
670
|
-
|
|
671
|
-
function delegatePasskeyCreate(options) {
|
|
674
|
+
var POPUP_RESULT_TIMEOUT_MS = 12e4;
|
|
675
|
+
var POPUP_CLOSED_POLL_MS = 500;
|
|
676
|
+
function createPasskeyViaPopup(options) {
|
|
672
677
|
return new Promise((resolve, reject) => {
|
|
673
|
-
const
|
|
678
|
+
const encoded = btoa(JSON.stringify(options));
|
|
679
|
+
const popupUrl = `${window.location.origin}/passkey-register#${encoded}`;
|
|
680
|
+
const popup = window.open(popupUrl, "swype-passkey", "width=460,height=600");
|
|
681
|
+
if (!popup) {
|
|
682
|
+
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
674
685
|
const timer = setTimeout(() => {
|
|
675
|
-
|
|
686
|
+
cleanup();
|
|
676
687
|
reject(new Error("Passkey creation timed out. Please try again."));
|
|
677
|
-
},
|
|
688
|
+
}, POPUP_RESULT_TIMEOUT_MS);
|
|
689
|
+
const closedPoll = setInterval(() => {
|
|
690
|
+
if (popup.closed) {
|
|
691
|
+
cleanup();
|
|
692
|
+
reject(new Error("Passkey setup window was closed before completing."));
|
|
693
|
+
}
|
|
694
|
+
}, POPUP_CLOSED_POLL_MS);
|
|
678
695
|
const handler = (event) => {
|
|
696
|
+
if (event.source !== popup) return;
|
|
679
697
|
const data = event.data;
|
|
680
698
|
if (!data || typeof data !== "object") return;
|
|
681
|
-
if (data.type !== "swype:passkey-
|
|
682
|
-
|
|
683
|
-
window.removeEventListener("message", handler);
|
|
699
|
+
if (data.type !== "swype:passkey-popup-result") return;
|
|
700
|
+
cleanup();
|
|
684
701
|
if (data.error) {
|
|
685
702
|
reject(new Error(data.error));
|
|
686
703
|
} else if (data.result) {
|
|
687
704
|
resolve(data.result);
|
|
688
705
|
} else {
|
|
689
|
-
reject(new Error("Invalid passkey
|
|
706
|
+
reject(new Error("Invalid passkey popup response."));
|
|
690
707
|
}
|
|
691
708
|
};
|
|
692
|
-
|
|
693
|
-
window.parent.postMessage({ type: "swype:passkey-create-request", id, options }, "*");
|
|
694
|
-
});
|
|
695
|
-
}
|
|
696
|
-
function delegatePasskeyGet(options) {
|
|
697
|
-
return new Promise((resolve, reject) => {
|
|
698
|
-
const id = `pg-${++delegationCounter}-${Date.now()}`;
|
|
699
|
-
const timer = setTimeout(() => {
|
|
700
|
-
window.removeEventListener("message", handler);
|
|
701
|
-
reject(new Error("Passkey verification timed out. Please try again."));
|
|
702
|
-
}, DELEGATION_GET_TIMEOUT_MS);
|
|
703
|
-
const handler = (event) => {
|
|
704
|
-
const data = event.data;
|
|
705
|
-
if (!data || typeof data !== "object") return;
|
|
706
|
-
if (data.type !== "swype:passkey-get-response" || data.id !== id) return;
|
|
709
|
+
function cleanup() {
|
|
707
710
|
clearTimeout(timer);
|
|
711
|
+
clearInterval(closedPoll);
|
|
708
712
|
window.removeEventListener("message", handler);
|
|
709
|
-
|
|
710
|
-
reject(new Error(data.error));
|
|
711
|
-
} else if (data.result) {
|
|
712
|
-
resolve(data.result);
|
|
713
|
-
} else {
|
|
714
|
-
reject(new Error("Invalid passkey get response."));
|
|
715
|
-
}
|
|
716
|
-
};
|
|
713
|
+
}
|
|
717
714
|
window.addEventListener("message", handler);
|
|
718
|
-
window.parent.postMessage({ type: "swype:passkey-get-request", id, options }, "*");
|
|
719
715
|
});
|
|
720
716
|
}
|
|
721
717
|
|
|
@@ -840,53 +836,51 @@ async function createPasskeyCredential(params) {
|
|
|
840
836
|
const challenge = new Uint8Array(32);
|
|
841
837
|
crypto.getRandomValues(challenge);
|
|
842
838
|
const rpId = resolvePasskeyRpId();
|
|
839
|
+
const publicKeyOptions = {
|
|
840
|
+
challenge,
|
|
841
|
+
rp: { name: "Swype", id: rpId },
|
|
842
|
+
user: {
|
|
843
|
+
id: new TextEncoder().encode(params.userId),
|
|
844
|
+
name: params.displayName,
|
|
845
|
+
displayName: params.displayName
|
|
846
|
+
},
|
|
847
|
+
pubKeyCredParams: [
|
|
848
|
+
{ alg: -7, type: "public-key" },
|
|
849
|
+
{ alg: -257, type: "public-key" }
|
|
850
|
+
],
|
|
851
|
+
authenticatorSelection: {
|
|
852
|
+
authenticatorAttachment: "platform",
|
|
853
|
+
residentKey: "preferred",
|
|
854
|
+
userVerification: "required"
|
|
855
|
+
},
|
|
856
|
+
timeout: 6e4
|
|
857
|
+
};
|
|
843
858
|
if (isInCrossOriginIframe()) {
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
userVerification: "required"
|
|
859
|
-
},
|
|
860
|
-
timeout: 6e4
|
|
861
|
-
});
|
|
859
|
+
try {
|
|
860
|
+
await waitForDocumentFocus();
|
|
861
|
+
const credential2 = await navigator.credentials.create({
|
|
862
|
+
publicKey: publicKeyOptions
|
|
863
|
+
});
|
|
864
|
+
if (!credential2) {
|
|
865
|
+
throw new Error("Passkey creation was cancelled.");
|
|
866
|
+
}
|
|
867
|
+
return extractPasskeyResult(credential2);
|
|
868
|
+
} catch (err) {
|
|
869
|
+
if (err instanceof PasskeyIframeBlockedError) throw err;
|
|
870
|
+
if (err instanceof Error && err.message === "Passkey creation was cancelled.") throw err;
|
|
871
|
+
throw new PasskeyIframeBlockedError();
|
|
872
|
+
}
|
|
862
873
|
}
|
|
863
874
|
await waitForDocumentFocus();
|
|
864
875
|
const credential = await navigator.credentials.create({
|
|
865
|
-
publicKey:
|
|
866
|
-
challenge,
|
|
867
|
-
rp: { name: "Swype", id: rpId },
|
|
868
|
-
user: {
|
|
869
|
-
id: new TextEncoder().encode(params.userId),
|
|
870
|
-
name: params.displayName,
|
|
871
|
-
displayName: params.displayName
|
|
872
|
-
},
|
|
873
|
-
pubKeyCredParams: [
|
|
874
|
-
{ alg: -7, type: "public-key" },
|
|
875
|
-
// ES256 (P-256)
|
|
876
|
-
{ alg: -257, type: "public-key" }
|
|
877
|
-
// RS256
|
|
878
|
-
],
|
|
879
|
-
authenticatorSelection: {
|
|
880
|
-
authenticatorAttachment: "platform",
|
|
881
|
-
residentKey: "preferred",
|
|
882
|
-
userVerification: "required"
|
|
883
|
-
},
|
|
884
|
-
timeout: 6e4
|
|
885
|
-
}
|
|
876
|
+
publicKey: publicKeyOptions
|
|
886
877
|
});
|
|
887
878
|
if (!credential) {
|
|
888
879
|
throw new Error("Passkey creation was cancelled.");
|
|
889
880
|
}
|
|
881
|
+
return extractPasskeyResult(credential);
|
|
882
|
+
}
|
|
883
|
+
function extractPasskeyResult(credential) {
|
|
890
884
|
const response = credential.response;
|
|
891
885
|
const publicKeyBytes = response.getPublicKey?.();
|
|
892
886
|
return {
|
|
@@ -894,6 +888,29 @@ async function createPasskeyCredential(params) {
|
|
|
894
888
|
publicKey: publicKeyBytes ? toBase64(publicKeyBytes) : ""
|
|
895
889
|
};
|
|
896
890
|
}
|
|
891
|
+
function buildPasskeyPopupOptions(params) {
|
|
892
|
+
const challenge = new Uint8Array(32);
|
|
893
|
+
crypto.getRandomValues(challenge);
|
|
894
|
+
const rpId = resolvePasskeyRpId();
|
|
895
|
+
return {
|
|
896
|
+
challenge: toBase64(challenge),
|
|
897
|
+
rpId,
|
|
898
|
+
rpName: "Swype",
|
|
899
|
+
userId: toBase64(new TextEncoder().encode(params.userId)),
|
|
900
|
+
userName: params.displayName,
|
|
901
|
+
userDisplayName: params.displayName,
|
|
902
|
+
pubKeyCredParams: [
|
|
903
|
+
{ alg: -7, type: "public-key" },
|
|
904
|
+
{ alg: -257, type: "public-key" }
|
|
905
|
+
],
|
|
906
|
+
authenticatorSelection: {
|
|
907
|
+
authenticatorAttachment: "platform",
|
|
908
|
+
residentKey: "preferred",
|
|
909
|
+
userVerification: "required"
|
|
910
|
+
},
|
|
911
|
+
timeout: 6e4
|
|
912
|
+
};
|
|
913
|
+
}
|
|
897
914
|
async function deviceHasPasskey(credentialId) {
|
|
898
915
|
const found = await findDevicePasskey([credentialId]);
|
|
899
916
|
return found != null;
|
|
@@ -903,16 +920,6 @@ async function findDevicePasskey(credentialIds) {
|
|
|
903
920
|
try {
|
|
904
921
|
const challenge = new Uint8Array(32);
|
|
905
922
|
crypto.getRandomValues(challenge);
|
|
906
|
-
if (isInCrossOriginIframe()) {
|
|
907
|
-
const result = await delegatePasskeyGet({
|
|
908
|
-
challenge: toBase64(challenge),
|
|
909
|
-
rpId: resolvePasskeyRpId(),
|
|
910
|
-
allowCredentials: credentialIds.map((id) => ({ type: "public-key", id })),
|
|
911
|
-
userVerification: "discouraged",
|
|
912
|
-
timeout: 3e4
|
|
913
|
-
});
|
|
914
|
-
return result.credentialId;
|
|
915
|
-
}
|
|
916
923
|
await waitForDocumentFocus();
|
|
917
924
|
const assertion = await navigator.credentials.get({
|
|
918
925
|
publicKey: {
|
|
@@ -1411,48 +1418,31 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
|
|
|
1411
1418
|
}
|
|
1412
1419
|
const hashBytes = hexToBytes(payload.userOpHash);
|
|
1413
1420
|
let signedUserOp;
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1421
|
+
const allowCredentials = payload.passkeyCredentialId ? [{
|
|
1422
|
+
type: "public-key",
|
|
1423
|
+
id: base64ToBytes(payload.passkeyCredentialId)
|
|
1424
|
+
}] : void 0;
|
|
1425
|
+
await waitForDocumentFocus();
|
|
1426
|
+
const assertion = await navigator.credentials.get({
|
|
1427
|
+
publicKey: {
|
|
1428
|
+
challenge: hashBytes,
|
|
1417
1429
|
rpId: resolvePasskeyRpId(),
|
|
1418
|
-
allowCredentials
|
|
1430
|
+
allowCredentials,
|
|
1419
1431
|
userVerification: "required",
|
|
1420
1432
|
timeout: 6e4
|
|
1421
|
-
});
|
|
1422
|
-
signedUserOp = {
|
|
1423
|
-
...payload.userOp,
|
|
1424
|
-
credentialId: delegatedResult.credentialId,
|
|
1425
|
-
signature: delegatedResult.signature,
|
|
1426
|
-
authenticatorData: delegatedResult.authenticatorData,
|
|
1427
|
-
clientDataJSON: delegatedResult.clientDataJSON
|
|
1428
|
-
};
|
|
1429
|
-
} else {
|
|
1430
|
-
const allowCredentials = payload.passkeyCredentialId ? [{
|
|
1431
|
-
type: "public-key",
|
|
1432
|
-
id: base64ToBytes(payload.passkeyCredentialId)
|
|
1433
|
-
}] : void 0;
|
|
1434
|
-
await waitForDocumentFocus();
|
|
1435
|
-
const assertion = await navigator.credentials.get({
|
|
1436
|
-
publicKey: {
|
|
1437
|
-
challenge: hashBytes,
|
|
1438
|
-
rpId: resolvePasskeyRpId(),
|
|
1439
|
-
allowCredentials,
|
|
1440
|
-
userVerification: "required",
|
|
1441
|
-
timeout: 6e4
|
|
1442
|
-
}
|
|
1443
|
-
});
|
|
1444
|
-
if (!assertion) {
|
|
1445
|
-
throw new Error("Passkey authentication was cancelled.");
|
|
1446
1433
|
}
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
credentialId: toBase64(assertion.rawId),
|
|
1451
|
-
signature: toBase64(response.signature),
|
|
1452
|
-
authenticatorData: toBase64(response.authenticatorData),
|
|
1453
|
-
clientDataJSON: toBase64(response.clientDataJSON)
|
|
1454
|
-
};
|
|
1434
|
+
});
|
|
1435
|
+
if (!assertion) {
|
|
1436
|
+
throw new Error("Passkey authentication was cancelled.");
|
|
1455
1437
|
}
|
|
1438
|
+
const response = assertion.response;
|
|
1439
|
+
signedUserOp = {
|
|
1440
|
+
...payload.userOp,
|
|
1441
|
+
credentialId: toBase64(assertion.rawId),
|
|
1442
|
+
signature: toBase64(response.signature),
|
|
1443
|
+
authenticatorData: toBase64(response.authenticatorData),
|
|
1444
|
+
clientDataJSON: toBase64(response.clientDataJSON)
|
|
1445
|
+
};
|
|
1456
1446
|
return await signTransfer(
|
|
1457
1447
|
apiBaseUrl,
|
|
1458
1448
|
token ?? "",
|
|
@@ -2658,14 +2648,18 @@ function CreatePasskeyScreen({
|
|
|
2658
2648
|
onCreatePasskey,
|
|
2659
2649
|
onBack,
|
|
2660
2650
|
creating,
|
|
2661
|
-
error
|
|
2651
|
+
error,
|
|
2652
|
+
popupFallback = false,
|
|
2653
|
+
onCreatePasskeyViaPopup
|
|
2662
2654
|
}) {
|
|
2663
2655
|
const { tokens } = useSwypeConfig();
|
|
2656
|
+
const handleCreate = popupFallback && onCreatePasskeyViaPopup ? onCreatePasskeyViaPopup : onCreatePasskey;
|
|
2657
|
+
const buttonLabel = popupFallback ? "Open passkey setup" : "Create passkey";
|
|
2664
2658
|
return /* @__PURE__ */ jsxs(
|
|
2665
2659
|
ScreenLayout,
|
|
2666
2660
|
{
|
|
2667
2661
|
footer: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2668
|
-
/* @__PURE__ */ jsx(PrimaryButton, { onClick:
|
|
2662
|
+
/* @__PURE__ */ jsx(PrimaryButton, { onClick: handleCreate, disabled: creating, loading: creating, children: buttonLabel }),
|
|
2669
2663
|
/* @__PURE__ */ jsx(PoweredByFooter, {})
|
|
2670
2664
|
] }),
|
|
2671
2665
|
children: [
|
|
@@ -2678,7 +2672,7 @@ function CreatePasskeyScreen({
|
|
|
2678
2672
|
/* @__PURE__ */ jsx("path", { d: "M9 14c0 1.5 1.34 2.5 3 2.5s3-1 3-2.5", stroke: tokens.accent, strokeWidth: "1.2", strokeLinecap: "round" })
|
|
2679
2673
|
] }) }),
|
|
2680
2674
|
/* @__PURE__ */ jsx("h2", { style: headingStyle3(tokens.text), children: "Create your passkey" }),
|
|
2681
|
-
/* @__PURE__ */ jsx("p", { style: subtitleStyle3(tokens.textSecondary), children: "Use Face ID to sign in instantly \u2014 no passwords, no codes." }),
|
|
2675
|
+
/* @__PURE__ */ jsx("p", { style: subtitleStyle3(tokens.textSecondary), children: popupFallback ? "Your browser requires a separate window for passkey setup. Tap the button below to continue." : "Use Face ID to sign in instantly \u2014 no passwords, no codes." }),
|
|
2682
2676
|
error && /* @__PURE__ */ jsx("div", { style: errorBannerStyle2(tokens), children: error }),
|
|
2683
2677
|
/* @__PURE__ */ jsx(InfoBanner, { children: "Your passkey is stored securely on your device. Swype never sees your biometric data." })
|
|
2684
2678
|
] })
|
|
@@ -3863,7 +3857,7 @@ function OpenWalletScreen({
|
|
|
3863
3857
|
const logoSrc = walletName ? KNOWN_LOGOS[walletName.toLowerCase()] : void 0;
|
|
3864
3858
|
const handleOpen = useCallback(() => {
|
|
3865
3859
|
const opened = window.open(deeplinkUri, "_blank");
|
|
3866
|
-
if (!opened) {
|
|
3860
|
+
if (!opened && window === window.parent) {
|
|
3867
3861
|
window.location.href = deeplinkUri;
|
|
3868
3862
|
}
|
|
3869
3863
|
}, [deeplinkUri]);
|
|
@@ -4131,6 +4125,7 @@ function SwypePaymentInner({
|
|
|
4131
4125
|
const [transfer, setTransfer] = useState(null);
|
|
4132
4126
|
const [creatingTransfer, setCreatingTransfer] = useState(false);
|
|
4133
4127
|
const [registeringPasskey, setRegisteringPasskey] = useState(false);
|
|
4128
|
+
const [passkeyPopupNeeded, setPasskeyPopupNeeded] = useState(false);
|
|
4134
4129
|
const [activeCredentialId, setActiveCredentialId] = useState(() => {
|
|
4135
4130
|
if (typeof window === "undefined") return null;
|
|
4136
4131
|
return window.localStorage.getItem(ACTIVE_CREDENTIAL_STORAGE_KEY);
|
|
@@ -4633,34 +4628,59 @@ function SwypePaymentInner({
|
|
|
4633
4628
|
merchantAuthorization,
|
|
4634
4629
|
transfer
|
|
4635
4630
|
]);
|
|
4631
|
+
const completePasskeyRegistration = useCallback(async (credentialId, publicKey) => {
|
|
4632
|
+
const token = await getAccessToken();
|
|
4633
|
+
if (!token) throw new Error("Not authenticated");
|
|
4634
|
+
await registerPasskey(apiBaseUrl, token, credentialId, publicKey);
|
|
4635
|
+
setActiveCredentialId(credentialId);
|
|
4636
|
+
window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
|
|
4637
|
+
setPasskeyPopupNeeded(false);
|
|
4638
|
+
const hasActiveWallet = accounts.some(
|
|
4639
|
+
(a) => a.wallets.some((w) => w.status === "ACTIVE")
|
|
4640
|
+
);
|
|
4641
|
+
if (accounts.length === 0 || !hasActiveWallet) {
|
|
4642
|
+
setStep("wallet-picker");
|
|
4643
|
+
} else {
|
|
4644
|
+
setStep("deposit");
|
|
4645
|
+
}
|
|
4646
|
+
}, [getAccessToken, apiBaseUrl, accounts]);
|
|
4636
4647
|
const handleRegisterPasskey = useCallback(async () => {
|
|
4637
4648
|
setRegisteringPasskey(true);
|
|
4638
4649
|
setError(null);
|
|
4639
4650
|
try {
|
|
4640
|
-
const token = await getAccessToken();
|
|
4641
|
-
if (!token) throw new Error("Not authenticated");
|
|
4642
4651
|
const passkeyDisplayName = user?.email?.address ?? user?.google?.name ?? user?.id ?? "Swype User";
|
|
4643
4652
|
const { credentialId, publicKey } = await createPasskeyCredential({
|
|
4644
4653
|
userId: user?.id ?? "unknown",
|
|
4645
4654
|
displayName: passkeyDisplayName
|
|
4646
4655
|
});
|
|
4647
|
-
await
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
(a) => a.wallets.some((w) => w.status === "ACTIVE")
|
|
4652
|
-
);
|
|
4653
|
-
if (accounts.length === 0 || !hasActiveWallet) {
|
|
4654
|
-
setStep("wallet-picker");
|
|
4656
|
+
await completePasskeyRegistration(credentialId, publicKey);
|
|
4657
|
+
} catch (err) {
|
|
4658
|
+
if (err instanceof PasskeyIframeBlockedError) {
|
|
4659
|
+
setPasskeyPopupNeeded(true);
|
|
4655
4660
|
} else {
|
|
4656
|
-
|
|
4661
|
+
setError(err instanceof Error ? err.message : "Failed to register passkey");
|
|
4657
4662
|
}
|
|
4663
|
+
} finally {
|
|
4664
|
+
setRegisteringPasskey(false);
|
|
4665
|
+
}
|
|
4666
|
+
}, [user, completePasskeyRegistration]);
|
|
4667
|
+
const handleCreatePasskeyViaPopup = useCallback(async () => {
|
|
4668
|
+
setRegisteringPasskey(true);
|
|
4669
|
+
setError(null);
|
|
4670
|
+
try {
|
|
4671
|
+
const passkeyDisplayName = user?.email?.address ?? user?.google?.name ?? user?.id ?? "Swype User";
|
|
4672
|
+
const popupOptions = buildPasskeyPopupOptions({
|
|
4673
|
+
userId: user?.id ?? "unknown",
|
|
4674
|
+
displayName: passkeyDisplayName
|
|
4675
|
+
});
|
|
4676
|
+
const { credentialId, publicKey } = await createPasskeyViaPopup(popupOptions);
|
|
4677
|
+
await completePasskeyRegistration(credentialId, publicKey);
|
|
4658
4678
|
} catch (err) {
|
|
4659
4679
|
setError(err instanceof Error ? err.message : "Failed to register passkey");
|
|
4660
4680
|
} finally {
|
|
4661
4681
|
setRegisteringPasskey(false);
|
|
4662
4682
|
}
|
|
4663
|
-
}, [
|
|
4683
|
+
}, [user, completePasskeyRegistration]);
|
|
4664
4684
|
const handleSelectProvider = useCallback((providerId) => {
|
|
4665
4685
|
setSelectedProviderId(providerId);
|
|
4666
4686
|
setSelectedAccountId(null);
|
|
@@ -4778,7 +4798,9 @@ function SwypePaymentInner({
|
|
|
4778
4798
|
onCreatePasskey: handleRegisterPasskey,
|
|
4779
4799
|
onBack: handleLogout,
|
|
4780
4800
|
creating: registeringPasskey,
|
|
4781
|
-
error
|
|
4801
|
+
error,
|
|
4802
|
+
popupFallback: passkeyPopupNeeded,
|
|
4803
|
+
onCreatePasskeyViaPopup: handleCreatePasskeyViaPopup
|
|
4782
4804
|
}
|
|
4783
4805
|
);
|
|
4784
4806
|
}
|
|
@@ -4911,6 +4933,6 @@ function SwypePaymentInner({
|
|
|
4911
4933
|
return null;
|
|
4912
4934
|
}
|
|
4913
4935
|
|
|
4914
|
-
export { IconCircle, OutlineButton, PoweredByFooter, PrimaryButton, ScreenHeader, ScreenLayout, SelectSourceScreen, SettingsMenu, SetupScreen, Spinner, StepList, SwypePayment, SwypeProvider, createPasskeyCredential, darkTheme, deviceHasPasskey, findDevicePasskey, getTheme, lightTheme, api_exports as swypeApi, useAuthorizationExecutor, useSwypeConfig, useSwypeDepositAmount, useTransferPolling, useTransferSigning };
|
|
4936
|
+
export { IconCircle, OutlineButton, PasskeyIframeBlockedError, PoweredByFooter, PrimaryButton, ScreenHeader, ScreenLayout, SelectSourceScreen, SettingsMenu, SetupScreen, Spinner, StepList, SwypePayment, SwypeProvider, buildPasskeyPopupOptions, createPasskeyCredential, createPasskeyViaPopup, darkTheme, deviceHasPasskey, findDevicePasskey, getTheme, lightTheme, api_exports as swypeApi, useAuthorizationExecutor, useSwypeConfig, useSwypeDepositAmount, useTransferPolling, useTransferSigning };
|
|
4915
4937
|
//# sourceMappingURL=index.js.map
|
|
4916
4938
|
//# sourceMappingURL=index.js.map
|