@tatchi-xyz/sdk 0.17.0 → 0.19.0
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/cjs/core/EmailRecovery/emailRecoveryPendingStore.js +69 -0
- package/dist/cjs/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
- package/dist/cjs/core/EmailRecovery/index.js +32 -20
- package/dist/cjs/core/EmailRecovery/index.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/emailRecovery.js +507 -452
- package/dist/cjs/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/index.js +1 -0
- package/dist/cjs/core/TatchiPasskey/index.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/relay.js +23 -1
- package/dist/cjs/core/TatchiPasskey/relay.js.map +1 -1
- package/dist/cjs/core/WalletIframe/client/IframeTransport.js +0 -7
- package/dist/cjs/core/WalletIframe/client/IframeTransport.js.map +1 -1
- package/dist/cjs/core/WalletIframe/client/router.js +6 -2
- package/dist/cjs/core/WalletIframe/client/router.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js +1 -1
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js +1 -1
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/index.js +23 -0
- package/dist/cjs/core/WebAuthnManager/index.js.map +1 -1
- package/dist/cjs/core/rpcCalls.js +8 -0
- package/dist/cjs/core/rpcCalls.js.map +1 -1
- package/dist/cjs/index.js +6 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{LinkedDevicesModal-B6api181.css → LinkedDevicesModal-CSSowiHP.css} +1 -1
- package/dist/{esm/react/components/AccountMenuButton/LinkedDevicesModal-B6api181.css.map → cjs/react/components/AccountMenuButton/LinkedDevicesModal-CSSowiHP.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{ProfileDropdown-B-DrG_u5.css → ProfileDropdown-CEPMZ1gY.css} +1 -1
- package/dist/{esm/react/components/AccountMenuButton/ProfileDropdown-B-DrG_u5.css.map → cjs/react/components/AccountMenuButton/ProfileDropdown-CEPMZ1gY.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css → Web3AuthProfileButton-DopOg7Xc.css} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css.map → Web3AuthProfileButton-DopOg7Xc.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css → TouchIcon-BQWentvJ.css} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css.map → TouchIcon-BQWentvJ.css.map} +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css → PasskeyAuthMenu-DwrzWMYx.css} +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css.map → PasskeyAuthMenu-DwrzWMYx.css.map} +1 -1
- package/dist/cjs/react/components/{ShowQRCode-nZhZSaba.css → ShowQRCode-CCN4h6Uv.css} +1 -1
- package/dist/cjs/react/components/{ShowQRCode-nZhZSaba.css.map → ShowQRCode-CCN4h6Uv.css.map} +1 -1
- package/dist/cjs/react/hooks/usePreconnectWalletAssets.js +27 -32
- package/dist/cjs/react/hooks/usePreconnectWalletAssets.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js +69 -0
- package/dist/cjs/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
- package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js +32 -20
- package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js +507 -452
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js +1 -0
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/relay.js +23 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/relay.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/IframeTransport.js +0 -7
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/IframeTransport.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js +6 -2
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js +1 -1
- package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js +1 -1
- package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WebAuthnManager/index.js +23 -0
- package/dist/cjs/react/sdk/src/core/WebAuthnManager/index.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/rpcCalls.js +8 -0
- package/dist/cjs/react/sdk/src/core/rpcCalls.js.map +1 -1
- package/dist/esm/core/EmailRecovery/emailRecoveryPendingStore.js +63 -0
- package/dist/esm/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
- package/dist/esm/core/EmailRecovery/index.js +28 -21
- package/dist/esm/core/EmailRecovery/index.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/emailRecovery.js +507 -452
- package/dist/esm/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/index.js +2 -1
- package/dist/esm/core/TatchiPasskey/index.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/relay.js +23 -1
- package/dist/esm/core/TatchiPasskey/relay.js.map +1 -1
- package/dist/esm/core/WalletIframe/client/IframeTransport.js +0 -7
- package/dist/esm/core/WalletIframe/client/IframeTransport.js.map +1 -1
- package/dist/esm/core/WalletIframe/client/router.js +7 -3
- package/dist/esm/core/WalletIframe/client/router.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js +1 -1
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js +1 -1
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/index.js +23 -0
- package/dist/esm/core/WebAuthnManager/index.js.map +1 -1
- package/dist/esm/core/rpcCalls.js +8 -1
- package/dist/esm/core/rpcCalls.js.map +1 -1
- package/dist/esm/index.js +4 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/components/AccountMenuButton/{LinkedDevicesModal-B6api181.css → LinkedDevicesModal-CSSowiHP.css} +1 -1
- package/dist/{cjs/react/components/AccountMenuButton/LinkedDevicesModal-B6api181.css.map → esm/react/components/AccountMenuButton/LinkedDevicesModal-CSSowiHP.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{ProfileDropdown-B-DrG_u5.css → ProfileDropdown-CEPMZ1gY.css} +1 -1
- package/dist/{cjs/react/components/AccountMenuButton/ProfileDropdown-B-DrG_u5.css.map → esm/react/components/AccountMenuButton/ProfileDropdown-CEPMZ1gY.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css → Web3AuthProfileButton-DopOg7Xc.css} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css.map → Web3AuthProfileButton-DopOg7Xc.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css → TouchIcon-BQWentvJ.css} +1 -1
- package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css.map → TouchIcon-BQWentvJ.css.map} +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css → PasskeyAuthMenu-DwrzWMYx.css} +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css.map → PasskeyAuthMenu-DwrzWMYx.css.map} +1 -1
- package/dist/esm/react/components/{ShowQRCode-nZhZSaba.css → ShowQRCode-CCN4h6Uv.css} +1 -1
- package/dist/esm/react/components/{ShowQRCode-nZhZSaba.css.map → ShowQRCode-CCN4h6Uv.css.map} +1 -1
- package/dist/esm/react/hooks/usePreconnectWalletAssets.js +27 -32
- package/dist/esm/react/hooks/usePreconnectWalletAssets.js.map +1 -1
- package/dist/esm/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js +63 -0
- package/dist/esm/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
- package/dist/esm/react/sdk/src/core/EmailRecovery/index.js +28 -21
- package/dist/esm/react/sdk/src/core/EmailRecovery/index.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js +507 -452
- package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/index.js +2 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/relay.js +23 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/relay.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WalletIframe/client/IframeTransport.js +0 -7
- package/dist/esm/react/sdk/src/core/WalletIframe/client/IframeTransport.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js +7 -3
- package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js +1 -1
- package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js +1 -1
- package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WebAuthnManager/index.js +23 -0
- package/dist/esm/react/sdk/src/core/WebAuthnManager/index.js.map +1 -1
- package/dist/esm/react/sdk/src/core/rpcCalls.js +8 -1
- package/dist/esm/react/sdk/src/core/rpcCalls.js.map +1 -1
- package/dist/esm/sdk/{createAdapters-qVGD6i0g.js → createAdapters-DIRR8_Z9.js} +1 -1
- package/dist/esm/sdk/{createAdapters-BumKM2ft.js → createAdapters-Yga6W0en.js} +2 -2
- package/dist/esm/sdk/{createAdapters-BumKM2ft.js.map → createAdapters-Yga6W0en.js.map} +1 -1
- package/dist/esm/sdk/{localOnly-pXMTqh1m.js → localOnly-BHScJasw.js} +2 -2
- package/dist/esm/sdk/{localOnly-Byi3AK7A.js → localOnly-VevCI7H0.js} +3 -3
- package/dist/esm/sdk/{localOnly-Byi3AK7A.js.map → localOnly-VevCI7H0.js.map} +1 -1
- package/dist/esm/sdk/offline-export-app.js +29 -6
- package/dist/esm/sdk/offline-export-app.js.map +1 -1
- package/dist/esm/sdk/{registration-CBiS4Ua_.js → registration-bKEg9Zr2.js} +2 -2
- package/dist/esm/sdk/{registration-CBiS4Ua_.js.map → registration-bKEg9Zr2.js.map} +1 -1
- package/dist/esm/sdk/{registration-DLPLsGCz.js → registration-lDD60Ytt.js} +1 -1
- package/dist/esm/sdk/{router-BLFegW7J.js → router-DuGYOd3G.js} +6 -9
- package/dist/esm/sdk/{rpcCalls-DEv9x5-f.js → rpcCalls-BQrJMTdg.js} +2 -2
- package/dist/esm/sdk/{rpcCalls-OhgEeFig.js → rpcCalls-YVeUVMk2.js} +8 -1
- package/dist/esm/sdk/{transactions-Bk-VavcV.js → transactions-BalIhtJ9.js} +1 -1
- package/dist/esm/sdk/{transactions-BIqKZeR0.js → transactions-bqaAwL4k.js} +2 -2
- package/dist/esm/sdk/{transactions-BIqKZeR0.js.map → transactions-bqaAwL4k.js.map} +1 -1
- package/dist/esm/sdk/wallet-iframe-host.js +641 -481
- package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker_bg.wasm +0 -0
- package/dist/types/src/core/EmailRecovery/emailRecoveryPendingStore.d.ts +25 -0
- package/dist/types/src/core/EmailRecovery/emailRecoveryPendingStore.d.ts.map +1 -0
- package/dist/types/src/core/EmailRecovery/index.d.ts +1 -0
- package/dist/types/src/core/EmailRecovery/index.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts +38 -6
- package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/index.d.ts +2 -2
- package/dist/types/src/core/TatchiPasskey/index.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/relay.d.ts +2 -1
- package/dist/types/src/core/TatchiPasskey/relay.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/client/IframeTransport.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/client/router.d.ts +3 -3
- package/dist/types/src/core/WalletIframe/client/router.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/index.d.ts +7 -0
- package/dist/types/src/core/WebAuthnManager/index.d.ts.map +1 -1
- package/dist/types/src/core/rpcCalls.d.ts +9 -0
- package/dist/types/src/core/rpcCalls.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/react/hooks/usePreconnectWalletAssets.d.ts.map +1 -1
- package/dist/workers/wasm_vrf_worker_bg.wasm +0 -0
- package/package.json +1 -1
|
@@ -15,7 +15,7 @@ import { MinimalNearClient, SignedTransaction, isOffline, openOfflineExport } fr
|
|
|
15
15
|
import { AccountRecoveryPhase, AccountRecoveryStatus, ActionPhase, ActionStatus, DEFAULT_WAIT_STATUS, DeviceLinkingPhase, DeviceLinkingStatus, EmailRecoveryPhase, EmailRecoveryStatus, LoginPhase, LoginStatus, RegistrationPhase, RegistrationStatus, init_rpc, init_sdkSentEvents } from "./sdkSentEvents-_jrJLIhw.js";
|
|
16
16
|
import { SecureConfirmMessageType, SecureConfirmationType, TouchIdPrompt, authenticatorsToAllowCredentials, createRandomVRFChallenge, getIntentDigest, init_accountIds, init_touchIdPrompt, init_vrf_worker, parseTransactionSummary, sanitizeForPostMessage, sendConfirmResponse, toAccountId, validateVRFChallenge } from "./requestHelpers-Dh1hEYL9.js";
|
|
17
17
|
import { PASSKEY_MANAGER_DEFAULT_CONFIGS, buildConfigsFromEnv, init_defaultConfigs } from "./defaultConfigs-DpslkAQd.js";
|
|
18
|
-
import { buildSetRecoveryEmailsActions, checkCanRegisterUserContractCall, executeDeviceLinkingContractCalls, getCredentialIdsContractCall, getDeviceLinkingAccountContractCall, getRecoveryEmailHashesContractCall, init_rpcCalls, syncAuthenticatorsContractCall, verifyAuthenticationResponse } from "./rpcCalls-
|
|
18
|
+
import { buildSetRecoveryEmailsActions, checkCanRegisterUserContractCall, executeDeviceLinkingContractCalls, getCredentialIdsContractCall, getDeviceLinkingAccountContractCall, getRecoveryEmailHashesContractCall, init_rpcCalls, syncAuthenticatorsContractCall, verifyAuthenticationResponse } from "./rpcCalls-YVeUVMk2.js";
|
|
19
19
|
import { getLastLoggedInDeviceNumber, init_getDeviceNumber, parseDeviceNumber } from "./getDeviceNumber-zsOHT_Um.js";
|
|
20
20
|
|
|
21
21
|
//#region src/core/sdkPaths/workers.ts
|
|
@@ -3271,42 +3271,42 @@ async function importFlow(label, loader) {
|
|
|
3271
3271
|
}
|
|
3272
3272
|
const HANDLERS = {
|
|
3273
3273
|
[SecureConfirmationType.DECRYPT_PRIVATE_KEY_WITH_PRF]: async ({ ctx, request, worker, confirmationConfig, transactionSummary }) => {
|
|
3274
|
-
const { handleLocalOnlyFlow } = await importFlow("localOnly", () => import("./localOnly-
|
|
3274
|
+
const { handleLocalOnlyFlow } = await importFlow("localOnly", () => import("./localOnly-BHScJasw.js"));
|
|
3275
3275
|
await handleLocalOnlyFlow(ctx, request, worker, {
|
|
3276
3276
|
confirmationConfig,
|
|
3277
3277
|
transactionSummary
|
|
3278
3278
|
});
|
|
3279
3279
|
},
|
|
3280
3280
|
[SecureConfirmationType.SHOW_SECURE_PRIVATE_KEY_UI]: async ({ ctx, request, worker, confirmationConfig, transactionSummary }) => {
|
|
3281
|
-
const { handleLocalOnlyFlow } = await importFlow("localOnly", () => import("./localOnly-
|
|
3281
|
+
const { handleLocalOnlyFlow } = await importFlow("localOnly", () => import("./localOnly-BHScJasw.js"));
|
|
3282
3282
|
await handleLocalOnlyFlow(ctx, request, worker, {
|
|
3283
3283
|
confirmationConfig,
|
|
3284
3284
|
transactionSummary
|
|
3285
3285
|
});
|
|
3286
3286
|
},
|
|
3287
3287
|
[SecureConfirmationType.REGISTER_ACCOUNT]: async ({ ctx, request, worker, confirmationConfig, transactionSummary }) => {
|
|
3288
|
-
const { handleRegistrationFlow } = await importFlow("registration", () => import("./registration-
|
|
3288
|
+
const { handleRegistrationFlow } = await importFlow("registration", () => import("./registration-lDD60Ytt.js"));
|
|
3289
3289
|
await handleRegistrationFlow(ctx, request, worker, {
|
|
3290
3290
|
confirmationConfig,
|
|
3291
3291
|
transactionSummary
|
|
3292
3292
|
});
|
|
3293
3293
|
},
|
|
3294
3294
|
[SecureConfirmationType.LINK_DEVICE]: async ({ ctx, request, worker, confirmationConfig, transactionSummary }) => {
|
|
3295
|
-
const { handleRegistrationFlow } = await importFlow("registration", () => import("./registration-
|
|
3295
|
+
const { handleRegistrationFlow } = await importFlow("registration", () => import("./registration-lDD60Ytt.js"));
|
|
3296
3296
|
await handleRegistrationFlow(ctx, request, worker, {
|
|
3297
3297
|
confirmationConfig,
|
|
3298
3298
|
transactionSummary
|
|
3299
3299
|
});
|
|
3300
3300
|
},
|
|
3301
3301
|
[SecureConfirmationType.SIGN_TRANSACTION]: async ({ ctx, request, worker, confirmationConfig, transactionSummary }) => {
|
|
3302
|
-
const { handleTransactionSigningFlow } = await importFlow("transactions", () => import("./transactions-
|
|
3302
|
+
const { handleTransactionSigningFlow } = await importFlow("transactions", () => import("./transactions-BalIhtJ9.js"));
|
|
3303
3303
|
await handleTransactionSigningFlow(ctx, request, worker, {
|
|
3304
3304
|
confirmationConfig,
|
|
3305
3305
|
transactionSummary
|
|
3306
3306
|
});
|
|
3307
3307
|
},
|
|
3308
3308
|
[SecureConfirmationType.SIGN_NEP413_MESSAGE]: async ({ ctx, request, worker, confirmationConfig, transactionSummary }) => {
|
|
3309
|
-
const { handleTransactionSigningFlow } = await importFlow("transactions", () => import("./transactions-
|
|
3309
|
+
const { handleTransactionSigningFlow } = await importFlow("transactions", () => import("./transactions-BalIhtJ9.js"));
|
|
3310
3310
|
await handleTransactionSigningFlow(ctx, request, worker, {
|
|
3311
3311
|
confirmationConfig,
|
|
3312
3312
|
transactionSummary
|
|
@@ -6385,6 +6385,29 @@ var WebAuthnManager = class {
|
|
|
6385
6385
|
async extractCosePublicKey(attestationObjectBase64url) {
|
|
6386
6386
|
return await this.signerWorkerManager.extractCosePublicKey(attestationObjectBase64url);
|
|
6387
6387
|
}
|
|
6388
|
+
/**
|
|
6389
|
+
* Helper to ensure the local `lastUser` pointer is consistent with the latest DB updates.
|
|
6390
|
+
* This fixes issues where a recovery or registration might have updated the user record
|
|
6391
|
+
* but failed to update the `lastUser` pointer (e.g. due to previous bugs or interruptions),
|
|
6392
|
+
* preventing the strict `ensureCurrentPasskey` check from passing.
|
|
6393
|
+
*/
|
|
6394
|
+
async autoHealLastUserPointer(nearAccountId) {
|
|
6395
|
+
try {
|
|
6396
|
+
const lastUser = await this.getLastUser();
|
|
6397
|
+
if (lastUser && lastUser.nearAccountId === nearAccountId) {
|
|
6398
|
+
const freshest = await IndexedDBManager.clientDB.getLastDBUpdatedUser(nearAccountId);
|
|
6399
|
+
if (freshest && freshest.deviceNumber !== lastUser.deviceNumber) {
|
|
6400
|
+
console.log(`[WebAuthnManager] Auto-healing lastUser pointer from device ${lastUser.deviceNumber} to ${freshest.deviceNumber}`);
|
|
6401
|
+
await this.setLastUser(nearAccountId, freshest.deviceNumber);
|
|
6402
|
+
}
|
|
6403
|
+
} else {
|
|
6404
|
+
const freshest = await IndexedDBManager.clientDB.getLastDBUpdatedUser(nearAccountId);
|
|
6405
|
+
if (freshest) await this.setLastUser(nearAccountId, freshest.deviceNumber);
|
|
6406
|
+
}
|
|
6407
|
+
} catch (e) {
|
|
6408
|
+
console.warn("[WebAuthnManager] Auto-heal pointer failed (non-fatal):", e);
|
|
6409
|
+
}
|
|
6410
|
+
}
|
|
6388
6411
|
/** Worker-driven export: two-phase V2 (collect PRF → decrypt → show UI) */
|
|
6389
6412
|
async exportNearKeypairWithUIWorkerDriven(nearAccountId, options) {
|
|
6390
6413
|
await this.withSigningSession({
|
|
@@ -9845,8 +9868,30 @@ async function signDelegateAction(args) {
|
|
|
9845
9868
|
//#endregion
|
|
9846
9869
|
//#region src/core/TatchiPasskey/relay.ts
|
|
9847
9870
|
init_sdkSentEvents();
|
|
9871
|
+
const toNumberArray = (value) => Array.isArray(value) ? value : Array.from(value);
|
|
9872
|
+
const normalizeSignedDelegateForRelay = (signedDelegate) => {
|
|
9873
|
+
const delegateAction = signedDelegate.delegateAction;
|
|
9874
|
+
const signature = signedDelegate.signature;
|
|
9875
|
+
return {
|
|
9876
|
+
delegateAction: {
|
|
9877
|
+
...delegateAction,
|
|
9878
|
+
publicKey: {
|
|
9879
|
+
...delegateAction.publicKey,
|
|
9880
|
+
keyData: toNumberArray(delegateAction.publicKey.keyData)
|
|
9881
|
+
}
|
|
9882
|
+
},
|
|
9883
|
+
signature: {
|
|
9884
|
+
...signature,
|
|
9885
|
+
signatureData: toNumberArray(signature.signatureData)
|
|
9886
|
+
}
|
|
9887
|
+
};
|
|
9888
|
+
};
|
|
9848
9889
|
async function sendDelegateActionViaRelayer(args) {
|
|
9849
9890
|
const { url, payload, signal, options } = args;
|
|
9891
|
+
const normalizedPayload = {
|
|
9892
|
+
...payload,
|
|
9893
|
+
signedDelegate: normalizeSignedDelegateForRelay(payload.signedDelegate)
|
|
9894
|
+
};
|
|
9850
9895
|
const emit = (event) => options?.onEvent?.(event);
|
|
9851
9896
|
const emitError = (message) => {
|
|
9852
9897
|
emit({
|
|
@@ -9868,7 +9913,7 @@ async function sendDelegateActionViaRelayer(args) {
|
|
|
9868
9913
|
res = await fetch(url, {
|
|
9869
9914
|
method: "POST",
|
|
9870
9915
|
headers: { "content-type": "application/json" },
|
|
9871
|
-
body: JSON.stringify(
|
|
9916
|
+
body: JSON.stringify(normalizedPayload),
|
|
9872
9917
|
signal
|
|
9873
9918
|
});
|
|
9874
9919
|
} catch (err$1) {
|
|
@@ -9933,28 +9978,65 @@ const OFFLINE_EXPORT_FALLBACK = "OFFLINE_EXPORT_FALLBACK";
|
|
|
9933
9978
|
const WALLET_UI_CLOSED = "WALLET_UI_CLOSED";
|
|
9934
9979
|
const EXPORT_NEAR_KEYPAIR_CANCELLED = "EXPORT_NEAR_KEYPAIR_CANCELLED";
|
|
9935
9980
|
|
|
9981
|
+
//#endregion
|
|
9982
|
+
//#region src/core/EmailRecovery/emailRecoveryPendingStore.ts
|
|
9983
|
+
var EmailRecoveryPendingStore;
|
|
9984
|
+
var init_emailRecoveryPendingStore = __esm({ "src/core/EmailRecovery/emailRecoveryPendingStore.ts": (() => {
|
|
9985
|
+
init_IndexedDBManager();
|
|
9986
|
+
EmailRecoveryPendingStore = class {
|
|
9987
|
+
getPendingTtlMs;
|
|
9988
|
+
now;
|
|
9989
|
+
constructor(options) {
|
|
9990
|
+
this.getPendingTtlMs = options.getPendingTtlMs;
|
|
9991
|
+
this.now = options.now ?? Date.now;
|
|
9992
|
+
}
|
|
9993
|
+
getPendingIndexKey(accountId) {
|
|
9994
|
+
return `pendingEmailRecovery:${accountId}`;
|
|
9995
|
+
}
|
|
9996
|
+
getPendingRecordKey(accountId, nearPublicKey) {
|
|
9997
|
+
return `${this.getPendingIndexKey(accountId)}:${nearPublicKey}`;
|
|
9998
|
+
}
|
|
9999
|
+
async get(accountId, nearPublicKey) {
|
|
10000
|
+
const pendingTtlMs = this.getPendingTtlMs();
|
|
10001
|
+
const indexKey = this.getPendingIndexKey(accountId);
|
|
10002
|
+
const indexedNearPublicKey = await IndexedDBManager.clientDB.getAppState(indexKey);
|
|
10003
|
+
const resolvedNearPublicKey = nearPublicKey ?? indexedNearPublicKey;
|
|
10004
|
+
if (!resolvedNearPublicKey) return null;
|
|
10005
|
+
const recordKey = this.getPendingRecordKey(accountId, resolvedNearPublicKey);
|
|
10006
|
+
const record = await IndexedDBManager.clientDB.getAppState(recordKey);
|
|
10007
|
+
const shouldClearIndex = indexedNearPublicKey === resolvedNearPublicKey;
|
|
10008
|
+
if (!record) {
|
|
10009
|
+
if (shouldClearIndex) await IndexedDBManager.clientDB.setAppState(indexKey, void 0).catch(() => {});
|
|
10010
|
+
return null;
|
|
10011
|
+
}
|
|
10012
|
+
if (this.now() - record.createdAt > pendingTtlMs) {
|
|
10013
|
+
await IndexedDBManager.clientDB.setAppState(recordKey, void 0).catch(() => {});
|
|
10014
|
+
if (shouldClearIndex) await IndexedDBManager.clientDB.setAppState(indexKey, void 0).catch(() => {});
|
|
10015
|
+
return null;
|
|
10016
|
+
}
|
|
10017
|
+
await this.touchIndex(accountId, record.nearPublicKey);
|
|
10018
|
+
return record;
|
|
10019
|
+
}
|
|
10020
|
+
async set(record) {
|
|
10021
|
+
const key = this.getPendingRecordKey(record.accountId, record.nearPublicKey);
|
|
10022
|
+
await IndexedDBManager.clientDB.setAppState(key, record);
|
|
10023
|
+
await this.touchIndex(record.accountId, record.nearPublicKey);
|
|
10024
|
+
}
|
|
10025
|
+
async clear(accountId, nearPublicKey) {
|
|
10026
|
+
const indexKey = this.getPendingIndexKey(accountId);
|
|
10027
|
+
const idx = await IndexedDBManager.clientDB.getAppState(indexKey).catch(() => void 0);
|
|
10028
|
+
const resolvedNearPublicKey = nearPublicKey || idx || "";
|
|
10029
|
+
if (resolvedNearPublicKey) await IndexedDBManager.clientDB.setAppState(this.getPendingRecordKey(accountId, resolvedNearPublicKey), void 0).catch(() => {});
|
|
10030
|
+
if (!nearPublicKey || idx === nearPublicKey) await IndexedDBManager.clientDB.setAppState(indexKey, void 0).catch(() => {});
|
|
10031
|
+
}
|
|
10032
|
+
async touchIndex(accountId, nearPublicKey) {
|
|
10033
|
+
await IndexedDBManager.clientDB.setAppState(this.getPendingIndexKey(accountId), nearPublicKey).catch(() => {});
|
|
10034
|
+
}
|
|
10035
|
+
};
|
|
10036
|
+
}) });
|
|
10037
|
+
|
|
9936
10038
|
//#endregion
|
|
9937
10039
|
//#region src/core/EmailRecovery/index.ts
|
|
9938
|
-
init_accountIds();
|
|
9939
|
-
init_IndexedDBManager();
|
|
9940
|
-
const canonicalizeEmail = (email) => {
|
|
9941
|
-
const raw = String(email || "").trim();
|
|
9942
|
-
if (!raw) return "";
|
|
9943
|
-
const withoutHeaderName = raw.replace(/^[a-z0-9-]+\s*:\s*/i, "").trim();
|
|
9944
|
-
const emailRegex = /([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*)/;
|
|
9945
|
-
const angleMatch = withoutHeaderName.match(/<([^>]+)>/);
|
|
9946
|
-
const candidates = [angleMatch?.[1], withoutHeaderName].filter((v) => typeof v === "string" && v.length > 0);
|
|
9947
|
-
for (const candidate of candidates) {
|
|
9948
|
-
const cleaned = candidate.replace(/^mailto:\s*/i, "");
|
|
9949
|
-
const match = cleaned.match(emailRegex);
|
|
9950
|
-
if (match?.[1]) return match[1].trim().toLowerCase();
|
|
9951
|
-
}
|
|
9952
|
-
return withoutHeaderName.toLowerCase();
|
|
9953
|
-
};
|
|
9954
|
-
const bytesToHex = (bytes) => {
|
|
9955
|
-
const arr = bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes);
|
|
9956
|
-
return `0x${Array.from(arr).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
9957
|
-
};
|
|
9958
10040
|
async function hashRecoveryEmails(emails, accountId) {
|
|
9959
10041
|
const encoder = new TextEncoder();
|
|
9960
10042
|
const salt = (accountId || "").trim().toLowerCase();
|
|
@@ -10001,6 +10083,30 @@ async function prepareRecoveryEmails(nearAccountId, recoveryEmails) {
|
|
|
10001
10083
|
async function getLocalRecoveryEmails(nearAccountId) {
|
|
10002
10084
|
return IndexedDBManager.getRecoveryEmails(nearAccountId);
|
|
10003
10085
|
}
|
|
10086
|
+
var canonicalizeEmail, bytesToHex;
|
|
10087
|
+
var init_EmailRecovery = __esm({ "src/core/EmailRecovery/index.ts": (() => {
|
|
10088
|
+
init_accountIds();
|
|
10089
|
+
init_IndexedDBManager();
|
|
10090
|
+
init_emailRecoveryPendingStore();
|
|
10091
|
+
canonicalizeEmail = (email) => {
|
|
10092
|
+
const raw = String(email || "").trim();
|
|
10093
|
+
if (!raw) return "";
|
|
10094
|
+
const withoutHeaderName = raw.replace(/^[a-z0-9-]+\s*:\s*/i, "").trim();
|
|
10095
|
+
const emailRegex = /([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*)/;
|
|
10096
|
+
const angleMatch = withoutHeaderName.match(/<([^>]+)>/);
|
|
10097
|
+
const candidates = [angleMatch?.[1], withoutHeaderName].filter((v) => typeof v === "string" && v.length > 0);
|
|
10098
|
+
for (const candidate of candidates) {
|
|
10099
|
+
const cleaned = candidate.replace(/^mailto:\s*/i, "");
|
|
10100
|
+
const match = cleaned.match(emailRegex);
|
|
10101
|
+
if (match?.[1]) return match[1].trim().toLowerCase();
|
|
10102
|
+
}
|
|
10103
|
+
return withoutHeaderName.toLowerCase();
|
|
10104
|
+
};
|
|
10105
|
+
bytesToHex = (bytes) => {
|
|
10106
|
+
const arr = bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes);
|
|
10107
|
+
return `0x${Array.from(arr).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
10108
|
+
};
|
|
10109
|
+
}) });
|
|
10004
10110
|
|
|
10005
10111
|
//#endregion
|
|
10006
10112
|
//#region src/core/TatchiPasskey/emailRecovery.ts
|
|
@@ -10047,9 +10153,11 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10047
10153
|
init_rpc();
|
|
10048
10154
|
init_getDeviceNumber();
|
|
10049
10155
|
init_login();
|
|
10156
|
+
init_EmailRecovery();
|
|
10050
10157
|
EmailRecoveryFlow = class {
|
|
10051
10158
|
context;
|
|
10052
10159
|
options;
|
|
10160
|
+
pendingStore;
|
|
10053
10161
|
pending = null;
|
|
10054
10162
|
phase = EmailRecoveryPhase.STEP_1_PREPARATION;
|
|
10055
10163
|
pollingTimer;
|
|
@@ -10060,6 +10168,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10060
10168
|
constructor(context, options) {
|
|
10061
10169
|
this.context = context;
|
|
10062
10170
|
this.options = options;
|
|
10171
|
+
this.pendingStore = options?.pendingStore ?? new EmailRecoveryPendingStore({ getPendingTtlMs: () => this.getConfig().pendingTtlMs });
|
|
10063
10172
|
}
|
|
10064
10173
|
setOptions(options) {
|
|
10065
10174
|
if (!options) return;
|
|
@@ -10067,6 +10176,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10067
10176
|
...this.options || {},
|
|
10068
10177
|
...options
|
|
10069
10178
|
};
|
|
10179
|
+
if (options.pendingStore) this.pendingStore = options.pendingStore;
|
|
10070
10180
|
}
|
|
10071
10181
|
emit(event) {
|
|
10072
10182
|
this.options?.onEvent?.(event);
|
|
@@ -10085,24 +10195,139 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10085
10195
|
this.options?.onError?.(err$1);
|
|
10086
10196
|
return err$1;
|
|
10087
10197
|
}
|
|
10198
|
+
async fail(step, message) {
|
|
10199
|
+
const err$1 = this.emitError(step, message);
|
|
10200
|
+
await this.options?.afterCall?.(false);
|
|
10201
|
+
throw err$1;
|
|
10202
|
+
}
|
|
10203
|
+
async assertValidAccountIdOrFail(step, accountId) {
|
|
10204
|
+
const validation = validateNearAccountId(accountId);
|
|
10205
|
+
if (!validation.valid) await this.fail(step, `Invalid NEAR account ID: ${validation.error}`);
|
|
10206
|
+
return toAccountId(accountId);
|
|
10207
|
+
}
|
|
10208
|
+
async resolvePendingOrFail(step, args, options) {
|
|
10209
|
+
const { allowErrorStatus = true, missingMessage = "No pending email recovery record found for this account", errorStatusMessage = "Pending email recovery is in an error state; please restart the flow" } = options ?? {};
|
|
10210
|
+
let rec = this.pending;
|
|
10211
|
+
if (!rec || rec.accountId !== args.accountId || args.nearPublicKey && rec.nearPublicKey !== args.nearPublicKey) {
|
|
10212
|
+
rec = await this.loadPending(args.accountId, args.nearPublicKey);
|
|
10213
|
+
this.pending = rec;
|
|
10214
|
+
}
|
|
10215
|
+
if (!rec) await this.fail(step, missingMessage);
|
|
10216
|
+
const resolved = rec;
|
|
10217
|
+
if (!allowErrorStatus && resolved.status === "error") await this.fail(step, errorStatusMessage);
|
|
10218
|
+
return resolved;
|
|
10219
|
+
}
|
|
10088
10220
|
getConfig() {
|
|
10089
10221
|
return getEmailRecoveryConfig(this.context.configs);
|
|
10090
10222
|
}
|
|
10091
|
-
|
|
10092
|
-
return
|
|
10223
|
+
toBigInt(value) {
|
|
10224
|
+
if (typeof value === "bigint") return value;
|
|
10225
|
+
if (typeof value === "number") return BigInt(value);
|
|
10226
|
+
if (typeof value === "string" && value.length > 0) return BigInt(value);
|
|
10227
|
+
return BigInt(0);
|
|
10093
10228
|
}
|
|
10094
|
-
|
|
10095
|
-
|
|
10229
|
+
computeAvailableBalance(accountView) {
|
|
10230
|
+
const STORAGE_PRICE_PER_BYTE = BigInt("10000000000000000000");
|
|
10231
|
+
const amount = this.toBigInt(accountView.amount);
|
|
10232
|
+
const locked = this.toBigInt(accountView.locked);
|
|
10233
|
+
const storageUsage = this.toBigInt(accountView.storage_usage);
|
|
10234
|
+
const storageCost = storageUsage * STORAGE_PRICE_PER_BYTE;
|
|
10235
|
+
const rawAvailable = amount - locked - storageCost;
|
|
10236
|
+
return rawAvailable > 0 ? rawAvailable : BigInt(0);
|
|
10237
|
+
}
|
|
10238
|
+
async assertSufficientBalance(nearAccountId) {
|
|
10239
|
+
const { minBalanceYocto } = this.getConfig();
|
|
10240
|
+
try {
|
|
10241
|
+
const accountView = await this.context.nearClient.viewAccount(nearAccountId);
|
|
10242
|
+
const available = this.computeAvailableBalance(accountView);
|
|
10243
|
+
if (available < BigInt(minBalanceYocto)) await this.fail(1, `This account does not have enough NEAR to finalize recovery. Available: ${available.toString()} yocto; required: ${String(minBalanceYocto)}. Please top up and try again.`);
|
|
10244
|
+
} catch (e) {
|
|
10245
|
+
await this.fail(1, e?.message || "Failed to fetch account balance for recovery");
|
|
10246
|
+
}
|
|
10247
|
+
}
|
|
10248
|
+
async getCanonicalRecoveryEmailOrFail(recoveryEmail) {
|
|
10249
|
+
const canonicalEmail = String(recoveryEmail || "").trim().toLowerCase();
|
|
10250
|
+
if (!canonicalEmail) await this.fail(1, "Recovery email is required for email-based account recovery");
|
|
10251
|
+
return canonicalEmail;
|
|
10252
|
+
}
|
|
10253
|
+
async getNextDeviceNumberFromContract(nearAccountId) {
|
|
10254
|
+
try {
|
|
10255
|
+
const { syncAuthenticatorsContractCall: syncAuthenticatorsContractCall$1 } = await import("./rpcCalls-BQrJMTdg.js");
|
|
10256
|
+
const authenticators = await syncAuthenticatorsContractCall$1(this.context.nearClient, this.context.configs.contractId, nearAccountId);
|
|
10257
|
+
const numbers = authenticators.map((a) => a?.authenticator?.deviceNumber).filter((n) => typeof n === "number" && Number.isFinite(n));
|
|
10258
|
+
const max = numbers.length > 0 ? Math.max(...numbers) : 0;
|
|
10259
|
+
return max + 1;
|
|
10260
|
+
} catch {
|
|
10261
|
+
return 1;
|
|
10262
|
+
}
|
|
10096
10263
|
}
|
|
10097
|
-
async
|
|
10264
|
+
async collectRecoveryCredentialOrFail(nearAccountId, deviceNumber) {
|
|
10265
|
+
const confirmerText = {
|
|
10266
|
+
title: this.options?.confirmerText?.title ?? "Register New Recovery Account",
|
|
10267
|
+
body: this.options?.confirmerText?.body ?? "Create a recovery account and send an encrypted email to recover your account."
|
|
10268
|
+
};
|
|
10269
|
+
const confirm = await this.context.webAuthnManager.requestRegistrationCredentialConfirmation({
|
|
10270
|
+
nearAccountId,
|
|
10271
|
+
deviceNumber,
|
|
10272
|
+
confirmerText,
|
|
10273
|
+
confirmationConfigOverride: this.options?.confirmationConfig
|
|
10274
|
+
});
|
|
10275
|
+
if (!confirm.confirmed || !confirm.credential) await this.fail(2, "User cancelled email recovery TouchID confirmation");
|
|
10276
|
+
return {
|
|
10277
|
+
credential: confirm.credential,
|
|
10278
|
+
vrfChallenge: confirm.vrfChallenge || void 0
|
|
10279
|
+
};
|
|
10280
|
+
}
|
|
10281
|
+
async deriveRecoveryKeysOrFail(nearAccountId, deviceNumber, credential) {
|
|
10282
|
+
const vrfDerivationResult = await this.context.webAuthnManager.deriveVrfKeypair({
|
|
10283
|
+
credential,
|
|
10284
|
+
nearAccountId
|
|
10285
|
+
});
|
|
10286
|
+
if (!vrfDerivationResult.success || !vrfDerivationResult.encryptedVrfKeypair) await this.fail(2, "Failed to derive VRF keypair from PRF for email recovery");
|
|
10287
|
+
const nearKeyResult = await this.context.webAuthnManager.deriveNearKeypairAndEncryptFromSerialized({
|
|
10288
|
+
nearAccountId,
|
|
10289
|
+
credential,
|
|
10290
|
+
options: { deviceNumber }
|
|
10291
|
+
});
|
|
10292
|
+
if (!nearKeyResult.success || !nearKeyResult.publicKey) await this.fail(2, "Failed to derive NEAR keypair for email recovery");
|
|
10293
|
+
return {
|
|
10294
|
+
encryptedVrfKeypair: vrfDerivationResult.encryptedVrfKeypair,
|
|
10295
|
+
serverEncryptedVrfKeypair: vrfDerivationResult.serverEncryptedVrfKeypair || null,
|
|
10296
|
+
vrfPublicKey: vrfDerivationResult.vrfPublicKey,
|
|
10297
|
+
nearPublicKey: nearKeyResult.publicKey
|
|
10298
|
+
};
|
|
10299
|
+
}
|
|
10300
|
+
emitAwaitEmail(rec, mailtoUrl) {
|
|
10301
|
+
this.phase = EmailRecoveryPhase.STEP_3_AWAIT_EMAIL;
|
|
10302
|
+
this.emit({
|
|
10303
|
+
step: 3,
|
|
10304
|
+
phase: EmailRecoveryPhase.STEP_3_AWAIT_EMAIL,
|
|
10305
|
+
status: EmailRecoveryStatus.PROGRESS,
|
|
10306
|
+
message: "New device key created; please send the recovery email from your registered address.",
|
|
10307
|
+
data: {
|
|
10308
|
+
accountId: rec.accountId,
|
|
10309
|
+
recoveryEmail: rec.recoveryEmail,
|
|
10310
|
+
nearPublicKey: rec.nearPublicKey,
|
|
10311
|
+
requestId: rec.requestId,
|
|
10312
|
+
mailtoUrl
|
|
10313
|
+
}
|
|
10314
|
+
});
|
|
10315
|
+
}
|
|
10316
|
+
emitAutoLoginEvent(status, message, data) {
|
|
10317
|
+
this.emit({
|
|
10318
|
+
step: 5,
|
|
10319
|
+
phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
|
|
10320
|
+
status,
|
|
10321
|
+
message,
|
|
10322
|
+
data
|
|
10323
|
+
});
|
|
10324
|
+
}
|
|
10325
|
+
async checkViaDkimViewMethod(rec) {
|
|
10098
10326
|
const { dkimVerifierAccountId, verificationViewMethod } = this.getConfig();
|
|
10099
10327
|
if (!dkimVerifierAccountId) return null;
|
|
10100
10328
|
try {
|
|
10101
|
-
const
|
|
10102
|
-
|
|
10103
|
-
method: verificationViewMethod,
|
|
10104
|
-
args: { request_id: rec.requestId }
|
|
10105
|
-
});
|
|
10329
|
+
const { getEmailRecoveryVerificationResult } = await import("./rpcCalls-BQrJMTdg.js");
|
|
10330
|
+
const result = await getEmailRecoveryVerificationResult(this.context.nearClient, dkimVerifierAccountId, verificationViewMethod, rec.requestId);
|
|
10106
10331
|
if (!result) return {
|
|
10107
10332
|
completed: false,
|
|
10108
10333
|
success: false
|
|
@@ -10134,43 +10359,78 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10134
10359
|
transactionHash: result.transaction_hash
|
|
10135
10360
|
};
|
|
10136
10361
|
} catch (err$1) {
|
|
10137
|
-
console.warn("[EmailRecoveryFlow] get_verification_result view failed;
|
|
10362
|
+
console.warn("[EmailRecoveryFlow] get_verification_result view failed; will retry", err$1);
|
|
10138
10363
|
return null;
|
|
10139
10364
|
}
|
|
10140
10365
|
}
|
|
10141
|
-
|
|
10142
|
-
|
|
10143
|
-
|
|
10144
|
-
|
|
10145
|
-
|
|
10146
|
-
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10366
|
+
buildPollingEventData(rec, details) {
|
|
10367
|
+
return {
|
|
10368
|
+
accountId: rec.accountId,
|
|
10369
|
+
requestId: rec.requestId,
|
|
10370
|
+
nearPublicKey: rec.nearPublicKey,
|
|
10371
|
+
transactionHash: details.transactionHash,
|
|
10372
|
+
elapsedMs: details.elapsedMs,
|
|
10373
|
+
pollCount: details.pollCount
|
|
10374
|
+
};
|
|
10375
|
+
}
|
|
10376
|
+
async sleepForPollInterval(ms) {
|
|
10377
|
+
await new Promise((resolve) => {
|
|
10378
|
+
this.pollIntervalResolver = resolve;
|
|
10379
|
+
this.pollingTimer = setTimeout(() => {
|
|
10380
|
+
this.pollIntervalResolver = void 0;
|
|
10381
|
+
this.pollingTimer = void 0;
|
|
10382
|
+
resolve();
|
|
10383
|
+
}, ms);
|
|
10384
|
+
}).finally(() => {
|
|
10385
|
+
this.pollIntervalResolver = void 0;
|
|
10386
|
+
});
|
|
10387
|
+
}
|
|
10388
|
+
async pollUntil(args) {
|
|
10389
|
+
const now = args.now ?? Date.now;
|
|
10390
|
+
const sleep = args.sleep ?? this.sleepForPollInterval.bind(this);
|
|
10391
|
+
const startedAt = now();
|
|
10392
|
+
let pollCount = 0;
|
|
10393
|
+
while (!args.isCancelled()) {
|
|
10394
|
+
pollCount += 1;
|
|
10395
|
+
const elapsedMs$1 = now() - startedAt;
|
|
10396
|
+
if (elapsedMs$1 > args.timeoutMs) return {
|
|
10397
|
+
status: "timedOut",
|
|
10398
|
+
elapsedMs: elapsedMs$1,
|
|
10399
|
+
pollCount
|
|
10400
|
+
};
|
|
10401
|
+
const result = await args.tick({
|
|
10402
|
+
elapsedMs: elapsedMs$1,
|
|
10403
|
+
pollCount
|
|
10404
|
+
});
|
|
10405
|
+
if (result.done) return {
|
|
10406
|
+
status: "completed",
|
|
10407
|
+
value: result.value,
|
|
10408
|
+
elapsedMs: elapsedMs$1,
|
|
10409
|
+
pollCount
|
|
10410
|
+
};
|
|
10411
|
+
if (args.isCancelled()) return {
|
|
10412
|
+
status: "cancelled",
|
|
10413
|
+
elapsedMs: elapsedMs$1,
|
|
10414
|
+
pollCount
|
|
10415
|
+
};
|
|
10416
|
+
await sleep(args.intervalMs);
|
|
10158
10417
|
}
|
|
10159
|
-
|
|
10160
|
-
return
|
|
10418
|
+
const elapsedMs = now() - startedAt;
|
|
10419
|
+
return {
|
|
10420
|
+
status: "cancelled",
|
|
10421
|
+
elapsedMs,
|
|
10422
|
+
pollCount
|
|
10423
|
+
};
|
|
10424
|
+
}
|
|
10425
|
+
async loadPending(accountId, nearPublicKey) {
|
|
10426
|
+
return this.pendingStore.get(accountId, nearPublicKey);
|
|
10161
10427
|
}
|
|
10162
10428
|
async savePending(rec) {
|
|
10163
|
-
|
|
10164
|
-
await IndexedDBManager.clientDB.setAppState(key, rec);
|
|
10165
|
-
await IndexedDBManager.clientDB.setAppState(this.getPendingIndexKey(rec.accountId), rec.nearPublicKey).catch(() => {});
|
|
10429
|
+
await this.pendingStore.set(rec);
|
|
10166
10430
|
this.pending = rec;
|
|
10167
10431
|
}
|
|
10168
10432
|
async clearPending(accountId, nearPublicKey) {
|
|
10169
|
-
|
|
10170
|
-
const idx = await IndexedDBManager.clientDB.getAppState(indexKey).catch(() => void 0);
|
|
10171
|
-
const resolvedNearPublicKey = nearPublicKey || idx || "";
|
|
10172
|
-
if (resolvedNearPublicKey) await IndexedDBManager.clientDB.setAppState(this.getPendingRecordKey(accountId, resolvedNearPublicKey), void 0).catch(() => {});
|
|
10173
|
-
if (!nearPublicKey || idx === nearPublicKey) await IndexedDBManager.clientDB.setAppState(indexKey, void 0).catch(() => {});
|
|
10433
|
+
await this.pendingStore.clear(accountId, nearPublicKey);
|
|
10174
10434
|
if (this.pending && this.pending.accountId === accountId && (!nearPublicKey || this.pending.nearPublicKey === nearPublicKey)) this.pending = null;
|
|
10175
10435
|
}
|
|
10176
10436
|
getState() {
|
|
@@ -10184,48 +10444,14 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10184
10444
|
const { accountId, nearPublicKey } = args;
|
|
10185
10445
|
this.cancelled = false;
|
|
10186
10446
|
this.error = void 0;
|
|
10187
|
-
const
|
|
10188
|
-
|
|
10189
|
-
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
const nearAccountId = toAccountId(accountId);
|
|
10194
|
-
let rec = this.pending;
|
|
10195
|
-
if (!rec || rec.accountId !== nearAccountId || nearPublicKey && rec.nearPublicKey !== nearPublicKey) {
|
|
10196
|
-
rec = await this.loadPending(nearAccountId, nearPublicKey);
|
|
10197
|
-
this.pending = rec;
|
|
10198
|
-
}
|
|
10199
|
-
if (!rec) {
|
|
10200
|
-
const err$1 = this.emitError(3, "No pending email recovery record found for this account");
|
|
10201
|
-
await this.options?.afterCall?.(false);
|
|
10202
|
-
throw err$1;
|
|
10203
|
-
}
|
|
10204
|
-
if (rec.status === "error") {
|
|
10205
|
-
const err$1 = this.emitError(3, "Pending email recovery is in an error state; please restart the flow");
|
|
10206
|
-
await this.options?.afterCall?.(false);
|
|
10207
|
-
throw err$1;
|
|
10208
|
-
}
|
|
10209
|
-
if (rec.status === "finalizing" || rec.status === "complete") {
|
|
10210
|
-
const err$1 = this.emitError(3, "Recovery email has already been processed on-chain for this request");
|
|
10211
|
-
await this.options?.afterCall?.(false);
|
|
10212
|
-
throw err$1;
|
|
10213
|
-
}
|
|
10447
|
+
const nearAccountId = await this.assertValidAccountIdOrFail(3, accountId);
|
|
10448
|
+
const rec = await this.resolvePendingOrFail(3, {
|
|
10449
|
+
accountId: nearAccountId,
|
|
10450
|
+
nearPublicKey
|
|
10451
|
+
}, { allowErrorStatus: false });
|
|
10452
|
+
if (rec.status === "finalizing" || rec.status === "complete") await this.fail(3, "Recovery email has already been processed on-chain for this request");
|
|
10214
10453
|
const mailtoUrl = rec.status === "awaiting-email" ? await this.buildMailtoUrlAndUpdateStatus(rec) : this.buildMailtoUrlInternal(rec);
|
|
10215
|
-
this.
|
|
10216
|
-
this.emit({
|
|
10217
|
-
step: 3,
|
|
10218
|
-
phase: EmailRecoveryPhase.STEP_3_AWAIT_EMAIL,
|
|
10219
|
-
status: EmailRecoveryStatus.PROGRESS,
|
|
10220
|
-
message: "New device key created; please send the recovery email from your registered address.",
|
|
10221
|
-
data: {
|
|
10222
|
-
accountId: rec.accountId,
|
|
10223
|
-
recoveryEmail: rec.recoveryEmail,
|
|
10224
|
-
nearPublicKey: rec.nearPublicKey,
|
|
10225
|
-
requestId: rec.requestId,
|
|
10226
|
-
mailtoUrl
|
|
10227
|
-
}
|
|
10228
|
-
});
|
|
10454
|
+
this.emitAwaitEmail(rec, mailtoUrl);
|
|
10229
10455
|
await this.options?.afterCall?.(true, void 0);
|
|
10230
10456
|
return mailtoUrl;
|
|
10231
10457
|
}
|
|
@@ -10240,49 +10466,10 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10240
10466
|
status: EmailRecoveryStatus.PROGRESS,
|
|
10241
10467
|
message: "Preparing email recovery..."
|
|
10242
10468
|
});
|
|
10243
|
-
const
|
|
10244
|
-
|
|
10245
|
-
|
|
10246
|
-
|
|
10247
|
-
throw err$1;
|
|
10248
|
-
}
|
|
10249
|
-
const nearAccountId = toAccountId(accountId);
|
|
10250
|
-
const { minBalanceYocto } = this.getConfig();
|
|
10251
|
-
const STORAGE_PRICE_PER_BYTE = BigInt("10000000000000000000");
|
|
10252
|
-
try {
|
|
10253
|
-
const accountView = await this.context.nearClient.viewAccount(nearAccountId);
|
|
10254
|
-
const amount = BigInt(accountView.amount || "0");
|
|
10255
|
-
const locked = BigInt(accountView.locked || "0");
|
|
10256
|
-
const storageUsage = BigInt(accountView.storage_usage || 0);
|
|
10257
|
-
const storageCost = storageUsage * STORAGE_PRICE_PER_BYTE;
|
|
10258
|
-
const rawAvailable = amount - locked - storageCost;
|
|
10259
|
-
const available = rawAvailable > 0 ? rawAvailable : BigInt(0);
|
|
10260
|
-
if (available < BigInt(minBalanceYocto)) {
|
|
10261
|
-
const err$1 = this.emitError(1, `This account does not have enough NEAR to finalize recovery. Available: ${available.toString()} yocto; required: ${String(minBalanceYocto)}. Please top up and try again.`);
|
|
10262
|
-
await this.options?.afterCall?.(false);
|
|
10263
|
-
throw err$1;
|
|
10264
|
-
}
|
|
10265
|
-
} catch (e) {
|
|
10266
|
-
const err$1 = this.emitError(1, e?.message || "Failed to fetch account balance for recovery");
|
|
10267
|
-
await this.options?.afterCall?.(false);
|
|
10268
|
-
throw err$1;
|
|
10269
|
-
}
|
|
10270
|
-
const canonicalEmail = String(recoveryEmail || "").trim().toLowerCase();
|
|
10271
|
-
if (!canonicalEmail) {
|
|
10272
|
-
const err$1 = this.emitError(1, "Recovery email is required for email-based account recovery");
|
|
10273
|
-
await this.options?.afterCall?.(false);
|
|
10274
|
-
throw err$1;
|
|
10275
|
-
}
|
|
10276
|
-
let deviceNumber = 1;
|
|
10277
|
-
try {
|
|
10278
|
-
const { syncAuthenticatorsContractCall: syncAuthenticatorsContractCall$1 } = await import("./rpcCalls-DEv9x5-f.js");
|
|
10279
|
-
const authenticators = await syncAuthenticatorsContractCall$1(this.context.nearClient, this.context.configs.contractId, nearAccountId);
|
|
10280
|
-
const numbers = authenticators.map((a) => a?.authenticator?.deviceNumber).filter((n) => typeof n === "number" && Number.isFinite(n));
|
|
10281
|
-
const max = numbers.length > 0 ? Math.max(...numbers) : 0;
|
|
10282
|
-
deviceNumber = max + 1;
|
|
10283
|
-
} catch {
|
|
10284
|
-
deviceNumber = 1;
|
|
10285
|
-
}
|
|
10469
|
+
const nearAccountId = await this.assertValidAccountIdOrFail(1, accountId);
|
|
10470
|
+
await this.assertSufficientBalance(nearAccountId);
|
|
10471
|
+
const canonicalEmail = await this.getCanonicalRecoveryEmailOrFail(recoveryEmail);
|
|
10472
|
+
const deviceNumber = await this.getNextDeviceNumberFromContract(nearAccountId);
|
|
10286
10473
|
this.phase = EmailRecoveryPhase.STEP_2_TOUCH_ID_REGISTRATION;
|
|
10287
10474
|
this.emit({
|
|
10288
10475
|
step: 2,
|
|
@@ -10291,69 +10478,24 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10291
10478
|
message: "Collecting passkey for email recovery..."
|
|
10292
10479
|
});
|
|
10293
10480
|
try {
|
|
10294
|
-
const
|
|
10295
|
-
|
|
10296
|
-
body: this.options?.confirmerText?.body ?? "Create a recovery account and send an encrypted email to recover your account."
|
|
10297
|
-
};
|
|
10298
|
-
const confirm = await this.context.webAuthnManager.requestRegistrationCredentialConfirmation({
|
|
10299
|
-
nearAccountId,
|
|
10300
|
-
deviceNumber,
|
|
10301
|
-
confirmerText,
|
|
10302
|
-
confirmationConfigOverride: this.options?.confirmationConfig
|
|
10303
|
-
});
|
|
10304
|
-
if (!confirm.confirmed || !confirm.credential) {
|
|
10305
|
-
const err$1 = this.emitError(2, "User cancelled email recovery TouchID confirmation");
|
|
10306
|
-
await this.options?.afterCall?.(false);
|
|
10307
|
-
throw err$1;
|
|
10308
|
-
}
|
|
10309
|
-
const vrfDerivationResult = await this.context.webAuthnManager.deriveVrfKeypair({
|
|
10310
|
-
credential: confirm.credential,
|
|
10311
|
-
nearAccountId
|
|
10312
|
-
});
|
|
10313
|
-
if (!vrfDerivationResult.success || !vrfDerivationResult.encryptedVrfKeypair) {
|
|
10314
|
-
const err$1 = this.emitError(2, "Failed to derive VRF keypair from PRF for email recovery");
|
|
10315
|
-
await this.options?.afterCall?.(false);
|
|
10316
|
-
throw err$1;
|
|
10317
|
-
}
|
|
10318
|
-
const nearKeyResult = await this.context.webAuthnManager.deriveNearKeypairAndEncryptFromSerialized({
|
|
10319
|
-
nearAccountId,
|
|
10320
|
-
credential: confirm.credential,
|
|
10321
|
-
options: { deviceNumber }
|
|
10322
|
-
});
|
|
10323
|
-
if (!nearKeyResult.success || !nearKeyResult.publicKey) {
|
|
10324
|
-
const err$1 = this.emitError(2, "Failed to derive NEAR keypair for email recovery");
|
|
10325
|
-
await this.options?.afterCall?.(false);
|
|
10326
|
-
throw err$1;
|
|
10327
|
-
}
|
|
10481
|
+
const confirm = await this.collectRecoveryCredentialOrFail(nearAccountId, deviceNumber);
|
|
10482
|
+
const derivedKeys = await this.deriveRecoveryKeysOrFail(nearAccountId, deviceNumber, confirm.credential);
|
|
10328
10483
|
const rec = {
|
|
10329
10484
|
accountId: nearAccountId,
|
|
10330
10485
|
recoveryEmail: canonicalEmail,
|
|
10331
10486
|
deviceNumber,
|
|
10332
|
-
nearPublicKey:
|
|
10487
|
+
nearPublicKey: derivedKeys.nearPublicKey,
|
|
10333
10488
|
requestId: generateEmailRecoveryRequestId(),
|
|
10334
|
-
encryptedVrfKeypair:
|
|
10335
|
-
serverEncryptedVrfKeypair:
|
|
10336
|
-
vrfPublicKey:
|
|
10489
|
+
encryptedVrfKeypair: derivedKeys.encryptedVrfKeypair,
|
|
10490
|
+
serverEncryptedVrfKeypair: derivedKeys.serverEncryptedVrfKeypair,
|
|
10491
|
+
vrfPublicKey: derivedKeys.vrfPublicKey,
|
|
10337
10492
|
credential: confirm.credential,
|
|
10338
10493
|
vrfChallenge: confirm.vrfChallenge || void 0,
|
|
10339
10494
|
createdAt: Date.now(),
|
|
10340
10495
|
status: "awaiting-email"
|
|
10341
10496
|
};
|
|
10342
10497
|
const mailtoUrl = await this.buildMailtoUrlAndUpdateStatus(rec);
|
|
10343
|
-
this.
|
|
10344
|
-
this.emit({
|
|
10345
|
-
step: 3,
|
|
10346
|
-
phase: EmailRecoveryPhase.STEP_3_AWAIT_EMAIL,
|
|
10347
|
-
status: EmailRecoveryStatus.PROGRESS,
|
|
10348
|
-
message: "New device key created; please send the recovery email from your registered address.",
|
|
10349
|
-
data: {
|
|
10350
|
-
accountId: rec.accountId,
|
|
10351
|
-
recoveryEmail: rec.recoveryEmail,
|
|
10352
|
-
nearPublicKey: rec.nearPublicKey,
|
|
10353
|
-
requestId: rec.requestId,
|
|
10354
|
-
mailtoUrl
|
|
10355
|
-
}
|
|
10356
|
-
});
|
|
10498
|
+
this.emitAwaitEmail(rec, mailtoUrl);
|
|
10357
10499
|
await this.options?.afterCall?.(true, void 0);
|
|
10358
10500
|
return {
|
|
10359
10501
|
mailtoUrl,
|
|
@@ -10381,28 +10523,11 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10381
10523
|
const { accountId, nearPublicKey } = args;
|
|
10382
10524
|
this.cancelled = false;
|
|
10383
10525
|
this.error = void 0;
|
|
10384
|
-
const
|
|
10385
|
-
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
10389
|
-
}
|
|
10390
|
-
const nearAccountId = toAccountId(accountId);
|
|
10391
|
-
let rec = this.pending;
|
|
10392
|
-
if (!rec || rec.accountId !== nearAccountId || nearPublicKey && rec.nearPublicKey !== nearPublicKey) {
|
|
10393
|
-
rec = await this.loadPending(nearAccountId, nearPublicKey);
|
|
10394
|
-
this.pending = rec;
|
|
10395
|
-
}
|
|
10396
|
-
if (!rec) {
|
|
10397
|
-
const err$1 = this.emitError(4, "No pending email recovery record found for this account");
|
|
10398
|
-
await this.options?.afterCall?.(false);
|
|
10399
|
-
throw err$1;
|
|
10400
|
-
}
|
|
10401
|
-
if (rec.status === "error") {
|
|
10402
|
-
const err$1 = this.emitError(4, "Pending email recovery is in an error state; please restart the flow");
|
|
10403
|
-
await this.options?.afterCall?.(false);
|
|
10404
|
-
throw err$1;
|
|
10405
|
-
}
|
|
10526
|
+
const nearAccountId = await this.assertValidAccountIdOrFail(4, accountId);
|
|
10527
|
+
const rec = await this.resolvePendingOrFail(4, {
|
|
10528
|
+
accountId: nearAccountId,
|
|
10529
|
+
nearPublicKey
|
|
10530
|
+
}, { allowErrorStatus: false });
|
|
10406
10531
|
if (rec.status === "complete" || rec.status === "finalizing") {
|
|
10407
10532
|
await this.options?.afterCall?.(true, void 0);
|
|
10408
10533
|
return;
|
|
@@ -10442,23 +10567,11 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10442
10567
|
const { accountId, nearPublicKey } = args;
|
|
10443
10568
|
this.cancelled = false;
|
|
10444
10569
|
this.error = void 0;
|
|
10445
|
-
const
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
|
|
10449
|
-
|
|
10450
|
-
}
|
|
10451
|
-
const nearAccountId = toAccountId(accountId);
|
|
10452
|
-
let rec = this.pending;
|
|
10453
|
-
if (!rec || rec.accountId !== nearAccountId || nearPublicKey && rec.nearPublicKey !== nearPublicKey) {
|
|
10454
|
-
rec = await this.loadPending(nearAccountId, nearPublicKey);
|
|
10455
|
-
this.pending = rec;
|
|
10456
|
-
}
|
|
10457
|
-
if (!rec) {
|
|
10458
|
-
const err$1 = this.emitError(4, "No pending email recovery record found for this account");
|
|
10459
|
-
await this.options?.afterCall?.(false);
|
|
10460
|
-
throw err$1;
|
|
10461
|
-
}
|
|
10570
|
+
const nearAccountId = await this.assertValidAccountIdOrFail(4, accountId);
|
|
10571
|
+
const rec = await this.resolvePendingOrFail(4, {
|
|
10572
|
+
accountId: nearAccountId,
|
|
10573
|
+
nearPublicKey
|
|
10574
|
+
}, { allowErrorStatus: true });
|
|
10462
10575
|
this.emit({
|
|
10463
10576
|
step: 0,
|
|
10464
10577
|
phase: EmailRecoveryPhase.RESUMED_FROM_PENDING,
|
|
@@ -10494,245 +10607,230 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10494
10607
|
}
|
|
10495
10608
|
this.phase = EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT;
|
|
10496
10609
|
this.pollingStartedAt = Date.now();
|
|
10497
|
-
|
|
10498
|
-
|
|
10499
|
-
|
|
10500
|
-
|
|
10501
|
-
|
|
10502
|
-
const
|
|
10610
|
+
const pollResult = await this.pollUntil({
|
|
10611
|
+
intervalMs: pollingIntervalMs,
|
|
10612
|
+
timeoutMs: maxPollingDurationMs,
|
|
10613
|
+
isCancelled: () => this.cancelled,
|
|
10614
|
+
tick: async ({ elapsedMs, pollCount }) => {
|
|
10615
|
+
const verification = await this.checkViaDkimViewMethod(rec);
|
|
10616
|
+
const completed = verification?.completed === true;
|
|
10617
|
+
const success = verification?.success === true;
|
|
10618
|
+
this.emit({
|
|
10619
|
+
step: 4,
|
|
10620
|
+
phase: EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT,
|
|
10621
|
+
status: EmailRecoveryStatus.PROGRESS,
|
|
10622
|
+
message: completed && success ? `Email verified for request ${rec.requestId}; finalizing registration` : `Waiting for email verification for request ${rec.requestId}`,
|
|
10623
|
+
data: this.buildPollingEventData(rec, {
|
|
10624
|
+
transactionHash: verification?.transactionHash,
|
|
10625
|
+
elapsedMs,
|
|
10626
|
+
pollCount
|
|
10627
|
+
})
|
|
10628
|
+
});
|
|
10629
|
+
if (!completed) return { done: false };
|
|
10630
|
+
if (!success) return {
|
|
10631
|
+
done: true,
|
|
10632
|
+
value: {
|
|
10633
|
+
outcome: "failed",
|
|
10634
|
+
errorMessage: verification?.errorMessage || "Email verification failed"
|
|
10635
|
+
}
|
|
10636
|
+
};
|
|
10637
|
+
return {
|
|
10638
|
+
done: true,
|
|
10639
|
+
value: { outcome: "verified" }
|
|
10640
|
+
};
|
|
10641
|
+
}
|
|
10642
|
+
});
|
|
10643
|
+
if (pollResult.status === "completed") {
|
|
10644
|
+
if (pollResult.value.outcome === "failed") {
|
|
10645
|
+
const err$2 = this.emitError(4, pollResult.value.errorMessage);
|
|
10503
10646
|
rec.status = "error";
|
|
10504
10647
|
await this.savePending(rec);
|
|
10505
10648
|
await this.options?.afterCall?.(false);
|
|
10506
10649
|
throw err$2;
|
|
10507
10650
|
}
|
|
10508
|
-
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
|
|
10513
|
-
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
requestId: rec.requestId,
|
|
10519
|
-
nearPublicKey: rec.nearPublicKey,
|
|
10520
|
-
transactionHash: verification?.transactionHash,
|
|
10521
|
-
elapsedMs: elapsed,
|
|
10522
|
-
pollCount
|
|
10523
|
-
}
|
|
10524
|
-
});
|
|
10525
|
-
if (completed) {
|
|
10526
|
-
if (!success) {
|
|
10527
|
-
const err$2 = this.emitError(4, verification?.errorMessage || "Email verification failed");
|
|
10528
|
-
rec.status = "error";
|
|
10529
|
-
await this.savePending(rec);
|
|
10530
|
-
await this.options?.afterCall?.(false);
|
|
10531
|
-
throw err$2;
|
|
10532
|
-
}
|
|
10533
|
-
rec.status = "finalizing";
|
|
10534
|
-
await this.savePending(rec);
|
|
10535
|
-
return;
|
|
10536
|
-
}
|
|
10537
|
-
if (this.cancelled) break;
|
|
10538
|
-
await new Promise((resolve) => {
|
|
10539
|
-
this.pollIntervalResolver = resolve;
|
|
10540
|
-
this.pollingTimer = setTimeout(() => {
|
|
10541
|
-
this.pollIntervalResolver = void 0;
|
|
10542
|
-
this.pollingTimer = void 0;
|
|
10543
|
-
resolve();
|
|
10544
|
-
}, pollingIntervalMs);
|
|
10545
|
-
}).finally(() => {
|
|
10546
|
-
this.pollIntervalResolver = void 0;
|
|
10547
|
-
});
|
|
10651
|
+
rec.status = "finalizing";
|
|
10652
|
+
await this.savePending(rec);
|
|
10653
|
+
return;
|
|
10654
|
+
}
|
|
10655
|
+
if (pollResult.status === "timedOut") {
|
|
10656
|
+
const err$2 = this.emitError(4, "Timed out waiting for recovery email to be processed on-chain");
|
|
10657
|
+
rec.status = "error";
|
|
10658
|
+
await this.savePending(rec);
|
|
10659
|
+
await this.options?.afterCall?.(false);
|
|
10660
|
+
throw err$2;
|
|
10548
10661
|
}
|
|
10549
10662
|
const err$1 = this.emitError(4, "Email recovery polling was cancelled");
|
|
10550
10663
|
await this.options?.afterCall?.(false);
|
|
10551
10664
|
throw err$1;
|
|
10552
10665
|
}
|
|
10553
|
-
|
|
10554
|
-
this.phase = EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION;
|
|
10555
|
-
this.emit({
|
|
10556
|
-
step: 5,
|
|
10557
|
-
phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
|
|
10558
|
-
status: EmailRecoveryStatus.PROGRESS,
|
|
10559
|
-
message: "Finalizing email recovery registration...",
|
|
10560
|
-
data: {
|
|
10561
|
-
accountId: rec.accountId,
|
|
10562
|
-
nearPublicKey: rec.nearPublicKey
|
|
10563
|
-
}
|
|
10564
|
-
});
|
|
10666
|
+
initializeNonceManager(rec) {
|
|
10565
10667
|
const nonceManager = this.context.webAuthnManager.getNonceManager();
|
|
10566
10668
|
const accountId = toAccountId(rec.accountId);
|
|
10567
10669
|
nonceManager.initializeUser(accountId, rec.nearPublicKey);
|
|
10670
|
+
return {
|
|
10671
|
+
nonceManager,
|
|
10672
|
+
accountId
|
|
10673
|
+
};
|
|
10674
|
+
}
|
|
10675
|
+
async signRegistrationTx(rec, accountId) {
|
|
10676
|
+
const vrfChallenge = rec.vrfChallenge;
|
|
10677
|
+
if (!vrfChallenge) return this.fail(5, "Missing VRF challenge for email recovery registration");
|
|
10678
|
+
const registrationResult = await this.context.webAuthnManager.signDevice2RegistrationWithStoredKey({
|
|
10679
|
+
nearAccountId: accountId,
|
|
10680
|
+
credential: rec.credential,
|
|
10681
|
+
vrfChallenge,
|
|
10682
|
+
deterministicVrfPublicKey: rec.vrfPublicKey,
|
|
10683
|
+
deviceNumber: rec.deviceNumber
|
|
10684
|
+
});
|
|
10685
|
+
if (!registrationResult.success || !registrationResult.signedTransaction) await this.fail(5, registrationResult.error || "Failed to sign email recovery registration transaction");
|
|
10686
|
+
return registrationResult.signedTransaction;
|
|
10687
|
+
}
|
|
10688
|
+
async broadcastRegistrationTxAndWaitFinal(rec, signedTx) {
|
|
10568
10689
|
try {
|
|
10569
|
-
|
|
10570
|
-
const err$1 = this.emitError(5, "Missing VRF challenge for email recovery registration");
|
|
10571
|
-
await this.options?.afterCall?.(false);
|
|
10572
|
-
throw err$1;
|
|
10573
|
-
}
|
|
10574
|
-
const registrationResult = await this.context.webAuthnManager.signDevice2RegistrationWithStoredKey({
|
|
10575
|
-
nearAccountId: accountId,
|
|
10576
|
-
credential: rec.credential,
|
|
10577
|
-
vrfChallenge: rec.vrfChallenge,
|
|
10578
|
-
deterministicVrfPublicKey: rec.vrfPublicKey,
|
|
10579
|
-
deviceNumber: rec.deviceNumber
|
|
10580
|
-
});
|
|
10581
|
-
if (!registrationResult.success || !registrationResult.signedTransaction) {
|
|
10582
|
-
const err$1 = this.emitError(5, registrationResult.error || "Failed to sign email recovery registration transaction");
|
|
10583
|
-
await this.options?.afterCall?.(false);
|
|
10584
|
-
throw err$1;
|
|
10585
|
-
}
|
|
10586
|
-
const signedTx = registrationResult.signedTransaction;
|
|
10690
|
+
const txResult = await this.context.nearClient.sendTransaction(signedTx, DEFAULT_WAIT_STATUS.linkDeviceRegistration);
|
|
10587
10691
|
try {
|
|
10588
|
-
const
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10594
|
-
|
|
10595
|
-
|
|
10596
|
-
|
|
10597
|
-
|
|
10598
|
-
accountId: rec.accountId,
|
|
10599
|
-
nearPublicKey: rec.nearPublicKey,
|
|
10600
|
-
transactionHash: txHash
|
|
10601
|
-
}
|
|
10602
|
-
});
|
|
10603
|
-
try {
|
|
10604
|
-
await IndexedDBManager.clientDB.storeWebAuthnUserData({
|
|
10605
|
-
nearAccountId: accountId,
|
|
10606
|
-
deviceNumber: rec.deviceNumber,
|
|
10607
|
-
clientNearPublicKey: rec.nearPublicKey,
|
|
10608
|
-
passkeyCredential: {
|
|
10609
|
-
id: rec.credential.id,
|
|
10610
|
-
rawId: rec.credential.rawId
|
|
10611
|
-
},
|
|
10612
|
-
encryptedVrfKeypair: rec.encryptedVrfKeypair,
|
|
10613
|
-
serverEncryptedVrfKeypair: rec.serverEncryptedVrfKeypair || void 0
|
|
10614
|
-
});
|
|
10615
|
-
const { syncAuthenticatorsContractCall: syncAuthenticatorsContractCall$1 } = await import("./rpcCalls-DEv9x5-f.js");
|
|
10616
|
-
const authenticators = await syncAuthenticatorsContractCall$1(this.context.nearClient, this.context.configs.contractId, accountId);
|
|
10617
|
-
const mappedAuthenticators = authenticators.map(({ authenticator }) => ({
|
|
10618
|
-
credentialId: authenticator.credentialId,
|
|
10619
|
-
credentialPublicKey: authenticator.credentialPublicKey,
|
|
10620
|
-
transports: authenticator.transports,
|
|
10621
|
-
name: authenticator.name,
|
|
10622
|
-
registered: authenticator.registered.toISOString(),
|
|
10623
|
-
vrfPublicKey: authenticator.vrfPublicKeys?.[0] || "",
|
|
10624
|
-
deviceNumber: authenticator.deviceNumber
|
|
10625
|
-
}));
|
|
10626
|
-
await IndexedDBManager.clientDB.syncAuthenticatorsFromContract(accountId, mappedAuthenticators);
|
|
10627
|
-
await IndexedDBManager.clientDB.setLastUser(accountId, rec.deviceNumber);
|
|
10628
|
-
} catch (syncErr) {
|
|
10629
|
-
console.warn("[EmailRecoveryFlow] Failed to sync authenticators after recovery:", syncErr);
|
|
10630
|
-
}
|
|
10692
|
+
const txHash = txResult?.transaction?.hash || txResult?.transaction_hash;
|
|
10693
|
+
if (txHash) this.emit({
|
|
10694
|
+
step: 5,
|
|
10695
|
+
phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
|
|
10696
|
+
status: EmailRecoveryStatus.PROGRESS,
|
|
10697
|
+
message: "Registration transaction confirmed",
|
|
10698
|
+
data: {
|
|
10699
|
+
accountId: rec.accountId,
|
|
10700
|
+
nearPublicKey: rec.nearPublicKey,
|
|
10701
|
+
transactionHash: txHash
|
|
10631
10702
|
}
|
|
10632
|
-
}
|
|
10633
|
-
|
|
10634
|
-
const msg = String(e?.message || "");
|
|
10635
|
-
const err$1 = this.emitError(5, msg || "Failed to broadcast email recovery registration transaction (insufficient funds or RPC error)");
|
|
10636
|
-
await this.options?.afterCall?.(false);
|
|
10637
|
-
throw err$1;
|
|
10638
|
-
}
|
|
10639
|
-
try {
|
|
10640
|
-
const txNonce = signedTx.transaction?.nonce;
|
|
10641
|
-
if (txNonce != null) await nonceManager.updateNonceFromBlockchain(this.context.nearClient, String(txNonce));
|
|
10703
|
+
});
|
|
10704
|
+
return txHash;
|
|
10642
10705
|
} catch {}
|
|
10706
|
+
} catch (e) {
|
|
10707
|
+
const msg = String(e?.message || "");
|
|
10708
|
+
await this.fail(5, msg || "Failed to broadcast email recovery registration transaction (insufficient funds or RPC error)");
|
|
10709
|
+
}
|
|
10710
|
+
return void 0;
|
|
10711
|
+
}
|
|
10712
|
+
mapAuthenticatorsFromContract(authenticators) {
|
|
10713
|
+
return authenticators.map(({ authenticator }) => ({
|
|
10714
|
+
credentialId: authenticator.credentialId,
|
|
10715
|
+
credentialPublicKey: authenticator.credentialPublicKey,
|
|
10716
|
+
transports: authenticator.transports,
|
|
10717
|
+
name: authenticator.name,
|
|
10718
|
+
registered: authenticator.registered.toISOString(),
|
|
10719
|
+
vrfPublicKey: authenticator.vrfPublicKeys?.[0] || "",
|
|
10720
|
+
deviceNumber: authenticator.deviceNumber
|
|
10721
|
+
}));
|
|
10722
|
+
}
|
|
10723
|
+
async syncAuthenticatorsBestEffort(accountId) {
|
|
10724
|
+
try {
|
|
10725
|
+
const { syncAuthenticatorsContractCall: syncAuthenticatorsContractCall$1 } = await import("./rpcCalls-BQrJMTdg.js");
|
|
10726
|
+
const authenticators = await syncAuthenticatorsContractCall$1(this.context.nearClient, this.context.configs.contractId, accountId);
|
|
10727
|
+
const mappedAuthenticators = this.mapAuthenticatorsFromContract(authenticators);
|
|
10728
|
+
await IndexedDBManager.clientDB.syncAuthenticatorsFromContract(accountId, mappedAuthenticators);
|
|
10729
|
+
return true;
|
|
10730
|
+
} catch (err$1) {
|
|
10731
|
+
console.warn("[EmailRecoveryFlow] Failed to sync authenticators after recovery:", err$1);
|
|
10732
|
+
return false;
|
|
10733
|
+
}
|
|
10734
|
+
}
|
|
10735
|
+
async setLastUserBestEffort(accountId, deviceNumber) {
|
|
10736
|
+
try {
|
|
10737
|
+
await IndexedDBManager.clientDB.setLastUser(accountId, deviceNumber);
|
|
10738
|
+
return true;
|
|
10739
|
+
} catch (err$1) {
|
|
10740
|
+
console.warn("[EmailRecoveryFlow] Failed to set last user after recovery:", err$1);
|
|
10741
|
+
return false;
|
|
10742
|
+
}
|
|
10743
|
+
}
|
|
10744
|
+
async updateNonceBestEffort(nonceManager, signedTx) {
|
|
10745
|
+
try {
|
|
10746
|
+
const txNonce = signedTx.transaction?.nonce;
|
|
10747
|
+
if (txNonce != null) await nonceManager.updateNonceFromBlockchain(this.context.nearClient, String(txNonce));
|
|
10748
|
+
} catch {}
|
|
10749
|
+
}
|
|
10750
|
+
async persistRecoveredUserData(rec, accountId) {
|
|
10751
|
+
const { webAuthnManager } = this.context;
|
|
10752
|
+
const payload = {
|
|
10753
|
+
nearAccountId: accountId,
|
|
10754
|
+
deviceNumber: rec.deviceNumber,
|
|
10755
|
+
clientNearPublicKey: rec.nearPublicKey,
|
|
10756
|
+
lastUpdated: Date.now(),
|
|
10757
|
+
passkeyCredential: {
|
|
10758
|
+
id: rec.credential.id,
|
|
10759
|
+
rawId: rec.credential.rawId
|
|
10760
|
+
},
|
|
10761
|
+
encryptedVrfKeypair: {
|
|
10762
|
+
encryptedVrfDataB64u: rec.encryptedVrfKeypair.encryptedVrfDataB64u,
|
|
10763
|
+
chacha20NonceB64u: rec.encryptedVrfKeypair.chacha20NonceB64u
|
|
10764
|
+
},
|
|
10765
|
+
serverEncryptedVrfKeypair: rec.serverEncryptedVrfKeypair || void 0
|
|
10766
|
+
};
|
|
10767
|
+
await webAuthnManager.storeUserData(payload);
|
|
10768
|
+
}
|
|
10769
|
+
/**
|
|
10770
|
+
* Explicitly persist the authenticator from the recovery record into the local cache.
|
|
10771
|
+
* This ensures the key is available immediately, bridging the gap before RPC sync sees it.
|
|
10772
|
+
*/
|
|
10773
|
+
async persistAuthenticatorBestEffort(rec, accountId) {
|
|
10774
|
+
try {
|
|
10643
10775
|
const { webAuthnManager } = this.context;
|
|
10644
|
-
|
|
10776
|
+
const attestationB64u = rec.credential.response.attestationObject;
|
|
10777
|
+
const credentialPublicKey = await webAuthnManager.extractCosePublicKey(attestationB64u);
|
|
10778
|
+
await webAuthnManager.storeAuthenticator({
|
|
10645
10779
|
nearAccountId: accountId,
|
|
10646
10780
|
deviceNumber: rec.deviceNumber,
|
|
10647
|
-
|
|
10648
|
-
|
|
10649
|
-
|
|
10650
|
-
|
|
10651
|
-
|
|
10652
|
-
|
|
10653
|
-
|
|
10654
|
-
encryptedVrfDataB64u: rec.encryptedVrfKeypair.encryptedVrfDataB64u,
|
|
10655
|
-
chacha20NonceB64u: rec.encryptedVrfKeypair.chacha20NonceB64u
|
|
10656
|
-
},
|
|
10657
|
-
serverEncryptedVrfKeypair: rec.serverEncryptedVrfKeypair || void 0
|
|
10658
|
-
});
|
|
10659
|
-
try {
|
|
10660
|
-
const attestationB64u = rec.credential.response.attestationObject;
|
|
10661
|
-
const credentialPublicKey = await webAuthnManager.extractCosePublicKey(attestationB64u);
|
|
10662
|
-
await webAuthnManager.storeAuthenticator({
|
|
10663
|
-
nearAccountId: accountId,
|
|
10664
|
-
deviceNumber: rec.deviceNumber,
|
|
10665
|
-
credentialId: rec.credential.rawId,
|
|
10666
|
-
credentialPublicKey,
|
|
10667
|
-
transports: ["internal"],
|
|
10668
|
-
name: `Device ${rec.deviceNumber} Passkey for ${rec.accountId.split(".")[0]}`,
|
|
10669
|
-
registered: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10670
|
-
syncedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10671
|
-
vrfPublicKey: rec.vrfPublicKey
|
|
10672
|
-
});
|
|
10673
|
-
} catch {}
|
|
10674
|
-
await this.attemptAutoLogin(rec);
|
|
10675
|
-
rec.status = "complete";
|
|
10676
|
-
await this.savePending(rec);
|
|
10677
|
-
await this.clearPending(rec.accountId, rec.nearPublicKey);
|
|
10678
|
-
this.phase = EmailRecoveryPhase.STEP_6_COMPLETE;
|
|
10679
|
-
this.emit({
|
|
10680
|
-
step: 6,
|
|
10681
|
-
phase: EmailRecoveryPhase.STEP_6_COMPLETE,
|
|
10682
|
-
status: EmailRecoveryStatus.SUCCESS,
|
|
10683
|
-
message: "Email recovery completed successfully",
|
|
10684
|
-
data: {
|
|
10685
|
-
accountId: rec.accountId,
|
|
10686
|
-
nearPublicKey: rec.nearPublicKey
|
|
10687
|
-
}
|
|
10781
|
+
credentialId: rec.credential.rawId,
|
|
10782
|
+
credentialPublicKey,
|
|
10783
|
+
transports: ["internal"],
|
|
10784
|
+
name: `Device ${rec.deviceNumber} Passkey for ${rec.accountId.split(".")[0]}`,
|
|
10785
|
+
registered: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10786
|
+
syncedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10787
|
+
vrfPublicKey: rec.vrfPublicKey
|
|
10688
10788
|
});
|
|
10789
|
+
console.log("[EmailRecoveryFlow] Locally persisted recovered authenticator for immediate use.");
|
|
10689
10790
|
} catch (e) {
|
|
10690
|
-
|
|
10691
|
-
await this.options?.afterCall?.(false);
|
|
10692
|
-
throw err$1;
|
|
10791
|
+
console.error("[EmailRecoveryFlow] Failed to locally persist authenticator (critical for immediate export):", e);
|
|
10693
10792
|
}
|
|
10694
10793
|
}
|
|
10695
|
-
async
|
|
10794
|
+
async markCompleteAndClearPending(rec) {
|
|
10795
|
+
rec.status = "complete";
|
|
10796
|
+
await this.savePending(rec);
|
|
10797
|
+
await this.clearPending(rec.accountId, rec.nearPublicKey);
|
|
10798
|
+
}
|
|
10799
|
+
async assertVrfActiveForAccount(accountId, message) {
|
|
10800
|
+
const vrfStatus = await this.context.webAuthnManager.checkVrfStatus();
|
|
10801
|
+
const vrfActiveForAccount = vrfStatus.active && vrfStatus.nearAccountId && String(vrfStatus.nearAccountId) === String(accountId);
|
|
10802
|
+
if (!vrfActiveForAccount) throw new Error(message);
|
|
10803
|
+
}
|
|
10804
|
+
async finalizeLocalLoginState(accountId, deviceNumber) {
|
|
10805
|
+
const { webAuthnManager } = this.context;
|
|
10806
|
+
await webAuthnManager.setLastUser(accountId, deviceNumber);
|
|
10807
|
+
await webAuthnManager.initializeCurrentUser(accountId, this.context.nearClient);
|
|
10696
10808
|
try {
|
|
10697
|
-
this.
|
|
10698
|
-
|
|
10699
|
-
|
|
10700
|
-
|
|
10701
|
-
|
|
10702
|
-
|
|
10809
|
+
await getLoginSession(this.context, accountId);
|
|
10810
|
+
} catch {}
|
|
10811
|
+
}
|
|
10812
|
+
async tryShamirUnlock(rec, accountId, deviceNumber) {
|
|
10813
|
+
if (!rec.serverEncryptedVrfKeypair || !rec.serverEncryptedVrfKeypair.serverKeyId || !this.context.configs.vrfWorkerConfigs?.shamir3pass?.relayServerUrl) return false;
|
|
10814
|
+
try {
|
|
10815
|
+
const { webAuthnManager } = this.context;
|
|
10816
|
+
const unlockResult = await webAuthnManager.shamir3PassDecryptVrfKeypair({
|
|
10817
|
+
nearAccountId: accountId,
|
|
10818
|
+
kek_s_b64u: rec.serverEncryptedVrfKeypair.kek_s_b64u,
|
|
10819
|
+
ciphertextVrfB64u: rec.serverEncryptedVrfKeypair.ciphertextVrfB64u,
|
|
10820
|
+
serverKeyId: rec.serverEncryptedVrfKeypair.serverKeyId
|
|
10703
10821
|
});
|
|
10822
|
+
if (!unlockResult.success) return false;
|
|
10823
|
+
await this.assertVrfActiveForAccount(accountId, "VRF session inactive after Shamir3Pass unlock");
|
|
10824
|
+
await this.finalizeLocalLoginState(accountId, deviceNumber);
|
|
10825
|
+
return true;
|
|
10826
|
+
} catch (err$1) {
|
|
10827
|
+
console.warn("[EmailRecoveryFlow] Shamir 3-pass unlock failed, falling back to TouchID", err$1);
|
|
10828
|
+
return false;
|
|
10829
|
+
}
|
|
10830
|
+
}
|
|
10831
|
+
async tryTouchIdUnlock(rec, accountId, deviceNumber) {
|
|
10832
|
+
try {
|
|
10704
10833
|
const { webAuthnManager } = this.context;
|
|
10705
|
-
const accountId = toAccountId(rec.accountId);
|
|
10706
|
-
const deviceNumber = parseDeviceNumber(rec.deviceNumber, { min: 1 });
|
|
10707
|
-
if (deviceNumber === null) throw new Error(`Invalid deviceNumber for auto-login: ${String(rec.deviceNumber)}`);
|
|
10708
|
-
if (rec.serverEncryptedVrfKeypair && rec.serverEncryptedVrfKeypair.serverKeyId && this.context.configs.vrfWorkerConfigs?.shamir3pass?.relayServerUrl) try {
|
|
10709
|
-
const unlockResult = await webAuthnManager.shamir3PassDecryptVrfKeypair({
|
|
10710
|
-
nearAccountId: accountId,
|
|
10711
|
-
kek_s_b64u: rec.serverEncryptedVrfKeypair.kek_s_b64u,
|
|
10712
|
-
ciphertextVrfB64u: rec.serverEncryptedVrfKeypair.ciphertextVrfB64u,
|
|
10713
|
-
serverKeyId: rec.serverEncryptedVrfKeypair.serverKeyId
|
|
10714
|
-
});
|
|
10715
|
-
if (unlockResult.success) {
|
|
10716
|
-
const vrfStatus$1 = await webAuthnManager.checkVrfStatus();
|
|
10717
|
-
const vrfActiveForAccount$1 = vrfStatus$1.active && vrfStatus$1.nearAccountId && String(vrfStatus$1.nearAccountId) === String(accountId);
|
|
10718
|
-
if (!vrfActiveForAccount$1) throw new Error("VRF session inactive after Shamir3Pass unlock");
|
|
10719
|
-
await webAuthnManager.setLastUser(accountId, deviceNumber);
|
|
10720
|
-
await webAuthnManager.initializeCurrentUser(accountId, this.context.nearClient);
|
|
10721
|
-
try {
|
|
10722
|
-
await getLoginSession(this.context, accountId);
|
|
10723
|
-
} catch {}
|
|
10724
|
-
this.emit({
|
|
10725
|
-
step: 5,
|
|
10726
|
-
phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
|
|
10727
|
-
status: EmailRecoveryStatus.SUCCESS,
|
|
10728
|
-
message: `Welcome ${accountId}`,
|
|
10729
|
-
data: { autoLogin: "success" }
|
|
10730
|
-
});
|
|
10731
|
-
return;
|
|
10732
|
-
}
|
|
10733
|
-
} catch (err$1) {
|
|
10734
|
-
console.warn("[EmailRecoveryFlow] Shamir 3-pass unlock failed, falling back to TouchID", err$1);
|
|
10735
|
-
}
|
|
10736
10834
|
const authChallenge = createRandomVRFChallenge();
|
|
10737
10835
|
const storedCredentialId = String(rec.credential?.rawId || rec.credential?.id || "").trim();
|
|
10738
10836
|
const credentialIds = storedCredentialId ? [storedCredentialId] : [];
|
|
@@ -10742,43 +10840,104 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10742
10840
|
challenge: authChallenge,
|
|
10743
10841
|
credentialIds: credentialIds.length > 0 ? credentialIds : authenticators.map((a) => a.credentialId)
|
|
10744
10842
|
});
|
|
10745
|
-
if (storedCredentialId && authCredential.rawId !== storedCredentialId)
|
|
10843
|
+
if (storedCredentialId && authCredential.rawId !== storedCredentialId) return {
|
|
10844
|
+
success: false,
|
|
10845
|
+
reason: "Wrong passkey selected during recovery auto-login; please use the newly recovered passkey."
|
|
10846
|
+
};
|
|
10746
10847
|
const vrfUnlockResult = await webAuthnManager.unlockVRFKeypair({
|
|
10747
10848
|
nearAccountId: accountId,
|
|
10748
10849
|
encryptedVrfKeypair: rec.encryptedVrfKeypair,
|
|
10749
10850
|
credential: authCredential
|
|
10750
10851
|
});
|
|
10751
|
-
if (!vrfUnlockResult.success)
|
|
10752
|
-
|
|
10753
|
-
|
|
10754
|
-
|
|
10755
|
-
await
|
|
10756
|
-
await
|
|
10757
|
-
|
|
10758
|
-
await getLoginSession(this.context, accountId);
|
|
10759
|
-
} catch {}
|
|
10760
|
-
this.emit({
|
|
10761
|
-
step: 5,
|
|
10762
|
-
phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
|
|
10763
|
-
status: EmailRecoveryStatus.SUCCESS,
|
|
10764
|
-
message: `Welcome ${accountId}`,
|
|
10765
|
-
data: { autoLogin: "success" }
|
|
10766
|
-
});
|
|
10852
|
+
if (!vrfUnlockResult.success) return {
|
|
10853
|
+
success: false,
|
|
10854
|
+
reason: vrfUnlockResult.error || "VRF unlock failed during auto-login"
|
|
10855
|
+
};
|
|
10856
|
+
await this.assertVrfActiveForAccount(accountId, "VRF session inactive after TouchID unlock");
|
|
10857
|
+
await this.finalizeLocalLoginState(accountId, deviceNumber);
|
|
10858
|
+
return { success: true };
|
|
10767
10859
|
} catch (err$1) {
|
|
10768
|
-
|
|
10769
|
-
|
|
10770
|
-
|
|
10771
|
-
}
|
|
10860
|
+
return {
|
|
10861
|
+
success: false,
|
|
10862
|
+
reason: err$1?.message || String(err$1)
|
|
10863
|
+
};
|
|
10864
|
+
}
|
|
10865
|
+
}
|
|
10866
|
+
async handleAutoLoginFailure(reason, err$1) {
|
|
10867
|
+
console.warn("[EmailRecoveryFlow] Auto-login failed after recovery", err$1 ?? reason);
|
|
10868
|
+
try {
|
|
10869
|
+
await this.context.webAuthnManager.clearVrfSession();
|
|
10870
|
+
} catch {}
|
|
10871
|
+
return {
|
|
10872
|
+
success: false,
|
|
10873
|
+
reason
|
|
10874
|
+
};
|
|
10875
|
+
}
|
|
10876
|
+
async finalizeRegistration(rec) {
|
|
10877
|
+
this.phase = EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION;
|
|
10878
|
+
this.emit({
|
|
10879
|
+
step: 5,
|
|
10880
|
+
phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
|
|
10881
|
+
status: EmailRecoveryStatus.PROGRESS,
|
|
10882
|
+
message: "Finalizing email recovery registration...",
|
|
10883
|
+
data: {
|
|
10884
|
+
accountId: rec.accountId,
|
|
10885
|
+
nearPublicKey: rec.nearPublicKey
|
|
10886
|
+
}
|
|
10887
|
+
});
|
|
10888
|
+
try {
|
|
10889
|
+
const { nonceManager, accountId } = this.initializeNonceManager(rec);
|
|
10890
|
+
const signedTx = await this.signRegistrationTx(rec, accountId);
|
|
10891
|
+
const txHash = await this.broadcastRegistrationTxAndWaitFinal(rec, signedTx);
|
|
10892
|
+
if (!txHash) console.warn("[EmailRecoveryFlow] Registration transaction confirmed without hash; continuing local persistence");
|
|
10893
|
+
await this.persistRecoveredUserData(rec, accountId);
|
|
10894
|
+
await this.syncAuthenticatorsBestEffort(accountId);
|
|
10895
|
+
await this.persistAuthenticatorBestEffort(rec, accountId);
|
|
10896
|
+
await this.setLastUserBestEffort(accountId, rec.deviceNumber);
|
|
10897
|
+
await this.updateNonceBestEffort(nonceManager, signedTx);
|
|
10898
|
+
this.emitAutoLoginEvent(EmailRecoveryStatus.PROGRESS, "Attempting auto-login with recovered device...", { autoLogin: "progress" });
|
|
10899
|
+
const autoLoginResult = await this.attemptAutoLogin(rec);
|
|
10900
|
+
if (autoLoginResult.success) this.emitAutoLoginEvent(EmailRecoveryStatus.SUCCESS, `Welcome ${accountId}`, { autoLogin: "success" });
|
|
10901
|
+
else this.emitAutoLoginEvent(EmailRecoveryStatus.ERROR, "Auto-login failed; please log in manually on this device.", {
|
|
10902
|
+
error: autoLoginResult.reason,
|
|
10903
|
+
autoLogin: "error"
|
|
10904
|
+
});
|
|
10905
|
+
await this.markCompleteAndClearPending(rec);
|
|
10906
|
+
this.phase = EmailRecoveryPhase.STEP_6_COMPLETE;
|
|
10772
10907
|
this.emit({
|
|
10773
|
-
step:
|
|
10774
|
-
phase: EmailRecoveryPhase.
|
|
10775
|
-
status: EmailRecoveryStatus.
|
|
10776
|
-
message: "
|
|
10908
|
+
step: 6,
|
|
10909
|
+
phase: EmailRecoveryPhase.STEP_6_COMPLETE,
|
|
10910
|
+
status: EmailRecoveryStatus.SUCCESS,
|
|
10911
|
+
message: "Email recovery completed successfully",
|
|
10777
10912
|
data: {
|
|
10778
|
-
|
|
10779
|
-
|
|
10913
|
+
accountId: rec.accountId,
|
|
10914
|
+
nearPublicKey: rec.nearPublicKey
|
|
10780
10915
|
}
|
|
10781
10916
|
});
|
|
10917
|
+
} catch (e) {
|
|
10918
|
+
const err$1 = this.emitError(5, e?.message || "Email recovery finalization failed");
|
|
10919
|
+
await this.options?.afterCall?.(false);
|
|
10920
|
+
throw err$1;
|
|
10921
|
+
}
|
|
10922
|
+
}
|
|
10923
|
+
async attemptAutoLogin(rec) {
|
|
10924
|
+
try {
|
|
10925
|
+
const accountId = toAccountId(rec.accountId);
|
|
10926
|
+
const deviceNumber = parseDeviceNumber(rec.deviceNumber, { min: 1 });
|
|
10927
|
+
if (deviceNumber === null) return this.handleAutoLoginFailure(`Invalid deviceNumber for auto-login: ${String(rec.deviceNumber)}`);
|
|
10928
|
+
const shamirUnlocked = await this.tryShamirUnlock(rec, accountId, deviceNumber);
|
|
10929
|
+
if (shamirUnlocked) return {
|
|
10930
|
+
success: true,
|
|
10931
|
+
method: "shamir"
|
|
10932
|
+
};
|
|
10933
|
+
const touchIdResult = await this.tryTouchIdUnlock(rec, accountId, deviceNumber);
|
|
10934
|
+
if (touchIdResult.success) return {
|
|
10935
|
+
success: true,
|
|
10936
|
+
method: "touchid"
|
|
10937
|
+
};
|
|
10938
|
+
return this.handleAutoLoginFailure(touchIdResult.reason || "Auto-login failed");
|
|
10939
|
+
} catch (err$1) {
|
|
10940
|
+
return this.handleAutoLoginFailure(err$1?.message || String(err$1), err$1);
|
|
10782
10941
|
}
|
|
10783
10942
|
}
|
|
10784
10943
|
};
|
|
@@ -10793,6 +10952,7 @@ init_IndexedDBManager();
|
|
|
10793
10952
|
init_actions();
|
|
10794
10953
|
init_errors();
|
|
10795
10954
|
init_rpcCalls();
|
|
10955
|
+
init_EmailRecovery();
|
|
10796
10956
|
init_defaultConfigs();
|
|
10797
10957
|
let warnedAboutSameOriginWallet = false;
|
|
10798
10958
|
/**
|
|
@@ -10849,7 +11009,7 @@ var TatchiPasskey = class {
|
|
|
10849
11009
|
} catch {}
|
|
10850
11010
|
if (!this.iframeRouter) {
|
|
10851
11011
|
if (!this.walletIframeInitInFlight) this.walletIframeInitInFlight = (async () => {
|
|
10852
|
-
const { WalletIframeRouter } = await import("./router-
|
|
11012
|
+
const { WalletIframeRouter } = await import("./router-DuGYOd3G.js");
|
|
10853
11013
|
this.iframeRouter = new WalletIframeRouter({
|
|
10854
11014
|
walletOrigin,
|
|
10855
11015
|
servicePath: walletIframeConfig?.walletServicePath || "/wallet-service",
|