@tatchi-xyz/sdk 0.20.0 → 0.21.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/TatchiPasskey/emailRecovery.js +67 -45
- package/dist/cjs/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/index.js +2 -1
- package/dist/cjs/core/TatchiPasskey/index.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/linkDevice.js +2 -1
- package/dist/cjs/core/TatchiPasskey/linkDevice.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/scanDevice.js +5 -3
- package/dist/cjs/core/TatchiPasskey/scanDevice.js.map +1 -1
- package/dist/cjs/core/WalletIframe/client/router.js +1 -1
- package/dist/cjs/core/WalletIframe/client/router.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +3 -4
- package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
- package/dist/cjs/core/defaultConfigs.js +3 -7
- package/dist/cjs/core/defaultConfigs.js.map +1 -1
- package/dist/cjs/core/nearCrypto.js +29 -5
- package/dist/cjs/core/nearCrypto.js.map +1 -1
- package/dist/cjs/core/rpcCalls.js +56 -26
- package/dist/cjs/core/rpcCalls.js.map +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{LinkedDevicesModal-BCrFe5p3.css → LinkedDevicesModal-BRtht0XI.css} +1 -1
- package/dist/{esm/react/components/AccountMenuButton/LinkedDevicesModal-BCrFe5p3.css.map → cjs/react/components/AccountMenuButton/LinkedDevicesModal-BRtht0XI.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{ProfileDropdown-CRJrtxDb.css → ProfileDropdown-BG_6hcim.css} +1 -1
- package/dist/{esm/react/components/AccountMenuButton/ProfileDropdown-CRJrtxDb.css.map → cjs/react/components/AccountMenuButton/ProfileDropdown-BG_6hcim.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-DXFRw8ND.css → Web3AuthProfileButton-k8_FAYFq.css} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-DXFRw8ND.css.map → Web3AuthProfileButton-k8_FAYFq.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-DNgbAK_i.css → TouchIcon-C-RcGfr5.css} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-DNgbAK_i.css.map → TouchIcon-C-RcGfr5.css.map} +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-DRwSoF8q.css → PasskeyAuthMenu-DKMiLeT9.css} +59 -4
- package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-DRwSoF8q.css.map → PasskeyAuthMenu-DKMiLeT9.css.map} +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/adapters/tatchi.js +1 -0
- package/dist/cjs/react/components/PasskeyAuthMenu/adapters/tatchi.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/client.js +30 -8
- package/dist/cjs/react/components/PasskeyAuthMenu/client.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/controller/useSDKEvents.js +22 -0
- package/dist/cjs/react/components/PasskeyAuthMenu/controller/useSDKEvents.js.map +1 -0
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js +17 -4
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js +254 -140
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js.map +1 -1
- package/dist/cjs/react/components/{ShowQRCode-CL4gsszN.css → ShowQRCode-CB0UCQ_h.css} +1 -1
- package/dist/cjs/react/components/{ShowQRCode-CL4gsszN.css.map → ShowQRCode-CB0UCQ_h.css.map} +1 -1
- package/dist/cjs/react/context/useSDKFlowRuntime.js +183 -0
- package/dist/cjs/react/context/useSDKFlowRuntime.js.map +1 -0
- package/dist/cjs/react/context/useTatchiContextValue.js +24 -15
- package/dist/cjs/react/context/useTatchiContextValue.js.map +1 -1
- package/dist/cjs/react/context/useTatchiWithSdkFlow.js +96 -0
- package/dist/cjs/react/context/useTatchiWithSdkFlow.js.map +1 -0
- package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js +1 -0
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js +67 -45
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js +2 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/linkDevice.js +2 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/linkDevice.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/scanDevice.js +5 -3
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/scanDevice.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js +1 -1
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +3 -4
- package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/defaultConfigs.js +3 -7
- package/dist/cjs/react/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/nearCrypto.js +29 -5
- package/dist/cjs/react/sdk/src/core/nearCrypto.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/rpcCalls.js +56 -26
- package/dist/cjs/react/sdk/src/core/rpcCalls.js.map +1 -1
- package/dist/cjs/server/email-recovery/emailParsers.js +2 -1
- package/dist/cjs/server/email-recovery/emailParsers.js.map +1 -1
- package/dist/cjs/server/email-recovery/index.js +6 -6
- package/dist/cjs/server/email-recovery/index.js.map +1 -1
- package/dist/cjs/server/email-recovery/rpcCalls.js +22 -3
- package/dist/cjs/server/email-recovery/rpcCalls.js.map +1 -1
- package/dist/cjs/server/router/cloudflare.js +8 -3
- package/dist/cjs/server/router/cloudflare.js.map +1 -1
- package/dist/cjs/server/router/express.js.map +1 -1
- package/dist/cjs/server/sdk/src/core/defaultConfigs.js +2 -4
- package/dist/cjs/server/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/cjs/server/sdk/src/core/nearCrypto.js +26 -7
- package/dist/cjs/server/sdk/src/core/nearCrypto.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/emailRecovery.js +67 -45
- 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/linkDevice.js +2 -1
- package/dist/esm/core/TatchiPasskey/linkDevice.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/scanDevice.js +5 -3
- package/dist/esm/core/TatchiPasskey/scanDevice.js.map +1 -1
- package/dist/esm/core/WalletIframe/client/router.js +1 -1
- package/dist/esm/core/WalletIframe/client/router.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +2 -3
- package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
- package/dist/esm/core/defaultConfigs.js +3 -7
- package/dist/esm/core/defaultConfigs.js.map +1 -1
- package/dist/esm/core/nearCrypto.js +24 -6
- package/dist/esm/core/nearCrypto.js.map +1 -1
- package/dist/esm/core/rpcCalls.js +56 -26
- package/dist/esm/core/rpcCalls.js.map +1 -1
- package/dist/esm/react/components/AccountMenuButton/{LinkedDevicesModal-BCrFe5p3.css → LinkedDevicesModal-BRtht0XI.css} +1 -1
- package/dist/{cjs/react/components/AccountMenuButton/LinkedDevicesModal-BCrFe5p3.css.map → esm/react/components/AccountMenuButton/LinkedDevicesModal-BRtht0XI.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{ProfileDropdown-CRJrtxDb.css → ProfileDropdown-BG_6hcim.css} +1 -1
- package/dist/{cjs/react/components/AccountMenuButton/ProfileDropdown-CRJrtxDb.css.map → esm/react/components/AccountMenuButton/ProfileDropdown-BG_6hcim.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-DXFRw8ND.css → Web3AuthProfileButton-k8_FAYFq.css} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-DXFRw8ND.css.map → Web3AuthProfileButton-k8_FAYFq.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-DNgbAK_i.css → TouchIcon-C-RcGfr5.css} +1 -1
- package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-DNgbAK_i.css.map → TouchIcon-C-RcGfr5.css.map} +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-DRwSoF8q.css → PasskeyAuthMenu-DKMiLeT9.css} +59 -4
- package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-DRwSoF8q.css.map → PasskeyAuthMenu-DKMiLeT9.css.map} +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/adapters/tatchi.js +1 -0
- package/dist/esm/react/components/PasskeyAuthMenu/adapters/tatchi.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/client.js +30 -8
- package/dist/esm/react/components/PasskeyAuthMenu/client.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/controller/useSDKEvents.js +20 -0
- package/dist/esm/react/components/PasskeyAuthMenu/controller/useSDKEvents.js.map +1 -0
- package/dist/esm/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js +17 -4
- package/dist/esm/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js +254 -140
- package/dist/esm/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js.map +1 -1
- package/dist/esm/react/components/{ShowQRCode-CL4gsszN.css → ShowQRCode-CB0UCQ_h.css} +1 -1
- package/dist/esm/react/components/{ShowQRCode-CL4gsszN.css.map → ShowQRCode-CB0UCQ_h.css.map} +1 -1
- package/dist/esm/react/context/useSDKFlowRuntime.js +181 -0
- package/dist/esm/react/context/useSDKFlowRuntime.js.map +1 -0
- package/dist/esm/react/context/useTatchiContextValue.js +25 -16
- package/dist/esm/react/context/useTatchiContextValue.js.map +1 -1
- package/dist/esm/react/context/useTatchiWithSdkFlow.js +94 -0
- package/dist/esm/react/context/useTatchiWithSdkFlow.js.map +1 -0
- package/dist/esm/react/sdk/src/core/EmailRecovery/index.js +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js +67 -45
- 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/linkDevice.js +2 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/linkDevice.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/scanDevice.js +5 -3
- package/dist/esm/react/sdk/src/core/TatchiPasskey/scanDevice.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js +1 -1
- package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +2 -3
- package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
- package/dist/esm/react/sdk/src/core/defaultConfigs.js +3 -7
- package/dist/esm/react/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/esm/react/sdk/src/core/nearCrypto.js +24 -6
- package/dist/esm/react/sdk/src/core/nearCrypto.js.map +1 -1
- package/dist/esm/react/sdk/src/core/rpcCalls.js +56 -26
- package/dist/esm/react/sdk/src/core/rpcCalls.js.map +1 -1
- package/dist/esm/react/styles/styles.css +58 -3
- package/dist/esm/sdk/{defaultConfigs-DpslkAQd.js → defaultConfigs-CfQDV-ya.js} +3 -7
- package/dist/esm/sdk/{getDeviceNumber-fXizNGQl.js → getDeviceNumber-BpernPnM.js} +4 -8
- package/dist/esm/sdk/getDeviceNumber-BpernPnM.js.map +1 -0
- package/dist/esm/sdk/offline-export-app.js +23 -6
- package/dist/esm/sdk/offline-export-app.js.map +1 -1
- package/dist/esm/sdk/{router-DuGYOd3G.js → router-BWtacLJg.js} +1 -1
- package/dist/esm/sdk/{rpcCalls-BQrJMTdg.js → rpcCalls-CYGJSCgm.js} +3 -3
- package/dist/esm/sdk/{rpcCalls-YVeUVMk2.js → rpcCalls-DZZSa-sk.js} +57 -27
- package/dist/esm/sdk/{transactions-bqaAwL4k.js → transactions-Cn9xTWlK.js} +2 -2
- package/dist/esm/sdk/{transactions-bqaAwL4k.js.map → transactions-Cn9xTWlK.js.map} +1 -1
- package/dist/esm/sdk/{transactions-BalIhtJ9.js → transactions-DfdwDQCn.js} +1 -1
- package/dist/esm/sdk/wallet-iframe-host.js +549 -557
- package/dist/esm/server/email-recovery/emailParsers.js +3 -1
- package/dist/esm/server/email-recovery/emailParsers.js.map +1 -1
- package/dist/esm/server/email-recovery/index.js +6 -6
- package/dist/esm/server/email-recovery/index.js.map +1 -1
- package/dist/esm/server/email-recovery/rpcCalls.js +22 -3
- package/dist/esm/server/email-recovery/rpcCalls.js.map +1 -1
- package/dist/esm/server/router/cloudflare.js +8 -3
- package/dist/esm/server/router/cloudflare.js.map +1 -1
- package/dist/esm/server/router/express.js.map +1 -1
- package/dist/esm/server/sdk/src/core/defaultConfigs.js +2 -4
- package/dist/esm/server/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/esm/server/sdk/src/core/nearCrypto.js +26 -8
- package/dist/esm/server/sdk/src/core/nearCrypto.js.map +1 -1
- package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker_bg.wasm +0 -0
- package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts +5 -4
- package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/index.d.ts +1 -1
- package/dist/types/src/core/TatchiPasskey/index.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/scanDevice.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/TatchiPasskeyIframe.d.ts +1 -1
- package/dist/types/src/core/WalletIframe/TatchiPasskeyIframe.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/client/router.d.ts +1 -1
- package/dist/types/src/core/WalletIframe/client/router.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/shared/messages.d.ts +1 -1
- package/dist/types/src/core/WalletIframe/shared/messages.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.d.ts +2 -1
- package/dist/types/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.d.ts.map +1 -1
- package/dist/types/src/core/defaultConfigs.d.ts.map +1 -1
- package/dist/types/src/core/nearCrypto.d.ts +14 -0
- package/dist/types/src/core/nearCrypto.d.ts.map +1 -1
- package/dist/types/src/core/rpcCalls.d.ts +11 -8
- package/dist/types/src/core/rpcCalls.d.ts.map +1 -1
- package/dist/types/src/core/types/tatchi.d.ts +0 -4
- package/dist/types/src/core/types/tatchi.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/adapters/tatchi.d.ts +2 -0
- package/dist/types/src/react/components/PasskeyAuthMenu/adapters/tatchi.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/client.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/controller/useSDKEvents.d.ts +10 -0
- package/dist/types/src/react/components/PasskeyAuthMenu/controller/useSDKEvents.d.ts.map +1 -0
- package/dist/types/src/react/components/PasskeyAuthMenu/types.d.ts +8 -3
- package/dist/types/src/react/components/PasskeyAuthMenu/types.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/ui/ContentSwitcher.d.ts +2 -0
- package/dist/types/src/react/components/PasskeyAuthMenu/ui/ContentSwitcher.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.d.ts +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.d.ts.map +1 -1
- package/dist/types/src/react/context/useSDKFlowRuntime.d.ts +10 -0
- package/dist/types/src/react/context/useSDKFlowRuntime.d.ts.map +1 -0
- package/dist/types/src/react/context/useTatchiContextValue.d.ts.map +1 -1
- package/dist/types/src/react/context/useTatchiWithSdkFlow.d.ts +9 -0
- package/dist/types/src/react/context/useTatchiWithSdkFlow.d.ts.map +1 -0
- package/dist/types/src/react/types.d.ts +31 -0
- package/dist/types/src/react/types.d.ts.map +1 -1
- package/dist/types/src/server/email-recovery/emailParsers.d.ts.map +1 -1
- package/dist/types/src/server/email-recovery/index.d.ts +5 -6
- package/dist/types/src/server/email-recovery/index.d.ts.map +1 -1
- package/dist/types/src/server/email-recovery/rpcCalls.d.ts +1 -0
- package/dist/types/src/server/email-recovery/rpcCalls.d.ts.map +1 -1
- package/dist/types/src/server/router/cloudflare-adaptor.d.ts.map +1 -1
- package/dist/workers/wasm_vrf_worker_bg.wasm +0 -0
- package/package.json +1 -1
- package/dist/esm/sdk/getDeviceNumber-fXizNGQl.js.map +0 -1
|
@@ -6,7 +6,9 @@ import { createRandomVRFChallenge, init_vrf_worker } from "../types/vrf-worker.j
|
|
|
6
6
|
import { errorMessage, init_errors } from "../../utils/errors.js";
|
|
7
7
|
import { EmailRecoveryPhase, EmailRecoveryStatus, init_sdkSentEvents } from "../types/sdkSentEvents.js";
|
|
8
8
|
import { DEFAULT_WAIT_STATUS, init_rpc } from "../types/rpc.js";
|
|
9
|
+
import { getEmailRecoveryAttempt, init_rpcCalls } from "../rpcCalls.js";
|
|
9
10
|
import { init_getDeviceNumber, parseDeviceNumber } from "../WebAuthnManager/SignerWorkerManager/getDeviceNumber.js";
|
|
11
|
+
import { ensureEd25519Prefix, init_nearCrypto } from "../nearCrypto.js";
|
|
10
12
|
import { getLoginSession, init_login } from "./login.js";
|
|
11
13
|
import { EmailRecoveryPendingStore } from "../EmailRecovery/emailRecoveryPendingStore.js";
|
|
12
14
|
import { init_EmailRecovery, parseLinkDeviceRegisterUserResponse } from "../EmailRecovery/index.js";
|
|
@@ -25,16 +27,12 @@ function getEmailRecoveryConfig(configs) {
|
|
|
25
27
|
const maxPollingDurationMs = Number(relayerEmailCfg.maxPollingDurationMs);
|
|
26
28
|
const pendingTtlMs = Number(relayerEmailCfg.pendingTtlMs);
|
|
27
29
|
const mailtoAddress = String(relayerEmailCfg.mailtoAddress);
|
|
28
|
-
const dkimVerifierAccountId = String(relayerEmailCfg.dkimVerifierAccountId);
|
|
29
|
-
const verificationViewMethod = String(relayerEmailCfg.verificationViewMethod);
|
|
30
30
|
return {
|
|
31
31
|
minBalanceYocto,
|
|
32
32
|
pollingIntervalMs,
|
|
33
33
|
maxPollingDurationMs,
|
|
34
34
|
pendingTtlMs,
|
|
35
|
-
mailtoAddress
|
|
36
|
-
dkimVerifierAccountId,
|
|
37
|
-
verificationViewMethod
|
|
35
|
+
mailtoAddress
|
|
38
36
|
};
|
|
39
37
|
}
|
|
40
38
|
function generateEmailRecoveryRequestId() {
|
|
@@ -59,6 +57,8 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
59
57
|
init_login();
|
|
60
58
|
init_EmailRecovery();
|
|
61
59
|
init_emailRecovery$1();
|
|
60
|
+
init_rpcCalls();
|
|
61
|
+
init_nearCrypto();
|
|
62
62
|
EmailRecoveryFlow = class {
|
|
63
63
|
context;
|
|
64
64
|
options;
|
|
@@ -151,10 +151,9 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
151
151
|
await this.fail(1, errorMessage(err) || "Failed to fetch account balance for recovery");
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
|
-
|
|
154
|
+
getCanonicalRecoveryEmail(recoveryEmail) {
|
|
155
155
|
const canonicalEmail = String(recoveryEmail || "").trim().toLowerCase();
|
|
156
|
-
|
|
157
|
-
return canonicalEmail;
|
|
156
|
+
return canonicalEmail || void 0;
|
|
158
157
|
}
|
|
159
158
|
async getNextDeviceNumberFromContract(nearAccountId) {
|
|
160
159
|
try {
|
|
@@ -212,7 +211,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
212
211
|
message: "New device key created; please send the recovery email from your registered address.",
|
|
213
212
|
data: {
|
|
214
213
|
accountId: rec.accountId,
|
|
215
|
-
recoveryEmail: rec.recoveryEmail,
|
|
214
|
+
...rec.recoveryEmail ? { recoveryEmail: rec.recoveryEmail } : {},
|
|
216
215
|
nearPublicKey: rec.nearPublicKey,
|
|
217
216
|
requestId: rec.requestId,
|
|
218
217
|
mailtoUrl
|
|
@@ -228,44 +227,57 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
228
227
|
data
|
|
229
228
|
});
|
|
230
229
|
}
|
|
231
|
-
async
|
|
232
|
-
const { dkimVerifierAccountId, verificationViewMethod } = this.getConfig();
|
|
233
|
-
if (!dkimVerifierAccountId) return null;
|
|
230
|
+
async checkViaEmailRecovererAttempt(rec) {
|
|
234
231
|
try {
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
if (!result) return {
|
|
232
|
+
const attempt = await getEmailRecoveryAttempt(this.context.nearClient, rec.accountId, rec.requestId);
|
|
233
|
+
if (!attempt) return {
|
|
238
234
|
completed: false,
|
|
239
|
-
success: false
|
|
235
|
+
success: false,
|
|
236
|
+
missing: true
|
|
240
237
|
};
|
|
241
|
-
if (
|
|
242
|
-
|
|
243
|
-
|
|
238
|
+
if (attempt.request_id && attempt.request_id !== rec.requestId) return {
|
|
239
|
+
completed: true,
|
|
240
|
+
success: false,
|
|
241
|
+
errorMessage: "Email recovery attempt request_id does not match requested requestId."
|
|
242
|
+
};
|
|
243
|
+
if (attempt.new_public_key && attempt.new_public_key !== rec.nearPublicKey) {
|
|
244
|
+
const expected = ensureEd25519Prefix(rec.nearPublicKey);
|
|
245
|
+
const actual = ensureEd25519Prefix(attempt.new_public_key);
|
|
246
|
+
if (actual === expected) {} else return {
|
|
244
247
|
completed: true,
|
|
245
248
|
success: false,
|
|
246
|
-
errorMessage:
|
|
247
|
-
transactionHash: result.transaction_hash
|
|
249
|
+
errorMessage: `Email recovery new_public_key mismatch for request ${rec.requestId}. Expected ${expected}; got ${actual}. This usually means the recovery email you sent was generated for a different device/attempt.`
|
|
248
250
|
};
|
|
249
251
|
}
|
|
250
|
-
|
|
252
|
+
const normalized = attempt.status.toLowerCase();
|
|
253
|
+
if (normalized === "complete" || normalized === "completed") return {
|
|
251
254
|
completed: true,
|
|
252
|
-
success:
|
|
253
|
-
errorMessage: "Email verification account_id does not match requested account.",
|
|
254
|
-
transactionHash: result.transaction_hash
|
|
255
|
+
success: true
|
|
255
256
|
};
|
|
256
|
-
if (
|
|
257
|
+
if (normalized.includes("failed")) return {
|
|
257
258
|
completed: true,
|
|
258
259
|
success: false,
|
|
259
|
-
errorMessage:
|
|
260
|
-
transactionHash: result.transaction_hash
|
|
260
|
+
errorMessage: attempt.error || `Email recovery failed (${attempt.status || "unknown status"})`
|
|
261
261
|
};
|
|
262
262
|
return {
|
|
263
|
-
completed:
|
|
264
|
-
success:
|
|
265
|
-
transactionHash: result.transaction_hash
|
|
263
|
+
completed: false,
|
|
264
|
+
success: false
|
|
266
265
|
};
|
|
267
266
|
} catch (err) {
|
|
268
|
-
console.warn("[EmailRecoveryFlow]
|
|
267
|
+
console.warn("[EmailRecoveryFlow] get_recovery_attempt view failed; will retry", err);
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async isRecoveryAccessKeyPresent(rec) {
|
|
272
|
+
try {
|
|
273
|
+
await this.context.nearClient.viewAccessKey(rec.accountId, rec.nearPublicKey);
|
|
274
|
+
return true;
|
|
275
|
+
} catch (err) {
|
|
276
|
+
const kind = typeof err?.kind === "string" ? String(err.kind) : "";
|
|
277
|
+
const short = typeof err?.short === "string" ? String(err.short) : "";
|
|
278
|
+
const msg = typeof err?.message === "string" ? String(err.message) : "";
|
|
279
|
+
if (/AccessKeyDoesNotExist/i.test(kind) || /AccessKeyDoesNotExist/i.test(short) || /access key does not exist/i.test(msg) || /access key .*does not exist/i.test(msg)) return false;
|
|
280
|
+
console.warn("[EmailRecoveryFlow] view_access_key failed while checking recovery key; will retry", err);
|
|
269
281
|
return null;
|
|
270
282
|
}
|
|
271
283
|
}
|
|
@@ -374,7 +386,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
374
386
|
});
|
|
375
387
|
const nearAccountId = await this.assertValidAccountIdOrFail(1, accountId);
|
|
376
388
|
await this.assertSufficientBalance(nearAccountId);
|
|
377
|
-
const canonicalEmail =
|
|
389
|
+
const canonicalEmail = this.getCanonicalRecoveryEmail(recoveryEmail);
|
|
378
390
|
const deviceNumber = await this.getNextDeviceNumberFromContract(nearAccountId);
|
|
379
391
|
this.phase = EmailRecoveryPhase.STEP_2_TOUCH_ID_REGISTRATION;
|
|
380
392
|
this.emit({
|
|
@@ -505,29 +517,39 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
505
517
|
await this.options?.afterCall?.(true, void 0);
|
|
506
518
|
}
|
|
507
519
|
async pollUntilAddKey(rec) {
|
|
508
|
-
const { pollingIntervalMs, maxPollingDurationMs
|
|
509
|
-
if (!dkimVerifierAccountId) {
|
|
510
|
-
const err$1 = this.emitError(4, "Email recovery verification contract (dkimVerifierAccountId) is not configured");
|
|
511
|
-
await this.options?.afterCall?.(false);
|
|
512
|
-
throw err$1;
|
|
513
|
-
}
|
|
520
|
+
const { pollingIntervalMs, maxPollingDurationMs } = this.getConfig();
|
|
514
521
|
this.phase = EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT;
|
|
515
522
|
this.pollingStartedAt = Date.now();
|
|
523
|
+
let sawAttempt = false;
|
|
516
524
|
const pollResult = await this.pollUntil({
|
|
517
525
|
intervalMs: pollingIntervalMs,
|
|
518
526
|
timeoutMs: maxPollingDurationMs,
|
|
519
527
|
isCancelled: () => this.cancelled,
|
|
520
528
|
tick: async ({ elapsedMs, pollCount }) => {
|
|
521
|
-
const verification = await this.
|
|
522
|
-
|
|
523
|
-
|
|
529
|
+
const verification = await this.checkViaEmailRecovererAttempt(rec);
|
|
530
|
+
if (verification && !verification.missing) sawAttempt = true;
|
|
531
|
+
let completed = verification?.completed === true;
|
|
532
|
+
let success = verification?.success === true;
|
|
533
|
+
let errorMessage$1 = verification?.errorMessage;
|
|
534
|
+
let transactionHash;
|
|
535
|
+
if (verification?.missing) {
|
|
536
|
+
const hasKey = await this.isRecoveryAccessKeyPresent(rec);
|
|
537
|
+
if (hasKey === true) {
|
|
538
|
+
completed = true;
|
|
539
|
+
success = true;
|
|
540
|
+
} else if (hasKey === false && sawAttempt) {
|
|
541
|
+
completed = true;
|
|
542
|
+
success = false;
|
|
543
|
+
errorMessage$1 = "Email recovery attempt was cleared on-chain before completion. Please resend the recovery email or restart the flow.";
|
|
544
|
+
} else if (hasKey === null) return { done: false };
|
|
545
|
+
}
|
|
524
546
|
this.emit({
|
|
525
547
|
step: 4,
|
|
526
548
|
phase: EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT,
|
|
527
549
|
status: EmailRecoveryStatus.PROGRESS,
|
|
528
|
-
message: completed && success ? `Email
|
|
550
|
+
message: completed && success ? `Email recovery completed for request ${rec.requestId}; finalizing registration` : `Waiting for email recovery for request ${rec.requestId}`,
|
|
529
551
|
data: this.buildPollingEventData(rec, {
|
|
530
|
-
transactionHash
|
|
552
|
+
transactionHash,
|
|
531
553
|
elapsedMs,
|
|
532
554
|
pollCount
|
|
533
555
|
})
|
|
@@ -537,7 +559,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
537
559
|
done: true,
|
|
538
560
|
value: {
|
|
539
561
|
outcome: "failed",
|
|
540
|
-
errorMessage:
|
|
562
|
+
errorMessage: errorMessage$1 || "Email recovery failed"
|
|
541
563
|
}
|
|
542
564
|
};
|
|
543
565
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emailRecovery.js","names":["err: unknown","errorMessage","elapsedMs","rec: PendingEmailRecovery","e: unknown","err","txResult: FinalExecutionOutcome","txUnknown: unknown","logs: string[]","payload: StoreUserDataPayload"],"sources":["../../../../src/core/TatchiPasskey/emailRecovery.ts"],"sourcesContent":["import type { PasskeyManagerContext } from './index';\nimport { IndexedDBManager } from '../IndexedDBManager';\nimport { validateNearAccountId } from '../../utils/validation';\nimport { errorMessage } from '../../utils/errors';\nimport { toAccountId, type AccountId } from '../types/accountIds';\nimport {\n EmailRecoveryPhase,\n EmailRecoveryStatus,\n type EmailRecoverySSEEvent,\n type EventCallback,\n type AfterCall,\n} from '../types/sdkSentEvents';\nimport type { TatchiConfigs } from '../types/tatchi';\nimport {\n createRandomVRFChallenge,\n type EncryptedVRFKeypair,\n type ServerEncryptedVrfKeypair,\n type VRFChallenge,\n} from '../types/vrf-worker';\nimport type { FinalExecutionOutcome } from '@near-js/types';\nimport type { StoredAuthenticator, WebAuthnRegistrationCredential } from '../types';\nimport type { ConfirmationConfig } from '../types/signer-worker';\nimport { DEFAULT_WAIT_STATUS } from '../types/rpc';\nimport { parseDeviceNumber } from '../WebAuthnManager/SignerWorkerManager/getDeviceNumber';\nimport { getLoginSession } from './login';\nimport type { SignedTransaction } from '../NearClient';\nimport {\n EmailRecoveryPendingStore,\n parseLinkDeviceRegisterUserResponse,\n type PendingStore,\n} from '../EmailRecovery';\nimport { EmailRecoveryError, EmailRecoveryErrorCode } from '../types/emailRecovery';\n\nexport type PendingEmailRecoveryStatus =\n | 'awaiting-email'\n | 'awaiting-add-key'\n | 'finalizing'\n | 'complete'\n | 'error';\n\nexport type PendingEmailRecovery = {\n accountId: AccountId;\n recoveryEmail: string;\n deviceNumber: number;\n nearPublicKey: string;\n requestId: string;\n encryptedVrfKeypair: EncryptedVRFKeypair;\n serverEncryptedVrfKeypair: ServerEncryptedVrfKeypair | null;\n vrfPublicKey: string;\n credential: WebAuthnRegistrationCredential;\n vrfChallenge?: VRFChallenge;\n createdAt: number;\n status: PendingEmailRecoveryStatus;\n};\n\ntype PollTickResult<T> = { done: false } | { done: true; value: T };\n\ntype PollUntilResult<T> =\n | { status: 'completed'; value: T; elapsedMs: number; pollCount: number }\n | { status: 'timedOut'; elapsedMs: number; pollCount: number }\n | { status: 'cancelled'; elapsedMs: number; pollCount: number };\n\ntype VerificationOutcome =\n | { outcome: 'verified' }\n | { outcome: 'failed'; errorMessage: string };\n\ntype AutoLoginResult =\n | { success: true; method: 'shamir' | 'touchid' }\n | { success: false; reason: string };\n\ntype StoreUserDataPayload = Parameters<PasskeyManagerContext['webAuthnManager']['storeUserData']>[0];\n\ntype AccountViewLike = {\n amount: bigint | string;\n locked: bigint | string;\n storage_usage: number | bigint;\n};\n\ntype CollectedRecoveryCredential = {\n credential: WebAuthnRegistrationCredential;\n vrfChallenge?: VRFChallenge;\n};\n\ntype DerivedRecoveryKeys = {\n encryptedVrfKeypair: EncryptedVRFKeypair;\n serverEncryptedVrfKeypair: ServerEncryptedVrfKeypair | null;\n vrfPublicKey: string;\n nearPublicKey: string;\n};\n\nexport interface EmailRecoveryFlowOptions {\n onEvent?: EventCallback<EmailRecoverySSEEvent>;\n onError?: (error: Error) => void;\n afterCall?: AfterCall<void>;\n pendingStore?: PendingStore;\n confirmerText?: { title?: string; body?: string };\n confirmationConfig?: Partial<ConfirmationConfig>;\n}\n\nfunction getEmailRecoveryConfig(configs: TatchiConfigs): {\n minBalanceYocto: string;\n pollingIntervalMs: number;\n maxPollingDurationMs: number;\n pendingTtlMs: number;\n mailtoAddress: string;\n dkimVerifierAccountId: string;\n verificationViewMethod: string;\n} {\n const relayerEmailCfg = configs.relayer.emailRecovery;\n const minBalanceYocto = String(relayerEmailCfg.minBalanceYocto);\n const pollingIntervalMs = Number(relayerEmailCfg.pollingIntervalMs);\n const maxPollingDurationMs = Number(relayerEmailCfg.maxPollingDurationMs);\n const pendingTtlMs = Number(relayerEmailCfg.pendingTtlMs);\n const mailtoAddress = String(relayerEmailCfg.mailtoAddress);\n const dkimVerifierAccountId = String(relayerEmailCfg.dkimVerifierAccountId);\n const verificationViewMethod = String(relayerEmailCfg.verificationViewMethod);\n return {\n minBalanceYocto,\n pollingIntervalMs,\n maxPollingDurationMs,\n pendingTtlMs,\n mailtoAddress,\n dkimVerifierAccountId,\n verificationViewMethod,\n };\n}\n\nexport function generateEmailRecoveryRequestId(): string {\n // 6-character A–Z0–9 identifier, suitable for short-lived correlation.\n const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';\n const length = 6;\n const bytes = new Uint8Array(length);\n (globalThis.crypto || window.crypto).getRandomValues(bytes);\n let out = '';\n for (let i = 0; i < length; i++) {\n out += alphabet[bytes[i] % alphabet.length];\n }\n return out;\n}\n\nexport class EmailRecoveryFlow {\n private context: PasskeyManagerContext;\n private options?: EmailRecoveryFlowOptions;\n private pendingStore: PendingStore;\n private pending: PendingEmailRecovery | null = null;\n private phase: EmailRecoveryPhase = EmailRecoveryPhase.STEP_1_PREPARATION;\n private pollingTimer: ReturnType<typeof setTimeout> | undefined;\n private pollIntervalResolver?: () => void;\n private pollingStartedAt: number | null = null;\n private cancelled = false;\n private error?: Error;\n\n constructor(context: PasskeyManagerContext, options?: EmailRecoveryFlowOptions) {\n this.context = context;\n this.options = options;\n this.pendingStore = options?.pendingStore ?? new EmailRecoveryPendingStore({\n getPendingTtlMs: () => this.getConfig().pendingTtlMs,\n });\n }\n\n setOptions(options?: EmailRecoveryFlowOptions) {\n if (!options) return;\n this.options = { ...(this.options || {}), ...options };\n if (options.pendingStore) {\n this.pendingStore = options.pendingStore;\n }\n }\n private emit(event: EmailRecoverySSEEvent) {\n this.options?.onEvent?.(event);\n }\n\n private emitError(step: number, messageOrError: string | Error): Error {\n const err = typeof messageOrError === 'string' ? new Error(messageOrError) : messageOrError;\n const message = err.message || (typeof messageOrError === 'string' ? messageOrError : 'Unknown error');\n this.phase = EmailRecoveryPhase.ERROR;\n this.error = err;\n this.emit({\n step,\n phase: EmailRecoveryPhase.ERROR,\n status: EmailRecoveryStatus.ERROR,\n message,\n error: message,\n } as EmailRecoverySSEEvent & { error: string });\n this.options?.onError?.(err);\n return err;\n }\n\n private async fail(step: number, message: string): Promise<never> {\n const err = this.emitError(step, message);\n await this.options?.afterCall?.(false);\n throw err;\n }\n\n private async assertValidAccountIdOrFail(step: number, accountId: string): Promise<AccountId> {\n const validation = validateNearAccountId(accountId as AccountId);\n if (!validation.valid) {\n await this.fail(step, `Invalid NEAR account ID: ${validation.error}`);\n }\n return toAccountId(accountId as string);\n }\n\n private async resolvePendingOrFail(\n step: number,\n args: { accountId: AccountId; nearPublicKey?: string },\n options?: {\n allowErrorStatus?: boolean;\n missingMessage?: string;\n errorStatusMessage?: string;\n }\n ): Promise<PendingEmailRecovery> {\n const {\n allowErrorStatus = true,\n missingMessage = 'No pending email recovery record found for this account',\n errorStatusMessage = 'Pending email recovery is in an error state; please restart the flow',\n } = options ?? {};\n\n let rec = this.pending;\n if (!rec || rec.accountId !== args.accountId || (args.nearPublicKey && rec.nearPublicKey !== args.nearPublicKey)) {\n rec = await this.loadPending(args.accountId, args.nearPublicKey);\n this.pending = rec;\n }\n\n if (!rec) {\n await this.fail(step, missingMessage);\n }\n\n const resolved = rec as PendingEmailRecovery;\n if (!allowErrorStatus && resolved.status === 'error') {\n await this.fail(step, errorStatusMessage);\n }\n\n return resolved;\n }\n\n private getConfig() {\n return getEmailRecoveryConfig(this.context.configs);\n }\n\n private toBigInt(value: bigint | number | string | null | undefined): bigint {\n if (typeof value === 'bigint') return value;\n if (typeof value === 'number') return BigInt(value);\n if (typeof value === 'string' && value.length > 0) return BigInt(value);\n return BigInt(0);\n }\n\n private computeAvailableBalance(accountView: AccountViewLike): bigint {\n const STORAGE_PRICE_PER_BYTE = BigInt('10000000000000000000'); // 1e19 yocto NEAR per byte\n const amount = this.toBigInt(accountView.amount);\n const locked = this.toBigInt(accountView.locked);\n const storageUsage = this.toBigInt(accountView.storage_usage);\n const storageCost = storageUsage * STORAGE_PRICE_PER_BYTE;\n const rawAvailable = amount - locked - storageCost;\n return rawAvailable > 0 ? rawAvailable : BigInt(0);\n }\n\n private async assertSufficientBalance(nearAccountId: AccountId): Promise<void> {\n const { minBalanceYocto } = this.getConfig();\n\n try {\n const accountView = await this.context.nearClient.viewAccount(nearAccountId);\n const available = this.computeAvailableBalance(accountView);\n if (available < BigInt(minBalanceYocto)) {\n await this.fail(\n 1,\n `This account does not have enough NEAR to finalize recovery. Available: ${available.toString()} yocto; required: ${String(minBalanceYocto)}. Please top up and try again.`\n );\n }\n } catch (err: unknown) {\n await this.fail(1, errorMessage(err) || 'Failed to fetch account balance for recovery');\n }\n }\n\n private async getCanonicalRecoveryEmailOrFail(recoveryEmail: string): Promise<string> {\n const canonicalEmail = String(recoveryEmail || '').trim().toLowerCase();\n if (!canonicalEmail) {\n await this.fail(1, 'Recovery email is required for email-based account recovery');\n }\n return canonicalEmail;\n }\n\n private async getNextDeviceNumberFromContract(nearAccountId: AccountId): Promise<number> {\n try {\n const { syncAuthenticatorsContractCall } = await import('../rpcCalls');\n const authenticators = await syncAuthenticatorsContractCall(\n this.context.nearClient,\n this.context.configs.contractId,\n nearAccountId\n );\n const numbers = authenticators\n .map(({ authenticator }) => authenticator.deviceNumber)\n .filter((n): n is number => typeof n === 'number' && Number.isFinite(n));\n const max = numbers.length > 0 ? Math.max(...numbers) : 0;\n return max + 1;\n } catch {\n return 1;\n }\n }\n\n private async collectRecoveryCredentialOrFail(\n nearAccountId: AccountId,\n deviceNumber: number\n ): Promise<CollectedRecoveryCredential> {\n const confirmerText = {\n title: this.options?.confirmerText?.title ?? 'Register New Recovery Account',\n body: this.options?.confirmerText?.body ?? 'Create a recovery account and send an encrypted email to recover your account.',\n };\n const confirm = await this.context.webAuthnManager.requestRegistrationCredentialConfirmation({\n nearAccountId,\n deviceNumber,\n confirmerText,\n confirmationConfigOverride: this.options?.confirmationConfig,\n });\n\n if (!confirm.confirmed || !confirm.credential) {\n await this.fail(2, 'User cancelled email recovery TouchID confirmation');\n }\n\n return {\n credential: confirm.credential,\n vrfChallenge: confirm.vrfChallenge || undefined,\n };\n }\n\n private async deriveRecoveryKeysOrFail(\n nearAccountId: AccountId,\n deviceNumber: number,\n credential: WebAuthnRegistrationCredential\n ): Promise<DerivedRecoveryKeys> {\n const vrfDerivationResult = await this.context.webAuthnManager.deriveVrfKeypair({\n credential,\n nearAccountId,\n });\n\n if (!vrfDerivationResult.success || !vrfDerivationResult.encryptedVrfKeypair) {\n await this.fail(2, 'Failed to derive VRF keypair from PRF for email recovery');\n }\n\n const nearKeyResult = await this.context.webAuthnManager.deriveNearKeypairAndEncryptFromSerialized({\n nearAccountId,\n credential,\n options: { deviceNumber },\n });\n\n if (!nearKeyResult.success || !nearKeyResult.publicKey) {\n await this.fail(2, 'Failed to derive NEAR keypair for email recovery');\n }\n\n return {\n encryptedVrfKeypair: vrfDerivationResult.encryptedVrfKeypair,\n serverEncryptedVrfKeypair: vrfDerivationResult.serverEncryptedVrfKeypair || null,\n vrfPublicKey: vrfDerivationResult.vrfPublicKey,\n nearPublicKey: nearKeyResult.publicKey,\n };\n }\n\n private emitAwaitEmail(rec: PendingEmailRecovery, mailtoUrl: string): void {\n this.phase = EmailRecoveryPhase.STEP_3_AWAIT_EMAIL;\n this.emit({\n step: 3,\n phase: EmailRecoveryPhase.STEP_3_AWAIT_EMAIL,\n status: EmailRecoveryStatus.PROGRESS,\n message: 'New device key created; please send the recovery email from your registered address.',\n data: {\n accountId: rec.accountId,\n recoveryEmail: rec.recoveryEmail,\n nearPublicKey: rec.nearPublicKey,\n requestId: rec.requestId,\n mailtoUrl,\n },\n } as EmailRecoverySSEEvent & { data: Record<string, unknown> });\n }\n\n private emitAutoLoginEvent(\n status: EmailRecoveryStatus,\n message: string,\n data: Record<string, unknown>\n ): void {\n this.emit({\n step: 5,\n phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,\n status,\n message,\n data,\n } as EmailRecoverySSEEvent & { data: Record<string, unknown> });\n }\n\n private async checkViaDkimViewMethod(\n rec: PendingEmailRecovery\n ): Promise<{ completed: boolean; success: boolean; errorMessage?: string; transactionHash?: string } | null> {\n const { dkimVerifierAccountId, verificationViewMethod } = this.getConfig();\n if (!dkimVerifierAccountId) return null;\n\n try {\n const { getEmailRecoveryVerificationResult } = await import('../rpcCalls');\n const result = await getEmailRecoveryVerificationResult(\n this.context.nearClient,\n dkimVerifierAccountId,\n verificationViewMethod,\n rec.requestId\n );\n\n if (!result) {\n return { completed: false, success: false };\n }\n\n if (!result.verified) {\n const errorMessage = result.error_message || result.error_code || 'Email verification failed on relayer/contract';\n return {\n completed: true,\n success: false,\n errorMessage,\n transactionHash: result.transaction_hash,\n };\n }\n\n // Optional safety checks: ensure the bound account/key match expectations when available.\n if (result.account_id && result.account_id !== rec.accountId) {\n return {\n completed: true,\n success: false,\n errorMessage: 'Email verification account_id does not match requested account.',\n transactionHash: result.transaction_hash,\n };\n }\n if (result.new_public_key && result.new_public_key !== rec.nearPublicKey) {\n return {\n completed: true,\n success: false,\n errorMessage: 'Email verification new_public_key does not match expected recovery key.',\n transactionHash: result.transaction_hash,\n };\n }\n\n return {\n completed: true,\n success: true,\n transactionHash: result.transaction_hash\n };\n } catch (err) {\n // Treat view errors as retryable; keep polling the view method.\n // eslint-disable-next-line no-console\n console.warn('[EmailRecoveryFlow] get_verification_result view failed; will retry', err);\n return null;\n }\n }\n\n private buildPollingEventData(\n rec: PendingEmailRecovery,\n details: { transactionHash?: string; elapsedMs: number; pollCount: number }\n ): Record<string, unknown> {\n return {\n accountId: rec.accountId,\n requestId: rec.requestId,\n nearPublicKey: rec.nearPublicKey,\n transactionHash: details.transactionHash,\n elapsedMs: details.elapsedMs,\n pollCount: details.pollCount,\n };\n }\n\n private async sleepForPollInterval(ms: number): Promise<void> {\n await new Promise<void>(resolve => {\n this.pollIntervalResolver = resolve;\n this.pollingTimer = setTimeout(() => {\n this.pollIntervalResolver = undefined;\n this.pollingTimer = undefined;\n resolve();\n }, ms);\n }).finally(() => {\n this.pollIntervalResolver = undefined;\n });\n }\n\n private async pollUntil<T>(args: {\n intervalMs: number;\n timeoutMs: number;\n isCancelled: () => boolean;\n tick: (ctx: { elapsedMs: number; pollCount: number }) => Promise<PollTickResult<T>>;\n sleep?: (ms: number) => Promise<void>;\n now?: () => number;\n }): Promise<PollUntilResult<T>> {\n const now = args.now ?? Date.now;\n const sleep = args.sleep ?? this.sleepForPollInterval.bind(this);\n const startedAt = now();\n let pollCount = 0;\n\n while (!args.isCancelled()) {\n pollCount += 1;\n const elapsedMs = now() - startedAt;\n if (elapsedMs > args.timeoutMs) {\n return { status: 'timedOut', elapsedMs, pollCount };\n }\n\n const result = await args.tick({ elapsedMs, pollCount });\n if (result.done) {\n return { status: 'completed', value: result.value, elapsedMs, pollCount };\n }\n\n if (args.isCancelled()) {\n return { status: 'cancelled', elapsedMs, pollCount };\n }\n\n await sleep(args.intervalMs);\n }\n\n const elapsedMs = now() - startedAt;\n return { status: 'cancelled', elapsedMs, pollCount };\n }\n\n private async loadPending(\n accountId: AccountId,\n nearPublicKey?: string\n ): Promise<PendingEmailRecovery | null> {\n return this.pendingStore.get(accountId, nearPublicKey);\n }\n\n private async savePending(rec: PendingEmailRecovery): Promise<void> {\n await this.pendingStore.set(rec);\n this.pending = rec;\n }\n\n private async clearPending(accountId: AccountId, nearPublicKey?: string): Promise<void> {\n await this.pendingStore.clear(accountId, nearPublicKey);\n\n if (\n this.pending\n && this.pending.accountId === accountId\n && (!nearPublicKey || this.pending.nearPublicKey === nearPublicKey)\n ) {\n this.pending = null;\n }\n }\n\n getState() {\n return {\n phase: this.phase,\n pending: this.pending,\n error: this.error,\n };\n }\n\n async buildMailtoUrl(args: { accountId: string; nearPublicKey?: string }): Promise<string> {\n const { accountId, nearPublicKey } = args;\n this.cancelled = false;\n this.error = undefined;\n\n const nearAccountId = await this.assertValidAccountIdOrFail(3, accountId);\n const rec = await this.resolvePendingOrFail(\n 3,\n { accountId: nearAccountId, nearPublicKey },\n { allowErrorStatus: false }\n );\n\n if (rec.status === 'finalizing' || rec.status === 'complete') {\n await this.fail(3, 'Recovery email has already been processed on-chain for this request');\n }\n\n const mailtoUrl =\n rec.status === 'awaiting-email'\n ? await this.buildMailtoUrlAndUpdateStatus(rec)\n : this.buildMailtoUrlInternal(rec);\n this.emitAwaitEmail(rec, mailtoUrl);\n await this.options?.afterCall?.(true, undefined);\n return mailtoUrl;\n }\n\n async start(args: { accountId: string; recoveryEmail: string }): Promise<{ mailtoUrl: string; nearPublicKey: string }> {\n const { accountId, recoveryEmail } = args;\n this.cancelled = false;\n this.error = undefined;\n this.phase = EmailRecoveryPhase.STEP_1_PREPARATION;\n\n this.emit({\n step: 1,\n phase: EmailRecoveryPhase.STEP_1_PREPARATION,\n status: EmailRecoveryStatus.PROGRESS,\n message: 'Preparing email recovery...',\n });\n\n const nearAccountId = await this.assertValidAccountIdOrFail(1, accountId);\n await this.assertSufficientBalance(nearAccountId);\n const canonicalEmail = await this.getCanonicalRecoveryEmailOrFail(recoveryEmail);\n\n // Determine deviceNumber from on-chain authenticators\n const deviceNumber = await this.getNextDeviceNumberFromContract(nearAccountId);\n\n this.phase = EmailRecoveryPhase.STEP_2_TOUCH_ID_REGISTRATION;\n this.emit({\n step: 2,\n phase: EmailRecoveryPhase.STEP_2_TOUCH_ID_REGISTRATION,\n status: EmailRecoveryStatus.PROGRESS,\n message: 'Collecting passkey for email recovery...',\n });\n\n try {\n const confirm = await this.collectRecoveryCredentialOrFail(nearAccountId, deviceNumber);\n const derivedKeys = await this.deriveRecoveryKeysOrFail(nearAccountId, deviceNumber, confirm.credential);\n\n const rec: PendingEmailRecovery = {\n accountId: nearAccountId,\n recoveryEmail: canonicalEmail,\n deviceNumber,\n nearPublicKey: derivedKeys.nearPublicKey,\n requestId: generateEmailRecoveryRequestId(),\n encryptedVrfKeypair: derivedKeys.encryptedVrfKeypair,\n serverEncryptedVrfKeypair: derivedKeys.serverEncryptedVrfKeypair,\n vrfPublicKey: derivedKeys.vrfPublicKey,\n credential: confirm.credential,\n vrfChallenge: confirm.vrfChallenge || undefined,\n createdAt: Date.now(),\n status: 'awaiting-email',\n };\n\n const mailtoUrl = await this.buildMailtoUrlAndUpdateStatus(rec);\n\n this.emitAwaitEmail(rec, mailtoUrl);\n\n await this.options?.afterCall?.(true, undefined);\n\n return { mailtoUrl, nearPublicKey: rec.nearPublicKey };\n } catch (e: unknown) {\n const err = this.emitError(2, errorMessage(e) || 'Email recovery TouchID/derivation failed');\n await this.options?.afterCall?.(false);\n throw err;\n }\n }\n\n private buildMailtoUrlInternal(rec: PendingEmailRecovery): string {\n const { mailtoAddress } = this.getConfig();\n const to = encodeURIComponent(mailtoAddress);\n const subject = encodeURIComponent(`recover-${rec.requestId} ${rec.accountId} ${rec.nearPublicKey}`);\n const body = encodeURIComponent(`Recovering account ${rec.accountId} with a new passkey.`);\n return `mailto:${to}?subject=${subject}&body=${body}`;\n }\n\n private async buildMailtoUrlAndUpdateStatus(rec: PendingEmailRecovery): Promise<string> {\n rec.status = 'awaiting-add-key';\n await this.savePending(rec);\n return this.buildMailtoUrlInternal(rec);\n }\n\n async startPolling(args: { accountId: string; nearPublicKey?: string }): Promise<void> {\n const { accountId, nearPublicKey } = args;\n this.cancelled = false;\n this.error = undefined;\n\n const nearAccountId = await this.assertValidAccountIdOrFail(4, accountId);\n const rec = await this.resolvePendingOrFail(\n 4,\n { accountId: nearAccountId, nearPublicKey },\n { allowErrorStatus: false }\n );\n if (rec.status === 'complete' || rec.status === 'finalizing') {\n await this.options?.afterCall?.(true, undefined);\n return;\n }\n if (rec.status === 'awaiting-email') {\n await this.buildMailtoUrlAndUpdateStatus(rec);\n }\n\n await this.pollUntilAddKey(rec);\n await this.options?.afterCall?.(true, undefined);\n }\n\n stopPolling(): void {\n this.cancelled = true;\n if (this.pollingTimer) {\n clearTimeout(this.pollingTimer);\n this.pollingTimer = undefined;\n }\n if (this.pollIntervalResolver) {\n this.pollIntervalResolver();\n this.pollIntervalResolver = undefined;\n }\n }\n\n /**\n * Best-effort cancellation and local state reset so callers can retry.\n * This does not remove any passkey created in the browser/OS (WebAuthn has no delete API),\n * but it will stop polling and clear the pending IndexedDB record for the given key.\n */\n async cancelAndReset(args?: { accountId?: string; nearPublicKey?: string }): Promise<void> {\n this.stopPolling();\n\n const normalizedAccountId = (args?.accountId || this.pending?.accountId || '').toString().trim();\n const nearPublicKey = (args?.nearPublicKey || this.pending?.nearPublicKey || '').toString().trim();\n\n if (normalizedAccountId) {\n try {\n await this.clearPending(toAccountId(normalizedAccountId), nearPublicKey);\n } catch {\n // best-effort\n }\n }\n\n this.pending = null;\n this.error = undefined;\n this.phase = EmailRecoveryPhase.STEP_1_PREPARATION;\n }\n\n async finalize(args: { accountId: string; nearPublicKey?: string }): Promise<void> {\n const { accountId, nearPublicKey } = args;\n this.cancelled = false;\n this.error = undefined;\n\n const nearAccountId = await this.assertValidAccountIdOrFail(4, accountId);\n const rec = await this.resolvePendingOrFail(\n 4,\n { accountId: nearAccountId, nearPublicKey },\n { allowErrorStatus: true }\n );\n\n this.emit({\n step: 0,\n phase: EmailRecoveryPhase.RESUMED_FROM_PENDING,\n status: EmailRecoveryStatus.PROGRESS,\n message: 'Resuming email recovery from pending state...',\n data: {\n accountId: rec.accountId,\n nearPublicKey: rec.nearPublicKey,\n status: rec.status,\n },\n } as EmailRecoverySSEEvent & { data: Record<string, unknown> });\n\n if (rec.status === 'complete') {\n this.phase = EmailRecoveryPhase.STEP_6_COMPLETE;\n this.emit({\n step: 6,\n phase: EmailRecoveryPhase.STEP_6_COMPLETE,\n status: EmailRecoveryStatus.SUCCESS,\n message: 'Email recovery already completed for this key.',\n });\n await this.options?.afterCall?.(true, undefined);\n return;\n }\n\n // Ensure verification has completed successfully before finalizing registration.\n await this.pollUntilAddKey(rec);\n await this.finalizeRegistration(rec);\n await this.options?.afterCall?.(true, undefined);\n }\n\n private async pollUntilAddKey(rec: PendingEmailRecovery): Promise<void> {\n const { pollingIntervalMs, maxPollingDurationMs, dkimVerifierAccountId } = this.getConfig();\n if (!dkimVerifierAccountId) {\n const err = this.emitError(4, 'Email recovery verification contract (dkimVerifierAccountId) is not configured');\n await this.options?.afterCall?.(false);\n throw err;\n }\n this.phase = EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT;\n this.pollingStartedAt = Date.now();\n\n const pollResult = await this.pollUntil<VerificationOutcome>({\n intervalMs: pollingIntervalMs,\n timeoutMs: maxPollingDurationMs,\n isCancelled: () => this.cancelled,\n tick: async ({ elapsedMs, pollCount }) => {\n const verification = await this.checkViaDkimViewMethod(rec);\n const completed = verification?.completed === true;\n const success = verification?.success === true;\n\n this.emit({\n step: 4,\n phase: EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT,\n status: EmailRecoveryStatus.PROGRESS,\n message: completed && success\n ? `Email verified for request ${rec.requestId}; finalizing registration`\n : `Waiting for email verification for request ${rec.requestId}`,\n data: this.buildPollingEventData(rec, {\n transactionHash: verification?.transactionHash,\n elapsedMs,\n pollCount,\n }),\n } as EmailRecoverySSEEvent & { data: Record<string, unknown> });\n\n if (!completed) {\n return { done: false };\n }\n\n if (!success) {\n return {\n done: true,\n value: {\n outcome: 'failed',\n errorMessage: verification?.errorMessage || 'Email verification failed',\n },\n };\n }\n\n return { done: true, value: { outcome: 'verified' } };\n },\n });\n\n if (pollResult.status === 'completed') {\n if (pollResult.value.outcome === 'failed') {\n const err = this.emitError(4, pollResult.value.errorMessage);\n rec.status = 'error';\n await this.savePending(rec);\n await this.options?.afterCall?.(false);\n throw err;\n }\n\n rec.status = 'finalizing';\n await this.savePending(rec);\n return;\n }\n\n if (pollResult.status === 'timedOut') {\n const err = this.emitError(4, 'Timed out waiting for recovery email to be processed on-chain');\n rec.status = 'error';\n await this.savePending(rec);\n await this.options?.afterCall?.(false);\n throw err;\n }\n\n const err = this.emitError(4, 'Email recovery polling was cancelled');\n await this.options?.afterCall?.(false);\n throw err;\n }\n\n private initializeNonceManager(\n rec: PendingEmailRecovery\n ): {\n nonceManager: ReturnType<PasskeyManagerContext['webAuthnManager']['getNonceManager']>;\n accountId: AccountId;\n } {\n const nonceManager = this.context.webAuthnManager.getNonceManager();\n const accountId = toAccountId(rec.accountId);\n nonceManager.initializeUser(accountId, rec.nearPublicKey);\n return { nonceManager, accountId };\n }\n\n /*\n * Signs a `link_device_register_user` contract call\n */\n private async signNewDevice2RegistrationTx(rec: PendingEmailRecovery, accountId: AccountId): Promise<SignedTransaction> {\n const vrfChallenge = rec.vrfChallenge;\n if (!vrfChallenge) {\n return this.fail(5, 'Missing VRF challenge for email recovery registration');\n }\n\n const registrationResult = await this.context.webAuthnManager.signDevice2RegistrationWithStoredKey({\n nearAccountId: accountId,\n credential: rec.credential,\n vrfChallenge,\n deterministicVrfPublicKey: rec.vrfPublicKey,\n deviceNumber: rec.deviceNumber,\n });\n\n if (!registrationResult.success || !registrationResult.signedTransaction) {\n await this.fail(5, registrationResult.error || 'Failed to sign email recovery registration transaction');\n }\n\n return registrationResult.signedTransaction;\n }\n\n private async broadcastRegistrationTxAndWaitFinal(\n rec: PendingEmailRecovery,\n signedTx: SignedTransaction\n ): Promise<string | undefined> {\n let txResult: FinalExecutionOutcome;\n try {\n txResult = await this.context.nearClient.sendTransaction(\n signedTx,\n DEFAULT_WAIT_STATUS.linkDeviceRegistration\n );\n } catch (err: unknown) {\n const msg = errorMessage(err) || 'Failed to broadcast email recovery registration transaction (insufficient funds or RPC error)';\n throw new Error(msg);\n }\n\n const txHash = this.getTxHash(txResult);\n\n // Contract can return `{ verified: false, registration_info: null }` without failing the tx.\n // When that happens, the authenticator was NOT registered on-chain, so we must not proceed\n // with local persistence + auto-login.\n const linkDeviceResult = parseLinkDeviceRegisterUserResponse(txResult);\n if (linkDeviceResult?.verified === false) {\n const logs = this.extractNearExecutionLogs(txResult);\n const isStaleChallenge = logs.some((log) => /StaleChallenge|freshness validation failed/i.test(log));\n const txHint = txHash ? ` (tx: ${txHash})` : '';\n const code = isStaleChallenge\n ? EmailRecoveryErrorCode.VRF_CHALLENGE_EXPIRED\n : EmailRecoveryErrorCode.REGISTRATION_NOT_VERIFIED;\n const message = isStaleChallenge\n ? `Timed out finalizing registration (VRF challenge expired). Please restart email recovery and try again${txHint}.`\n : `Registration did not verify on-chain. Please try again${txHint}.`;\n throw new EmailRecoveryError(message, code, {\n accountId: rec.accountId,\n nearPublicKey: rec.nearPublicKey,\n transactionHash: txHash,\n logs,\n result: linkDeviceResult,\n });\n }\n\n if (txHash) {\n this.emit({\n step: 5,\n phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,\n status: EmailRecoveryStatus.PROGRESS,\n message: 'Registration transaction confirmed',\n data: {\n accountId: rec.accountId,\n nearPublicKey: rec.nearPublicKey,\n transactionHash: txHash,\n },\n } as EmailRecoverySSEEvent & { data: Record<string, unknown> });\n }\n\n return txHash;\n }\n\n private getTxHash(outcome: FinalExecutionOutcome): string | undefined {\n const txUnknown: unknown = outcome.transaction;\n if (txUnknown && typeof txUnknown === 'object') {\n const hash = (txUnknown as Record<string, unknown>).hash;\n if (typeof hash === 'string' && hash.length > 0) return hash;\n }\n\n const fallback = (outcome as unknown as Record<string, unknown>).transaction_hash;\n return typeof fallback === 'string' && fallback.length > 0 ? fallback : undefined;\n }\n\n private extractNearExecutionLogs(outcome: FinalExecutionOutcome): string[] {\n const logs: string[] = [];\n for (const entry of outcome.transaction_outcome.outcome.logs) {\n logs.push(String(entry));\n }\n for (const receipt of outcome.receipts_outcome) {\n for (const entry of receipt.outcome.logs) {\n logs.push(String(entry));\n }\n }\n return logs;\n }\n\n private mapAuthenticatorsFromContract(\n authenticators: Array<{ credentialId: string; authenticator: StoredAuthenticator }>\n ) {\n return authenticators.map(({ authenticator }) => ({\n credentialId: authenticator.credentialId,\n credentialPublicKey: authenticator.credentialPublicKey,\n transports: authenticator.transports,\n name: authenticator.name,\n registered: authenticator.registered.toISOString(),\n vrfPublicKey: authenticator.vrfPublicKeys?.[0] || '',\n deviceNumber: authenticator.deviceNumber,\n }));\n }\n\n private async syncAuthenticatorsBestEffort(accountId: AccountId): Promise<boolean> {\n try {\n const { syncAuthenticatorsContractCall } = await import('../rpcCalls');\n const authenticators = await syncAuthenticatorsContractCall(\n this.context.nearClient,\n this.context.configs.contractId,\n accountId\n );\n\n const mappedAuthenticators = this.mapAuthenticatorsFromContract(authenticators);\n await IndexedDBManager.clientDB.syncAuthenticatorsFromContract(accountId, mappedAuthenticators);\n return true;\n } catch (err) {\n console.warn('[EmailRecoveryFlow] Failed to sync authenticators after recovery:', err);\n return false;\n }\n }\n\n private async setLastUserBestEffort(accountId: AccountId, deviceNumber: number): Promise<boolean> {\n try {\n await IndexedDBManager.clientDB.setLastUser(accountId, deviceNumber);\n return true;\n } catch (err) {\n console.warn('[EmailRecoveryFlow] Failed to set last user after recovery:', err);\n return false;\n }\n }\n\n private async updateNonceBestEffort(\n nonceManager: ReturnType<PasskeyManagerContext['webAuthnManager']['getNonceManager']>,\n signedTx: SignedTransaction\n ): Promise<void> {\n try {\n const txNonce = signedTx.transaction.nonce;\n if (txNonce != null) {\n await nonceManager.updateNonceFromBlockchain(\n this.context.nearClient,\n String(txNonce)\n );\n }\n } catch {\n // best-effort; do not fail flow\n }\n }\n\n private async persistRecoveredUserData(rec: PendingEmailRecovery, accountId: AccountId): Promise<void> {\n const { webAuthnManager } = this.context;\n\n const payload: StoreUserDataPayload = {\n nearAccountId: accountId,\n deviceNumber: rec.deviceNumber,\n clientNearPublicKey: rec.nearPublicKey,\n lastUpdated: Date.now(),\n passkeyCredential: {\n id: rec.credential.id,\n rawId: rec.credential.rawId,\n },\n encryptedVrfKeypair: {\n encryptedVrfDataB64u: rec.encryptedVrfKeypair.encryptedVrfDataB64u,\n chacha20NonceB64u: rec.encryptedVrfKeypair.chacha20NonceB64u,\n },\n serverEncryptedVrfKeypair: rec.serverEncryptedVrfKeypair || undefined,\n };\n\n await webAuthnManager.storeUserData(payload);\n }\n\n /**\n * Explicitly persist the authenticator from the recovery record into the local cache.\n * This ensures the key is available immediately, bridging the gap before RPC sync sees it.\n */\n private async persistAuthenticatorBestEffort(rec: PendingEmailRecovery, accountId: AccountId): Promise<void> {\n try {\n const { webAuthnManager } = this.context;\n const attestationB64u = rec.credential.response.attestationObject;\n const credentialPublicKey = await webAuthnManager.extractCosePublicKey(attestationB64u);\n\n await webAuthnManager.storeAuthenticator({\n nearAccountId: accountId,\n deviceNumber: rec.deviceNumber,\n credentialId: rec.credential.rawId,\n credentialPublicKey,\n transports: ['internal'],\n name: `Device ${rec.deviceNumber} Passkey for ${rec.accountId.split('.')[0]}`,\n registered: new Date().toISOString(),\n syncedAt: new Date().toISOString(), // Local truth is fresh\n vrfPublicKey: rec.vrfPublicKey,\n });\n console.log('[EmailRecoveryFlow] Locally persisted recovered authenticator for immediate use.');\n } catch (e) {\n console.error('[EmailRecoveryFlow] Failed to locally persist authenticator (critical for immediate export):', e);\n // We log error but don't rethrow to avoid crashing the final success UI.\n }\n }\n\n private async markCompleteAndClearPending(rec: PendingEmailRecovery): Promise<void> {\n rec.status = 'complete';\n await this.savePending(rec);\n await this.clearPending(rec.accountId, rec.nearPublicKey);\n }\n\n private async assertVrfActiveForAccount(accountId: AccountId, message: string): Promise<void> {\n const vrfStatus = await this.context.webAuthnManager.checkVrfStatus();\n const vrfActiveForAccount =\n vrfStatus.active\n && vrfStatus.nearAccountId\n && String(vrfStatus.nearAccountId) === String(accountId);\n if (!vrfActiveForAccount) {\n throw new Error(message);\n }\n }\n\n private async finalizeLocalLoginState(accountId: AccountId, deviceNumber: number): Promise<void> {\n const { webAuthnManager } = this.context;\n await webAuthnManager.setLastUser(accountId, deviceNumber);\n await webAuthnManager.initializeCurrentUser(accountId, this.context.nearClient);\n try { await getLoginSession(this.context, accountId); } catch { }\n }\n\n private async tryShamirUnlock(\n rec: PendingEmailRecovery,\n accountId: AccountId,\n deviceNumber: number\n ): Promise<boolean> {\n if (\n !rec.serverEncryptedVrfKeypair\n || !rec.serverEncryptedVrfKeypair.serverKeyId\n || !this.context.configs.vrfWorkerConfigs?.shamir3pass?.relayServerUrl\n ) {\n return false;\n }\n\n try {\n const { webAuthnManager } = this.context;\n const unlockResult = await webAuthnManager.shamir3PassDecryptVrfKeypair({\n nearAccountId: accountId,\n kek_s_b64u: rec.serverEncryptedVrfKeypair.kek_s_b64u,\n ciphertextVrfB64u: rec.serverEncryptedVrfKeypair.ciphertextVrfB64u,\n serverKeyId: rec.serverEncryptedVrfKeypair.serverKeyId,\n });\n\n if (!unlockResult.success) {\n return false;\n }\n\n await this.assertVrfActiveForAccount(accountId, 'VRF session inactive after Shamir3Pass unlock');\n await this.finalizeLocalLoginState(accountId, deviceNumber);\n return true;\n } catch (err) {\n console.warn('[EmailRecoveryFlow] Shamir 3-pass unlock failed, falling back to TouchID', err);\n return false;\n }\n }\n\n private async tryTouchIdUnlock(\n rec: PendingEmailRecovery,\n accountId: AccountId,\n deviceNumber: number\n ): Promise<{ success: boolean; reason?: string }> {\n try {\n const { webAuthnManager } = this.context;\n const authChallenge = createRandomVRFChallenge() as VRFChallenge;\n\n const storedCredentialId = String(rec.credential?.rawId || rec.credential?.id || '').trim();\n const credentialIds = storedCredentialId ? [storedCredentialId] : [];\n const authenticators = credentialIds.length > 0\n ? []\n : await webAuthnManager.getAuthenticatorsByUser(accountId);\n const authCredential = await webAuthnManager.getAuthenticationCredentialsSerializedDualPrf({\n nearAccountId: accountId,\n challenge: authChallenge,\n credentialIds: credentialIds.length > 0 ? credentialIds : authenticators.map((a) => a.credentialId),\n });\n\n if (storedCredentialId && authCredential.rawId !== storedCredentialId) {\n return {\n success: false,\n reason: 'Wrong passkey selected during recovery auto-login; please use the newly recovered passkey.',\n };\n }\n\n const vrfUnlockResult = await webAuthnManager.unlockVRFKeypair({\n nearAccountId: accountId,\n encryptedVrfKeypair: rec.encryptedVrfKeypair,\n credential: authCredential,\n });\n\n if (!vrfUnlockResult.success) {\n return { success: false, reason: vrfUnlockResult.error || 'VRF unlock failed during auto-login' };\n }\n\n await this.assertVrfActiveForAccount(accountId, 'VRF session inactive after TouchID unlock');\n await this.finalizeLocalLoginState(accountId, deviceNumber);\n return { success: true };\n } catch (err: unknown) {\n return { success: false, reason: errorMessage(err) || String(err) };\n }\n }\n\n private async handleAutoLoginFailure(reason: string, err?: unknown): Promise<AutoLoginResult> {\n console.warn('[EmailRecoveryFlow] Auto-login failed after recovery', err ?? reason);\n try {\n await this.context.webAuthnManager.clearVrfSession();\n } catch { }\n return { success: false, reason };\n }\n\n private async finalizeRegistration(rec: PendingEmailRecovery): Promise<void> {\n this.phase = EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION;\n this.emit({\n step: 5,\n phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,\n status: EmailRecoveryStatus.PROGRESS,\n message: 'Finalizing email recovery registration...',\n data: {\n accountId: rec.accountId,\n nearPublicKey: rec.nearPublicKey,\n },\n } as EmailRecoverySSEEvent & { data: Record<string, unknown> });\n\n try {\n const { nonceManager, accountId } = this.initializeNonceManager(rec);\n const signedTx = await this.signNewDevice2RegistrationTx(rec, accountId);\n const txHash = await this.broadcastRegistrationTxAndWaitFinal(rec, signedTx);\n if (!txHash) {\n console.warn('[EmailRecoveryFlow] Registration transaction confirmed without hash; continuing local persistence');\n }\n\n // CRITICAL: Persist local state immediately.\n // 1. Store the new user record (Device N) so that `getLastUser()` finds it.\n await this.persistRecoveredUserData(rec, accountId);\n\n // 2. Sync authenticators (RPC might be stale, but we try).\n await this.syncAuthenticatorsBestEffort(accountId);\n\n // 3. FORCE-SAVE the local authenticator from our recovery record.\n // This is crucial because RPC sync might be slow/empty immediately after TX.\n // We must ensure the new key is in the DB so `ensureCurrentPasskey` finds it.\n // We do this AFTER sync to ensure it's not wiped by a stale sync.\n await this.persistAuthenticatorBestEffort(rec, accountId);\n\n // 4. Set as active user to ensure immediate subsequent calls use this identity.\n await this.setLastUserBestEffort(accountId, rec.deviceNumber);\n\n await this.updateNonceBestEffort(nonceManager, signedTx);\n\n this.emitAutoLoginEvent(EmailRecoveryStatus.PROGRESS, 'Attempting auto-login with recovered device...', {\n autoLogin: 'progress',\n });\n\n const autoLoginResult = await this.attemptAutoLogin(rec);\n if (autoLoginResult.success) {\n this.emitAutoLoginEvent(EmailRecoveryStatus.SUCCESS, `Welcome ${accountId}`, {\n autoLogin: 'success',\n });\n } else {\n this.emitAutoLoginEvent(EmailRecoveryStatus.ERROR, 'Auto-login failed; please log in manually on this device.', {\n error: autoLoginResult.reason,\n autoLogin: 'error',\n });\n }\n\n await this.markCompleteAndClearPending(rec);\n\n this.phase = EmailRecoveryPhase.STEP_6_COMPLETE;\n this.emit({\n step: 6,\n phase: EmailRecoveryPhase.STEP_6_COMPLETE,\n status: EmailRecoveryStatus.SUCCESS,\n message: 'Email recovery completed successfully',\n data: {\n accountId: rec.accountId,\n nearPublicKey: rec.nearPublicKey,\n },\n } as EmailRecoverySSEEvent & { data: Record<string, unknown> });\n } catch (e: unknown) {\n rec.status = 'error';\n await this.savePending(rec).catch(() => { });\n const original = e instanceof Error\n ? e\n : new Error(errorMessage(e) || 'Email recovery finalization failed');\n const err = this.emitError(5, original);\n await this.options?.afterCall?.(false);\n throw err;\n }\n }\n\n private async attemptAutoLogin(rec: PendingEmailRecovery): Promise<AutoLoginResult> {\n try {\n const accountId = toAccountId(rec.accountId);\n const deviceNumber = parseDeviceNumber(rec.deviceNumber, { min: 1 });\n if (deviceNumber === null) {\n return this.handleAutoLoginFailure(\n `Invalid deviceNumber for auto-login: ${String(rec.deviceNumber)}`\n );\n }\n\n const shamirUnlocked = await this.tryShamirUnlock(rec, accountId, deviceNumber);\n if (shamirUnlocked) {\n return { success: true, method: 'shamir' };\n }\n\n const touchIdResult = await this.tryTouchIdUnlock(rec, accountId, deviceNumber);\n if (touchIdResult.success) {\n return { success: true, method: 'touchid' };\n }\n\n return this.handleAutoLoginFailure(touchIdResult.reason || 'Auto-login failed');\n } catch (err: unknown) {\n return this.handleAutoLoginFailure(errorMessage(err) || String(err), err);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"emailRecovery.js","names":["err: unknown","err: any","elapsedMs","rec: PendingEmailRecovery","e: unknown","errorMessage","transactionHash: string | undefined","err","txResult: FinalExecutionOutcome","txUnknown: unknown","logs: string[]","payload: StoreUserDataPayload"],"sources":["../../../../src/core/TatchiPasskey/emailRecovery.ts"],"sourcesContent":["import type { PasskeyManagerContext } from './index';\nimport { IndexedDBManager } from '../IndexedDBManager';\nimport { validateNearAccountId } from '../../utils/validation';\nimport { errorMessage } from '../../utils/errors';\nimport { toAccountId, type AccountId } from '../types/accountIds';\nimport {\n EmailRecoveryPhase,\n EmailRecoveryStatus,\n type EmailRecoverySSEEvent,\n type EventCallback,\n type AfterCall,\n} from '../types/sdkSentEvents';\nimport type { TatchiConfigs } from '../types/tatchi';\nimport {\n createRandomVRFChallenge,\n type EncryptedVRFKeypair,\n type ServerEncryptedVrfKeypair,\n type VRFChallenge,\n} from '../types/vrf-worker';\nimport type { FinalExecutionOutcome } from '@near-js/types';\nimport type { StoredAuthenticator, WebAuthnRegistrationCredential } from '../types';\nimport type { ConfirmationConfig } from '../types/signer-worker';\nimport { DEFAULT_WAIT_STATUS } from '../types/rpc';\nimport { parseDeviceNumber } from '../WebAuthnManager/SignerWorkerManager/getDeviceNumber';\nimport { getLoginSession } from './login';\nimport type { SignedTransaction } from '../NearClient';\nimport {\n EmailRecoveryPendingStore,\n parseLinkDeviceRegisterUserResponse,\n type PendingStore,\n} from '../EmailRecovery';\nimport { EmailRecoveryError, EmailRecoveryErrorCode } from '../types/emailRecovery';\nimport { getEmailRecoveryAttempt } from '../rpcCalls';\nimport { ensureEd25519Prefix } from '../nearCrypto';\n\nexport type PendingEmailRecoveryStatus =\n | 'awaiting-email'\n | 'awaiting-add-key'\n | 'finalizing'\n | 'complete'\n | 'error';\n\nexport type PendingEmailRecovery = {\n accountId: AccountId;\n recoveryEmail?: string;\n deviceNumber: number;\n nearPublicKey: string;\n requestId: string;\n encryptedVrfKeypair: EncryptedVRFKeypair;\n serverEncryptedVrfKeypair: ServerEncryptedVrfKeypair | null;\n vrfPublicKey: string;\n credential: WebAuthnRegistrationCredential;\n vrfChallenge?: VRFChallenge;\n createdAt: number;\n status: PendingEmailRecoveryStatus;\n};\n\ntype PollTickResult<T> = { done: false } | { done: true; value: T };\n\ntype PollUntilResult<T> =\n | { status: 'completed'; value: T; elapsedMs: number; pollCount: number }\n | { status: 'timedOut'; elapsedMs: number; pollCount: number }\n | { status: 'cancelled'; elapsedMs: number; pollCount: number };\n\ntype VerificationOutcome =\n | { outcome: 'verified' }\n | { outcome: 'failed'; errorMessage: string };\n\ntype AutoLoginResult =\n | { success: true; method: 'shamir' | 'touchid' }\n | { success: false; reason: string };\n\ntype StoreUserDataPayload = Parameters<PasskeyManagerContext['webAuthnManager']['storeUserData']>[0];\n\ntype AccountViewLike = {\n amount: bigint | string;\n locked: bigint | string;\n storage_usage: number | bigint;\n};\n\ntype CollectedRecoveryCredential = {\n credential: WebAuthnRegistrationCredential;\n vrfChallenge?: VRFChallenge;\n};\n\ntype DerivedRecoveryKeys = {\n encryptedVrfKeypair: EncryptedVRFKeypair;\n serverEncryptedVrfKeypair: ServerEncryptedVrfKeypair | null;\n vrfPublicKey: string;\n nearPublicKey: string;\n};\n\nexport interface EmailRecoveryFlowOptions {\n onEvent?: EventCallback<EmailRecoverySSEEvent>;\n onError?: (error: Error) => void;\n afterCall?: AfterCall<void>;\n pendingStore?: PendingStore;\n confirmerText?: { title?: string; body?: string };\n confirmationConfig?: Partial<ConfirmationConfig>;\n}\n\nfunction getEmailRecoveryConfig(configs: TatchiConfigs): {\n minBalanceYocto: string;\n pollingIntervalMs: number;\n maxPollingDurationMs: number;\n pendingTtlMs: number;\n mailtoAddress: string;\n} {\n const relayerEmailCfg = configs.relayer.emailRecovery;\n const minBalanceYocto = String(relayerEmailCfg.minBalanceYocto);\n const pollingIntervalMs = Number(relayerEmailCfg.pollingIntervalMs);\n const maxPollingDurationMs = Number(relayerEmailCfg.maxPollingDurationMs);\n const pendingTtlMs = Number(relayerEmailCfg.pendingTtlMs);\n const mailtoAddress = String(relayerEmailCfg.mailtoAddress);\n return {\n minBalanceYocto,\n pollingIntervalMs,\n maxPollingDurationMs,\n pendingTtlMs,\n mailtoAddress,\n };\n}\n\nexport function generateEmailRecoveryRequestId(): string {\n // 6-character A–Z0–9 identifier, suitable for short-lived correlation.\n const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';\n const length = 6;\n const bytes = new Uint8Array(length);\n (globalThis.crypto || window.crypto).getRandomValues(bytes);\n let out = '';\n for (let i = 0; i < length; i++) {\n out += alphabet[bytes[i] % alphabet.length];\n }\n return out;\n}\n\nexport class EmailRecoveryFlow {\n private context: PasskeyManagerContext;\n private options?: EmailRecoveryFlowOptions;\n private pendingStore: PendingStore;\n private pending: PendingEmailRecovery | null = null;\n private phase: EmailRecoveryPhase = EmailRecoveryPhase.STEP_1_PREPARATION;\n private pollingTimer: ReturnType<typeof setTimeout> | undefined;\n private pollIntervalResolver?: () => void;\n private pollingStartedAt: number | null = null;\n private cancelled = false;\n private error?: Error;\n\n constructor(context: PasskeyManagerContext, options?: EmailRecoveryFlowOptions) {\n this.context = context;\n this.options = options;\n this.pendingStore = options?.pendingStore ?? new EmailRecoveryPendingStore({\n getPendingTtlMs: () => this.getConfig().pendingTtlMs,\n });\n }\n\n setOptions(options?: EmailRecoveryFlowOptions) {\n if (!options) return;\n this.options = { ...(this.options || {}), ...options };\n if (options.pendingStore) {\n this.pendingStore = options.pendingStore;\n }\n }\n private emit(event: EmailRecoverySSEEvent) {\n this.options?.onEvent?.(event);\n }\n\n private emitError(step: number, messageOrError: string | Error): Error {\n const err = typeof messageOrError === 'string' ? new Error(messageOrError) : messageOrError;\n const message = err.message || (typeof messageOrError === 'string' ? messageOrError : 'Unknown error');\n this.phase = EmailRecoveryPhase.ERROR;\n this.error = err;\n this.emit({\n step,\n phase: EmailRecoveryPhase.ERROR,\n status: EmailRecoveryStatus.ERROR,\n message,\n error: message,\n } as EmailRecoverySSEEvent & { error: string });\n this.options?.onError?.(err);\n return err;\n }\n\n private async fail(step: number, message: string): Promise<never> {\n const err = this.emitError(step, message);\n await this.options?.afterCall?.(false);\n throw err;\n }\n\n private async assertValidAccountIdOrFail(step: number, accountId: string): Promise<AccountId> {\n const validation = validateNearAccountId(accountId as AccountId);\n if (!validation.valid) {\n await this.fail(step, `Invalid NEAR account ID: ${validation.error}`);\n }\n return toAccountId(accountId as string);\n }\n\n private async resolvePendingOrFail(\n step: number,\n args: { accountId: AccountId; nearPublicKey?: string },\n options?: {\n allowErrorStatus?: boolean;\n missingMessage?: string;\n errorStatusMessage?: string;\n }\n ): Promise<PendingEmailRecovery> {\n const {\n allowErrorStatus = true,\n missingMessage = 'No pending email recovery record found for this account',\n errorStatusMessage = 'Pending email recovery is in an error state; please restart the flow',\n } = options ?? {};\n\n let rec = this.pending;\n if (!rec || rec.accountId !== args.accountId || (args.nearPublicKey && rec.nearPublicKey !== args.nearPublicKey)) {\n rec = await this.loadPending(args.accountId, args.nearPublicKey);\n this.pending = rec;\n }\n\n if (!rec) {\n await this.fail(step, missingMessage);\n }\n\n const resolved = rec as PendingEmailRecovery;\n if (!allowErrorStatus && resolved.status === 'error') {\n await this.fail(step, errorStatusMessage);\n }\n\n return resolved;\n }\n\n private getConfig() {\n return getEmailRecoveryConfig(this.context.configs);\n }\n\n private toBigInt(value: bigint | number | string | null | undefined): bigint {\n if (typeof value === 'bigint') return value;\n if (typeof value === 'number') return BigInt(value);\n if (typeof value === 'string' && value.length > 0) return BigInt(value);\n return BigInt(0);\n }\n\n private computeAvailableBalance(accountView: AccountViewLike): bigint {\n const STORAGE_PRICE_PER_BYTE = BigInt('10000000000000000000'); // 1e19 yocto NEAR per byte\n const amount = this.toBigInt(accountView.amount);\n const locked = this.toBigInt(accountView.locked);\n const storageUsage = this.toBigInt(accountView.storage_usage);\n const storageCost = storageUsage * STORAGE_PRICE_PER_BYTE;\n const rawAvailable = amount - locked - storageCost;\n return rawAvailable > 0 ? rawAvailable : BigInt(0);\n }\n\n private async assertSufficientBalance(nearAccountId: AccountId): Promise<void> {\n const { minBalanceYocto } = this.getConfig();\n\n try {\n const accountView = await this.context.nearClient.viewAccount(nearAccountId);\n const available = this.computeAvailableBalance(accountView);\n if (available < BigInt(minBalanceYocto)) {\n await this.fail(\n 1,\n `This account does not have enough NEAR to finalize recovery. Available: ${available.toString()} yocto; required: ${String(minBalanceYocto)}. Please top up and try again.`\n );\n }\n } catch (err: unknown) {\n await this.fail(1, errorMessage(err) || 'Failed to fetch account balance for recovery');\n }\n }\n\n private getCanonicalRecoveryEmail(recoveryEmail?: string): string | undefined {\n const canonicalEmail = String(recoveryEmail || '').trim().toLowerCase();\n return canonicalEmail || undefined;\n }\n\n private async getNextDeviceNumberFromContract(nearAccountId: AccountId): Promise<number> {\n try {\n const { syncAuthenticatorsContractCall } = await import('../rpcCalls');\n const authenticators = await syncAuthenticatorsContractCall(\n this.context.nearClient,\n this.context.configs.contractId,\n nearAccountId\n );\n const numbers = authenticators\n .map(({ authenticator }) => authenticator.deviceNumber)\n .filter((n): n is number => typeof n === 'number' && Number.isFinite(n));\n const max = numbers.length > 0 ? Math.max(...numbers) : 0;\n return max + 1;\n } catch {\n return 1;\n }\n }\n\n private async collectRecoveryCredentialOrFail(\n nearAccountId: AccountId,\n deviceNumber: number\n ): Promise<CollectedRecoveryCredential> {\n const confirmerText = {\n title: this.options?.confirmerText?.title ?? 'Register New Recovery Account',\n body: this.options?.confirmerText?.body ?? 'Create a recovery account and send an encrypted email to recover your account.',\n };\n const confirm = await this.context.webAuthnManager.requestRegistrationCredentialConfirmation({\n nearAccountId,\n deviceNumber,\n confirmerText,\n confirmationConfigOverride: this.options?.confirmationConfig,\n });\n\n if (!confirm.confirmed || !confirm.credential) {\n await this.fail(2, 'User cancelled email recovery TouchID confirmation');\n }\n\n return {\n credential: confirm.credential,\n vrfChallenge: confirm.vrfChallenge || undefined,\n };\n }\n\n private async deriveRecoveryKeysOrFail(\n nearAccountId: AccountId,\n deviceNumber: number,\n credential: WebAuthnRegistrationCredential\n ): Promise<DerivedRecoveryKeys> {\n const vrfDerivationResult = await this.context.webAuthnManager.deriveVrfKeypair({\n credential,\n nearAccountId,\n });\n\n if (!vrfDerivationResult.success || !vrfDerivationResult.encryptedVrfKeypair) {\n await this.fail(2, 'Failed to derive VRF keypair from PRF for email recovery');\n }\n\n const nearKeyResult = await this.context.webAuthnManager.deriveNearKeypairAndEncryptFromSerialized({\n nearAccountId,\n credential,\n options: { deviceNumber },\n });\n\n if (!nearKeyResult.success || !nearKeyResult.publicKey) {\n await this.fail(2, 'Failed to derive NEAR keypair for email recovery');\n }\n\n return {\n encryptedVrfKeypair: vrfDerivationResult.encryptedVrfKeypair,\n serverEncryptedVrfKeypair: vrfDerivationResult.serverEncryptedVrfKeypair || null,\n vrfPublicKey: vrfDerivationResult.vrfPublicKey,\n nearPublicKey: nearKeyResult.publicKey,\n };\n }\n\n private emitAwaitEmail(rec: PendingEmailRecovery, mailtoUrl: string): void {\n this.phase = EmailRecoveryPhase.STEP_3_AWAIT_EMAIL;\n this.emit({\n step: 3,\n phase: EmailRecoveryPhase.STEP_3_AWAIT_EMAIL,\n status: EmailRecoveryStatus.PROGRESS,\n message: 'New device key created; please send the recovery email from your registered address.',\n data: {\n accountId: rec.accountId,\n ...(rec.recoveryEmail ? { recoveryEmail: rec.recoveryEmail } : {}),\n nearPublicKey: rec.nearPublicKey,\n requestId: rec.requestId,\n mailtoUrl,\n },\n } as EmailRecoverySSEEvent & { data: Record<string, unknown> });\n }\n\n private emitAutoLoginEvent(\n status: EmailRecoveryStatus,\n message: string,\n data: Record<string, unknown>\n ): void {\n this.emit({\n step: 5,\n phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,\n status,\n message,\n data,\n } as EmailRecoverySSEEvent & { data: Record<string, unknown> });\n }\n\n private async checkViaEmailRecovererAttempt(\n rec: PendingEmailRecovery\n ): Promise<{ completed: boolean; success: boolean; missing?: boolean; errorMessage?: string; } | null> {\n try {\n const attempt = await getEmailRecoveryAttempt(\n this.context.nearClient,\n rec.accountId,\n rec.requestId\n );\n\n if (!attempt) {\n return { completed: false, success: false, missing: true };\n }\n\n // Optional safety checks: ensure the bound request/key match expectations when available.\n if (attempt.request_id && attempt.request_id !== rec.requestId) {\n return {\n completed: true,\n success: false,\n errorMessage: 'Email recovery attempt request_id does not match requested requestId.',\n };\n }\n\n if (attempt.new_public_key && attempt.new_public_key !== rec.nearPublicKey) {\n const expected = ensureEd25519Prefix(rec.nearPublicKey);\n const actual = ensureEd25519Prefix(attempt.new_public_key);\n\n // The relayer/prover often forwards only the base58 part while the SDK\n // persists `ed25519:<base58>`. Compare normalized forms to avoid false\n // mismatches from prefix formatting differences.\n if (actual === expected) {\n // no-op; treat as matching\n } else {\n return {\n completed: true,\n success: false,\n errorMessage:\n `Email recovery new_public_key mismatch for request ${rec.requestId}. ` +\n `Expected ${expected}; got ${actual}. ` +\n 'This usually means the recovery email you sent was generated for a different device/attempt.',\n };\n }\n }\n\n const normalized = attempt.status.toLowerCase();\n\n if (normalized === 'complete' || normalized === 'completed') {\n return {\n completed: true,\n success: true,\n };\n }\n\n if (normalized.includes('failed')) {\n return {\n completed: true,\n success: false,\n errorMessage: attempt.error || `Email recovery failed (${attempt.status || 'unknown status'})`,\n };\n }\n\n return {\n completed: false,\n success: false,\n };\n } catch (err) {\n // Treat view errors as retryable; keep polling the view method.\n // eslint-disable-next-line no-console\n console.warn('[EmailRecoveryFlow] get_recovery_attempt view failed; will retry', err);\n return null;\n }\n }\n\n private async isRecoveryAccessKeyPresent(rec: PendingEmailRecovery): Promise<boolean | null> {\n try {\n await this.context.nearClient.viewAccessKey(rec.accountId, rec.nearPublicKey);\n return true;\n } catch (err: any) {\n const kind = typeof err?.kind === 'string' ? String(err.kind) : '';\n const short = typeof err?.short === 'string' ? String(err.short) : '';\n const msg = typeof err?.message === 'string' ? String(err.message) : '';\n\n if (\n /AccessKeyDoesNotExist/i.test(kind) ||\n /AccessKeyDoesNotExist/i.test(short) ||\n /access key does not exist/i.test(msg) ||\n /access key .*does not exist/i.test(msg)\n ) {\n return false;\n }\n\n // Unexpected view error: retryable; treat as unknown to avoid failing the flow.\n // eslint-disable-next-line no-console\n console.warn('[EmailRecoveryFlow] view_access_key failed while checking recovery key; will retry', err);\n return null;\n }\n }\n\n private buildPollingEventData(\n rec: PendingEmailRecovery,\n details: { transactionHash?: string; elapsedMs: number; pollCount: number }\n ): Record<string, unknown> {\n return {\n accountId: rec.accountId,\n requestId: rec.requestId,\n nearPublicKey: rec.nearPublicKey,\n transactionHash: details.transactionHash,\n elapsedMs: details.elapsedMs,\n pollCount: details.pollCount,\n };\n }\n\n private async sleepForPollInterval(ms: number): Promise<void> {\n await new Promise<void>(resolve => {\n this.pollIntervalResolver = resolve;\n this.pollingTimer = setTimeout(() => {\n this.pollIntervalResolver = undefined;\n this.pollingTimer = undefined;\n resolve();\n }, ms);\n }).finally(() => {\n this.pollIntervalResolver = undefined;\n });\n }\n\n private async pollUntil<T>(args: {\n intervalMs: number;\n timeoutMs: number;\n isCancelled: () => boolean;\n tick: (ctx: { elapsedMs: number; pollCount: number }) => Promise<PollTickResult<T>>;\n sleep?: (ms: number) => Promise<void>;\n now?: () => number;\n }): Promise<PollUntilResult<T>> {\n const now = args.now ?? Date.now;\n const sleep = args.sleep ?? this.sleepForPollInterval.bind(this);\n const startedAt = now();\n let pollCount = 0;\n\n while (!args.isCancelled()) {\n pollCount += 1;\n const elapsedMs = now() - startedAt;\n if (elapsedMs > args.timeoutMs) {\n return { status: 'timedOut', elapsedMs, pollCount };\n }\n\n const result = await args.tick({ elapsedMs, pollCount });\n if (result.done) {\n return { status: 'completed', value: result.value, elapsedMs, pollCount };\n }\n\n if (args.isCancelled()) {\n return { status: 'cancelled', elapsedMs, pollCount };\n }\n\n await sleep(args.intervalMs);\n }\n\n const elapsedMs = now() - startedAt;\n return { status: 'cancelled', elapsedMs, pollCount };\n }\n\n private async loadPending(\n accountId: AccountId,\n nearPublicKey?: string\n ): Promise<PendingEmailRecovery | null> {\n return this.pendingStore.get(accountId, nearPublicKey);\n }\n\n private async savePending(rec: PendingEmailRecovery): Promise<void> {\n await this.pendingStore.set(rec);\n this.pending = rec;\n }\n\n private async clearPending(accountId: AccountId, nearPublicKey?: string): Promise<void> {\n await this.pendingStore.clear(accountId, nearPublicKey);\n\n if (\n this.pending\n && this.pending.accountId === accountId\n && (!nearPublicKey || this.pending.nearPublicKey === nearPublicKey)\n ) {\n this.pending = null;\n }\n }\n\n getState() {\n return {\n phase: this.phase,\n pending: this.pending,\n error: this.error,\n };\n }\n\n async buildMailtoUrl(args: { accountId: string; nearPublicKey?: string }): Promise<string> {\n const { accountId, nearPublicKey } = args;\n this.cancelled = false;\n this.error = undefined;\n\n const nearAccountId = await this.assertValidAccountIdOrFail(3, accountId);\n const rec = await this.resolvePendingOrFail(\n 3,\n { accountId: nearAccountId, nearPublicKey },\n { allowErrorStatus: false }\n );\n\n if (rec.status === 'finalizing' || rec.status === 'complete') {\n await this.fail(3, 'Recovery email has already been processed on-chain for this request');\n }\n\n const mailtoUrl =\n rec.status === 'awaiting-email'\n ? await this.buildMailtoUrlAndUpdateStatus(rec)\n : this.buildMailtoUrlInternal(rec);\n this.emitAwaitEmail(rec, mailtoUrl);\n await this.options?.afterCall?.(true, undefined);\n return mailtoUrl;\n }\n\n async start(args: { accountId: string; recoveryEmail?: string }): Promise<{ mailtoUrl: string; nearPublicKey: string }> {\n const { accountId, recoveryEmail } = args;\n this.cancelled = false;\n this.error = undefined;\n this.phase = EmailRecoveryPhase.STEP_1_PREPARATION;\n\n this.emit({\n step: 1,\n phase: EmailRecoveryPhase.STEP_1_PREPARATION,\n status: EmailRecoveryStatus.PROGRESS,\n message: 'Preparing email recovery...',\n });\n\n const nearAccountId = await this.assertValidAccountIdOrFail(1, accountId);\n await this.assertSufficientBalance(nearAccountId);\n const canonicalEmail = this.getCanonicalRecoveryEmail(recoveryEmail);\n\n // Determine deviceNumber from on-chain authenticators\n const deviceNumber = await this.getNextDeviceNumberFromContract(nearAccountId);\n\n this.phase = EmailRecoveryPhase.STEP_2_TOUCH_ID_REGISTRATION;\n this.emit({\n step: 2,\n phase: EmailRecoveryPhase.STEP_2_TOUCH_ID_REGISTRATION,\n status: EmailRecoveryStatus.PROGRESS,\n message: 'Collecting passkey for email recovery...',\n });\n\n try {\n const confirm = await this.collectRecoveryCredentialOrFail(nearAccountId, deviceNumber);\n const derivedKeys = await this.deriveRecoveryKeysOrFail(nearAccountId, deviceNumber, confirm.credential);\n\n const rec: PendingEmailRecovery = {\n accountId: nearAccountId,\n recoveryEmail: canonicalEmail,\n deviceNumber,\n nearPublicKey: derivedKeys.nearPublicKey,\n requestId: generateEmailRecoveryRequestId(),\n encryptedVrfKeypair: derivedKeys.encryptedVrfKeypair,\n serverEncryptedVrfKeypair: derivedKeys.serverEncryptedVrfKeypair,\n vrfPublicKey: derivedKeys.vrfPublicKey,\n credential: confirm.credential,\n vrfChallenge: confirm.vrfChallenge || undefined,\n createdAt: Date.now(),\n status: 'awaiting-email',\n };\n\n const mailtoUrl = await this.buildMailtoUrlAndUpdateStatus(rec);\n\n this.emitAwaitEmail(rec, mailtoUrl);\n\n await this.options?.afterCall?.(true, undefined);\n\n return { mailtoUrl, nearPublicKey: rec.nearPublicKey };\n } catch (e: unknown) {\n const err = this.emitError(2, errorMessage(e) || 'Email recovery TouchID/derivation failed');\n await this.options?.afterCall?.(false);\n throw err;\n }\n }\n\n private buildMailtoUrlInternal(rec: PendingEmailRecovery): string {\n const { mailtoAddress } = this.getConfig();\n const to = encodeURIComponent(mailtoAddress);\n const subject = encodeURIComponent(`recover-${rec.requestId} ${rec.accountId} ${rec.nearPublicKey}`);\n const body = encodeURIComponent(`Recovering account ${rec.accountId} with a new passkey.`);\n return `mailto:${to}?subject=${subject}&body=${body}`;\n }\n\n private async buildMailtoUrlAndUpdateStatus(rec: PendingEmailRecovery): Promise<string> {\n rec.status = 'awaiting-add-key';\n await this.savePending(rec);\n return this.buildMailtoUrlInternal(rec);\n }\n\n async startPolling(args: { accountId: string; nearPublicKey?: string }): Promise<void> {\n const { accountId, nearPublicKey } = args;\n this.cancelled = false;\n this.error = undefined;\n\n const nearAccountId = await this.assertValidAccountIdOrFail(4, accountId);\n const rec = await this.resolvePendingOrFail(\n 4,\n { accountId: nearAccountId, nearPublicKey },\n { allowErrorStatus: false }\n );\n if (rec.status === 'complete' || rec.status === 'finalizing') {\n await this.options?.afterCall?.(true, undefined);\n return;\n }\n if (rec.status === 'awaiting-email') {\n await this.buildMailtoUrlAndUpdateStatus(rec);\n }\n\n await this.pollUntilAddKey(rec);\n await this.options?.afterCall?.(true, undefined);\n }\n\n stopPolling(): void {\n this.cancelled = true;\n if (this.pollingTimer) {\n clearTimeout(this.pollingTimer);\n this.pollingTimer = undefined;\n }\n if (this.pollIntervalResolver) {\n this.pollIntervalResolver();\n this.pollIntervalResolver = undefined;\n }\n }\n\n /**\n * Best-effort cancellation and local state reset so callers can retry.\n * This does not remove any passkey created in the browser/OS (WebAuthn has no delete API),\n * but it will stop polling and clear the pending IndexedDB record for the given key.\n */\n async cancelAndReset(args?: { accountId?: string; nearPublicKey?: string }): Promise<void> {\n this.stopPolling();\n\n const normalizedAccountId = (args?.accountId || this.pending?.accountId || '').toString().trim();\n const nearPublicKey = (args?.nearPublicKey || this.pending?.nearPublicKey || '').toString().trim();\n\n if (normalizedAccountId) {\n try {\n await this.clearPending(toAccountId(normalizedAccountId), nearPublicKey);\n } catch {\n // best-effort\n }\n }\n\n this.pending = null;\n this.error = undefined;\n this.phase = EmailRecoveryPhase.STEP_1_PREPARATION;\n }\n\n async finalize(args: { accountId: string; nearPublicKey?: string }): Promise<void> {\n const { accountId, nearPublicKey } = args;\n this.cancelled = false;\n this.error = undefined;\n\n const nearAccountId = await this.assertValidAccountIdOrFail(4, accountId);\n const rec = await this.resolvePendingOrFail(\n 4,\n { accountId: nearAccountId, nearPublicKey },\n { allowErrorStatus: true }\n );\n\n this.emit({\n step: 0,\n phase: EmailRecoveryPhase.RESUMED_FROM_PENDING,\n status: EmailRecoveryStatus.PROGRESS,\n message: 'Resuming email recovery from pending state...',\n data: {\n accountId: rec.accountId,\n nearPublicKey: rec.nearPublicKey,\n status: rec.status,\n },\n } as EmailRecoverySSEEvent & { data: Record<string, unknown> });\n\n if (rec.status === 'complete') {\n this.phase = EmailRecoveryPhase.STEP_6_COMPLETE;\n this.emit({\n step: 6,\n phase: EmailRecoveryPhase.STEP_6_COMPLETE,\n status: EmailRecoveryStatus.SUCCESS,\n message: 'Email recovery already completed for this key.',\n });\n await this.options?.afterCall?.(true, undefined);\n return;\n }\n\n // Ensure verification has completed successfully before finalizing registration.\n await this.pollUntilAddKey(rec);\n await this.finalizeRegistration(rec);\n await this.options?.afterCall?.(true, undefined);\n }\n\n private async pollUntilAddKey(rec: PendingEmailRecovery): Promise<void> {\n const { pollingIntervalMs, maxPollingDurationMs } = this.getConfig();\n this.phase = EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT;\n this.pollingStartedAt = Date.now();\n let sawAttempt = false;\n\n const pollResult = await this.pollUntil<VerificationOutcome>({\n intervalMs: pollingIntervalMs,\n timeoutMs: maxPollingDurationMs,\n isCancelled: () => this.cancelled,\n tick: async ({ elapsedMs, pollCount }) => {\n const verification = await this.checkViaEmailRecovererAttempt(rec);\n if (verification && !verification.missing) {\n sawAttempt = true;\n }\n\n let completed = verification?.completed === true;\n let success = verification?.success === true;\n let errorMessage = verification?.errorMessage;\n let transactionHash: string | undefined;\n\n if (verification?.missing) {\n const hasKey = await this.isRecoveryAccessKeyPresent(rec);\n if (hasKey === true) {\n completed = true;\n success = true;\n } else if (hasKey === false && sawAttempt) {\n completed = true;\n success = false;\n errorMessage =\n 'Email recovery attempt was cleared on-chain before completion. Please resend the recovery email or restart the flow.';\n } else if (hasKey === null) {\n // Retry on unexpected view errors while checking access key presence.\n return { done: false };\n }\n }\n\n this.emit({\n step: 4,\n phase: EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT,\n status: EmailRecoveryStatus.PROGRESS,\n message: completed && success\n ? `Email recovery completed for request ${rec.requestId}; finalizing registration`\n : `Waiting for email recovery for request ${rec.requestId}`,\n data: this.buildPollingEventData(rec, {\n transactionHash,\n elapsedMs,\n pollCount,\n }),\n } as EmailRecoverySSEEvent & { data: Record<string, unknown> });\n\n if (!completed) {\n return { done: false };\n }\n\n if (!success) {\n return {\n done: true,\n value: {\n outcome: 'failed',\n errorMessage: errorMessage || 'Email recovery failed',\n },\n };\n }\n\n return { done: true, value: { outcome: 'verified' } };\n },\n });\n\n if (pollResult.status === 'completed') {\n if (pollResult.value.outcome === 'failed') {\n const err = this.emitError(4, pollResult.value.errorMessage);\n rec.status = 'error';\n await this.savePending(rec);\n await this.options?.afterCall?.(false);\n throw err;\n }\n\n rec.status = 'finalizing';\n await this.savePending(rec);\n return;\n }\n\n if (pollResult.status === 'timedOut') {\n const err = this.emitError(4, 'Timed out waiting for recovery email to be processed on-chain');\n rec.status = 'error';\n await this.savePending(rec);\n await this.options?.afterCall?.(false);\n throw err;\n }\n\n const err = this.emitError(4, 'Email recovery polling was cancelled');\n await this.options?.afterCall?.(false);\n throw err;\n }\n\n private initializeNonceManager(\n rec: PendingEmailRecovery\n ): {\n nonceManager: ReturnType<PasskeyManagerContext['webAuthnManager']['getNonceManager']>;\n accountId: AccountId;\n } {\n const nonceManager = this.context.webAuthnManager.getNonceManager();\n const accountId = toAccountId(rec.accountId);\n nonceManager.initializeUser(accountId, rec.nearPublicKey);\n return { nonceManager, accountId };\n }\n\n /*\n * Signs a `link_device_register_user` contract call\n */\n private async signNewDevice2RegistrationTx(rec: PendingEmailRecovery, accountId: AccountId): Promise<SignedTransaction> {\n const vrfChallenge = rec.vrfChallenge;\n if (!vrfChallenge) {\n return this.fail(5, 'Missing VRF challenge for email recovery registration');\n }\n\n const registrationResult = await this.context.webAuthnManager.signDevice2RegistrationWithStoredKey({\n nearAccountId: accountId,\n credential: rec.credential,\n vrfChallenge,\n deterministicVrfPublicKey: rec.vrfPublicKey,\n deviceNumber: rec.deviceNumber,\n });\n\n if (!registrationResult.success || !registrationResult.signedTransaction) {\n await this.fail(5, registrationResult.error || 'Failed to sign email recovery registration transaction');\n }\n\n return registrationResult.signedTransaction;\n }\n\n private async broadcastRegistrationTxAndWaitFinal(\n rec: PendingEmailRecovery,\n signedTx: SignedTransaction\n ): Promise<string | undefined> {\n let txResult: FinalExecutionOutcome;\n try {\n txResult = await this.context.nearClient.sendTransaction(\n signedTx,\n DEFAULT_WAIT_STATUS.linkDeviceRegistration\n );\n } catch (err: unknown) {\n const msg = errorMessage(err) || 'Failed to broadcast email recovery registration transaction (insufficient funds or RPC error)';\n throw new Error(msg);\n }\n\n const txHash = this.getTxHash(txResult);\n\n // Contract can return `{ verified: false, registration_info: null }` without failing the tx.\n // When that happens, the authenticator was NOT registered on-chain, so we must not proceed\n // with local persistence + auto-login.\n const linkDeviceResult = parseLinkDeviceRegisterUserResponse(txResult);\n if (linkDeviceResult?.verified === false) {\n const logs = this.extractNearExecutionLogs(txResult);\n const isStaleChallenge = logs.some((log) => /StaleChallenge|freshness validation failed/i.test(log));\n const txHint = txHash ? ` (tx: ${txHash})` : '';\n const code = isStaleChallenge\n ? EmailRecoveryErrorCode.VRF_CHALLENGE_EXPIRED\n : EmailRecoveryErrorCode.REGISTRATION_NOT_VERIFIED;\n const message = isStaleChallenge\n ? `Timed out finalizing registration (VRF challenge expired). Please restart email recovery and try again${txHint}.`\n : `Registration did not verify on-chain. Please try again${txHint}.`;\n throw new EmailRecoveryError(message, code, {\n accountId: rec.accountId,\n nearPublicKey: rec.nearPublicKey,\n transactionHash: txHash,\n logs,\n result: linkDeviceResult,\n });\n }\n\n if (txHash) {\n this.emit({\n step: 5,\n phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,\n status: EmailRecoveryStatus.PROGRESS,\n message: 'Registration transaction confirmed',\n data: {\n accountId: rec.accountId,\n nearPublicKey: rec.nearPublicKey,\n transactionHash: txHash,\n },\n } as EmailRecoverySSEEvent & { data: Record<string, unknown> });\n }\n\n return txHash;\n }\n\n private getTxHash(outcome: FinalExecutionOutcome): string | undefined {\n const txUnknown: unknown = outcome.transaction;\n if (txUnknown && typeof txUnknown === 'object') {\n const hash = (txUnknown as Record<string, unknown>).hash;\n if (typeof hash === 'string' && hash.length > 0) return hash;\n }\n\n const fallback = (outcome as unknown as Record<string, unknown>).transaction_hash;\n return typeof fallback === 'string' && fallback.length > 0 ? fallback : undefined;\n }\n\n private extractNearExecutionLogs(outcome: FinalExecutionOutcome): string[] {\n const logs: string[] = [];\n for (const entry of outcome.transaction_outcome.outcome.logs) {\n logs.push(String(entry));\n }\n for (const receipt of outcome.receipts_outcome) {\n for (const entry of receipt.outcome.logs) {\n logs.push(String(entry));\n }\n }\n return logs;\n }\n\n private mapAuthenticatorsFromContract(\n authenticators: Array<{ credentialId: string; authenticator: StoredAuthenticator }>\n ) {\n return authenticators.map(({ authenticator }) => ({\n credentialId: authenticator.credentialId,\n credentialPublicKey: authenticator.credentialPublicKey,\n transports: authenticator.transports,\n name: authenticator.name,\n registered: authenticator.registered.toISOString(),\n vrfPublicKey: authenticator.vrfPublicKeys?.[0] || '',\n deviceNumber: authenticator.deviceNumber,\n }));\n }\n\n private async syncAuthenticatorsBestEffort(accountId: AccountId): Promise<boolean> {\n try {\n const { syncAuthenticatorsContractCall } = await import('../rpcCalls');\n const authenticators = await syncAuthenticatorsContractCall(\n this.context.nearClient,\n this.context.configs.contractId,\n accountId\n );\n\n const mappedAuthenticators = this.mapAuthenticatorsFromContract(authenticators);\n await IndexedDBManager.clientDB.syncAuthenticatorsFromContract(accountId, mappedAuthenticators);\n return true;\n } catch (err) {\n console.warn('[EmailRecoveryFlow] Failed to sync authenticators after recovery:', err);\n return false;\n }\n }\n\n private async setLastUserBestEffort(accountId: AccountId, deviceNumber: number): Promise<boolean> {\n try {\n await IndexedDBManager.clientDB.setLastUser(accountId, deviceNumber);\n return true;\n } catch (err) {\n console.warn('[EmailRecoveryFlow] Failed to set last user after recovery:', err);\n return false;\n }\n }\n\n private async updateNonceBestEffort(\n nonceManager: ReturnType<PasskeyManagerContext['webAuthnManager']['getNonceManager']>,\n signedTx: SignedTransaction\n ): Promise<void> {\n try {\n const txNonce = signedTx.transaction.nonce;\n if (txNonce != null) {\n await nonceManager.updateNonceFromBlockchain(\n this.context.nearClient,\n String(txNonce)\n );\n }\n } catch {\n // best-effort; do not fail flow\n }\n }\n\n private async persistRecoveredUserData(rec: PendingEmailRecovery, accountId: AccountId): Promise<void> {\n const { webAuthnManager } = this.context;\n\n const payload: StoreUserDataPayload = {\n nearAccountId: accountId,\n deviceNumber: rec.deviceNumber,\n clientNearPublicKey: rec.nearPublicKey,\n lastUpdated: Date.now(),\n passkeyCredential: {\n id: rec.credential.id,\n rawId: rec.credential.rawId,\n },\n encryptedVrfKeypair: {\n encryptedVrfDataB64u: rec.encryptedVrfKeypair.encryptedVrfDataB64u,\n chacha20NonceB64u: rec.encryptedVrfKeypair.chacha20NonceB64u,\n },\n serverEncryptedVrfKeypair: rec.serverEncryptedVrfKeypair || undefined,\n };\n\n await webAuthnManager.storeUserData(payload);\n }\n\n /**\n * Explicitly persist the authenticator from the recovery record into the local cache.\n * This ensures the key is available immediately, bridging the gap before RPC sync sees it.\n */\n private async persistAuthenticatorBestEffort(rec: PendingEmailRecovery, accountId: AccountId): Promise<void> {\n try {\n const { webAuthnManager } = this.context;\n const attestationB64u = rec.credential.response.attestationObject;\n const credentialPublicKey = await webAuthnManager.extractCosePublicKey(attestationB64u);\n\n await webAuthnManager.storeAuthenticator({\n nearAccountId: accountId,\n deviceNumber: rec.deviceNumber,\n credentialId: rec.credential.rawId,\n credentialPublicKey,\n transports: ['internal'],\n name: `Device ${rec.deviceNumber} Passkey for ${rec.accountId.split('.')[0]}`,\n registered: new Date().toISOString(),\n syncedAt: new Date().toISOString(), // Local truth is fresh\n vrfPublicKey: rec.vrfPublicKey,\n });\n console.log('[EmailRecoveryFlow] Locally persisted recovered authenticator for immediate use.');\n } catch (e) {\n console.error('[EmailRecoveryFlow] Failed to locally persist authenticator (critical for immediate export):', e);\n // We log error but don't rethrow to avoid crashing the final success UI.\n }\n }\n\n private async markCompleteAndClearPending(rec: PendingEmailRecovery): Promise<void> {\n rec.status = 'complete';\n await this.savePending(rec);\n await this.clearPending(rec.accountId, rec.nearPublicKey);\n }\n\n private async assertVrfActiveForAccount(accountId: AccountId, message: string): Promise<void> {\n const vrfStatus = await this.context.webAuthnManager.checkVrfStatus();\n const vrfActiveForAccount =\n vrfStatus.active\n && vrfStatus.nearAccountId\n && String(vrfStatus.nearAccountId) === String(accountId);\n if (!vrfActiveForAccount) {\n throw new Error(message);\n }\n }\n\n private async finalizeLocalLoginState(accountId: AccountId, deviceNumber: number): Promise<void> {\n const { webAuthnManager } = this.context;\n await webAuthnManager.setLastUser(accountId, deviceNumber);\n await webAuthnManager.initializeCurrentUser(accountId, this.context.nearClient);\n try { await getLoginSession(this.context, accountId); } catch { }\n }\n\n private async tryShamirUnlock(\n rec: PendingEmailRecovery,\n accountId: AccountId,\n deviceNumber: number\n ): Promise<boolean> {\n if (\n !rec.serverEncryptedVrfKeypair\n || !rec.serverEncryptedVrfKeypair.serverKeyId\n || !this.context.configs.vrfWorkerConfigs?.shamir3pass?.relayServerUrl\n ) {\n return false;\n }\n\n try {\n const { webAuthnManager } = this.context;\n const unlockResult = await webAuthnManager.shamir3PassDecryptVrfKeypair({\n nearAccountId: accountId,\n kek_s_b64u: rec.serverEncryptedVrfKeypair.kek_s_b64u,\n ciphertextVrfB64u: rec.serverEncryptedVrfKeypair.ciphertextVrfB64u,\n serverKeyId: rec.serverEncryptedVrfKeypair.serverKeyId,\n });\n\n if (!unlockResult.success) {\n return false;\n }\n\n await this.assertVrfActiveForAccount(accountId, 'VRF session inactive after Shamir3Pass unlock');\n await this.finalizeLocalLoginState(accountId, deviceNumber);\n return true;\n } catch (err) {\n console.warn('[EmailRecoveryFlow] Shamir 3-pass unlock failed, falling back to TouchID', err);\n return false;\n }\n }\n\n private async tryTouchIdUnlock(\n rec: PendingEmailRecovery,\n accountId: AccountId,\n deviceNumber: number\n ): Promise<{ success: boolean; reason?: string }> {\n try {\n const { webAuthnManager } = this.context;\n const authChallenge = createRandomVRFChallenge() as VRFChallenge;\n\n const storedCredentialId = String(rec.credential?.rawId || rec.credential?.id || '').trim();\n const credentialIds = storedCredentialId ? [storedCredentialId] : [];\n const authenticators = credentialIds.length > 0\n ? []\n : await webAuthnManager.getAuthenticatorsByUser(accountId);\n const authCredential = await webAuthnManager.getAuthenticationCredentialsSerializedDualPrf({\n nearAccountId: accountId,\n challenge: authChallenge,\n credentialIds: credentialIds.length > 0 ? credentialIds : authenticators.map((a) => a.credentialId),\n });\n\n if (storedCredentialId && authCredential.rawId !== storedCredentialId) {\n return {\n success: false,\n reason: 'Wrong passkey selected during recovery auto-login; please use the newly recovered passkey.',\n };\n }\n\n const vrfUnlockResult = await webAuthnManager.unlockVRFKeypair({\n nearAccountId: accountId,\n encryptedVrfKeypair: rec.encryptedVrfKeypair,\n credential: authCredential,\n });\n\n if (!vrfUnlockResult.success) {\n return { success: false, reason: vrfUnlockResult.error || 'VRF unlock failed during auto-login' };\n }\n\n await this.assertVrfActiveForAccount(accountId, 'VRF session inactive after TouchID unlock');\n await this.finalizeLocalLoginState(accountId, deviceNumber);\n return { success: true };\n } catch (err: unknown) {\n return { success: false, reason: errorMessage(err) || String(err) };\n }\n }\n\n private async handleAutoLoginFailure(reason: string, err?: unknown): Promise<AutoLoginResult> {\n console.warn('[EmailRecoveryFlow] Auto-login failed after recovery', err ?? reason);\n try {\n await this.context.webAuthnManager.clearVrfSession();\n } catch { }\n return { success: false, reason };\n }\n\n private async finalizeRegistration(rec: PendingEmailRecovery): Promise<void> {\n this.phase = EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION;\n this.emit({\n step: 5,\n phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,\n status: EmailRecoveryStatus.PROGRESS,\n message: 'Finalizing email recovery registration...',\n data: {\n accountId: rec.accountId,\n nearPublicKey: rec.nearPublicKey,\n },\n } as EmailRecoverySSEEvent & { data: Record<string, unknown> });\n\n try {\n const { nonceManager, accountId } = this.initializeNonceManager(rec);\n const signedTx = await this.signNewDevice2RegistrationTx(rec, accountId);\n const txHash = await this.broadcastRegistrationTxAndWaitFinal(rec, signedTx);\n if (!txHash) {\n console.warn('[EmailRecoveryFlow] Registration transaction confirmed without hash; continuing local persistence');\n }\n\n // CRITICAL: Persist local state immediately.\n // 1. Store the new user record (Device N) so that `getLastUser()` finds it.\n await this.persistRecoveredUserData(rec, accountId);\n\n // 2. Sync authenticators (RPC might be stale, but we try).\n await this.syncAuthenticatorsBestEffort(accountId);\n\n // 3. FORCE-SAVE the local authenticator from our recovery record.\n // This is crucial because RPC sync might be slow/empty immediately after TX.\n // We must ensure the new key is in the DB so `ensureCurrentPasskey` finds it.\n // We do this AFTER sync to ensure it's not wiped by a stale sync.\n await this.persistAuthenticatorBestEffort(rec, accountId);\n\n // 4. Set as active user to ensure immediate subsequent calls use this identity.\n await this.setLastUserBestEffort(accountId, rec.deviceNumber);\n\n await this.updateNonceBestEffort(nonceManager, signedTx);\n\n this.emitAutoLoginEvent(EmailRecoveryStatus.PROGRESS, 'Attempting auto-login with recovered device...', {\n autoLogin: 'progress',\n });\n\n const autoLoginResult = await this.attemptAutoLogin(rec);\n if (autoLoginResult.success) {\n this.emitAutoLoginEvent(EmailRecoveryStatus.SUCCESS, `Welcome ${accountId}`, {\n autoLogin: 'success',\n });\n } else {\n this.emitAutoLoginEvent(EmailRecoveryStatus.ERROR, 'Auto-login failed; please log in manually on this device.', {\n error: autoLoginResult.reason,\n autoLogin: 'error',\n });\n }\n\n await this.markCompleteAndClearPending(rec);\n\n this.phase = EmailRecoveryPhase.STEP_6_COMPLETE;\n this.emit({\n step: 6,\n phase: EmailRecoveryPhase.STEP_6_COMPLETE,\n status: EmailRecoveryStatus.SUCCESS,\n message: 'Email recovery completed successfully',\n data: {\n accountId: rec.accountId,\n nearPublicKey: rec.nearPublicKey,\n },\n } as EmailRecoverySSEEvent & { data: Record<string, unknown> });\n } catch (e: unknown) {\n rec.status = 'error';\n await this.savePending(rec).catch(() => { });\n const original = e instanceof Error\n ? e\n : new Error(errorMessage(e) || 'Email recovery finalization failed');\n const err = this.emitError(5, original);\n await this.options?.afterCall?.(false);\n throw err;\n }\n }\n\n private async attemptAutoLogin(rec: PendingEmailRecovery): Promise<AutoLoginResult> {\n try {\n const accountId = toAccountId(rec.accountId);\n const deviceNumber = parseDeviceNumber(rec.deviceNumber, { min: 1 });\n if (deviceNumber === null) {\n return this.handleAutoLoginFailure(\n `Invalid deviceNumber for auto-login: ${String(rec.deviceNumber)}`\n );\n }\n\n const shamirUnlocked = await this.tryShamirUnlock(rec, accountId, deviceNumber);\n if (shamirUnlocked) {\n return { success: true, method: 'shamir' };\n }\n\n const touchIdResult = await this.tryTouchIdUnlock(rec, accountId, deviceNumber);\n if (touchIdResult.success) {\n return { success: true, method: 'touchid' };\n }\n\n return this.handleAutoLoginFailure(touchIdResult.reason || 'Auto-login failed');\n } catch (err: unknown) {\n return this.handleAutoLoginFailure(errorMessage(err) || String(err), err);\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1128,6 +1128,7 @@ var TatchiPasskey = class {
|
|
|
1128
1128
|
const { accountId, recoveryEmail, options } = args;
|
|
1129
1129
|
if (this.shouldUseWalletIframe()) try {
|
|
1130
1130
|
const router = await this.requireWalletIframeRouter();
|
|
1131
|
+
const normalizedRecoveryEmail = typeof recoveryEmail === "string" && recoveryEmail.trim().length > 0 ? recoveryEmail.trim() : void 0;
|
|
1131
1132
|
const confirmerText = options?.confirmerText;
|
|
1132
1133
|
const confirmationConfig = options?.confirmationConfig;
|
|
1133
1134
|
const safeOptions = {
|
|
@@ -1136,7 +1137,7 @@ var TatchiPasskey = class {
|
|
|
1136
1137
|
};
|
|
1137
1138
|
const res = await router.startEmailRecovery({
|
|
1138
1139
|
accountId,
|
|
1139
|
-
recoveryEmail,
|
|
1140
|
+
recoveryEmail: normalizedRecoveryEmail,
|
|
1140
1141
|
onEvent: options?.onEvent,
|
|
1141
1142
|
options: Object.keys(safeOptions).length > 0 ? safeOptions : void 0
|
|
1142
1143
|
});
|