@tatchi-xyz/sdk 0.31.0 → 0.31.1
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/IndexedDBManager/passkeyClientDB.js +2 -2
- package/dist/cjs/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/faucets/createAccountRelayServer.js +9 -8
- package/dist/cjs/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/login.js +1 -1
- package/dist/cjs/core/TatchiPasskey/login.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/registration.js +67 -56
- package/dist/cjs/core/TatchiPasskey/registration.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js +1 -10
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +58 -67
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +74 -75
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js +17 -7
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/index.js +3 -3
- package/dist/cjs/core/WebAuthnManager/index.js.map +1 -1
- package/dist/cjs/core/defaultConfigs.js +3 -1
- package/dist/cjs/core/defaultConfigs.js.map +1 -1
- package/dist/cjs/react/components/AccountMenuButton/TransactionSettingsSection.js +3 -3
- package/dist/cjs/react/components/AccountMenuButton/TransactionSettingsSection.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CRlobBrN.css → PasskeyAuthMenu-D2eRb2-S.css} +3 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/PasskeyAuthMenu-D2eRb2-S.css.map +1 -0
- package/dist/cjs/react/components/PasskeyAuthMenu/preload.js +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/preload.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/shell.js +52 -13
- package/dist/cjs/react/components/PasskeyAuthMenu/shell.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/skeleton.js +4 -2
- package/dist/cjs/react/components/PasskeyAuthMenu/skeleton.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js +5 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js.map +1 -1
- package/dist/cjs/react/index.js +1 -1
- package/dist/cjs/react/src/core/IndexedDBManager/passkeyClientDB.js +2 -2
- package/dist/cjs/react/src/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
- package/dist/cjs/react/src/core/TatchiPasskey/faucets/createAccountRelayServer.js +9 -8
- package/dist/cjs/react/src/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
- package/dist/cjs/react/src/core/TatchiPasskey/login.js +1 -1
- package/dist/cjs/react/src/core/TatchiPasskey/login.js.map +1 -1
- package/dist/cjs/react/src/core/TatchiPasskey/registration.js +67 -56
- package/dist/cjs/react/src/core/TatchiPasskey/registration.js.map +1 -1
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js +1 -10
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js.map +1 -1
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +58 -67
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +74 -75
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js +17 -7
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js.map +1 -1
- package/dist/cjs/react/src/core/WebAuthnManager/index.js +3 -3
- package/dist/cjs/react/src/core/WebAuthnManager/index.js.map +1 -1
- package/dist/cjs/react/src/core/defaultConfigs.js +3 -1
- package/dist/cjs/react/src/core/defaultConfigs.js.map +1 -1
- package/dist/cjs/server/core/AuthService.js +49 -6
- package/dist/cjs/server/core/AuthService.js.map +1 -1
- package/dist/cjs/server/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/esm/core/IndexedDBManager/passkeyClientDB.js +2 -2
- package/dist/esm/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/faucets/createAccountRelayServer.js +9 -8
- package/dist/esm/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/login.js +1 -1
- package/dist/esm/core/TatchiPasskey/login.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/registration.js +67 -56
- package/dist/esm/core/TatchiPasskey/registration.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js +1 -10
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +58 -67
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +74 -75
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js +17 -7
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/index.js +3 -3
- package/dist/esm/core/WebAuthnManager/index.js.map +1 -1
- package/dist/esm/core/defaultConfigs.js +3 -1
- package/dist/esm/core/defaultConfigs.js.map +1 -1
- package/dist/esm/react/components/AccountMenuButton/TransactionSettingsSection.js +3 -3
- package/dist/esm/react/components/AccountMenuButton/TransactionSettingsSection.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-D2VHZ04W.css → PasskeyAuthMenu-qTHAv58Z.css} +3 -1
- package/dist/esm/react/components/PasskeyAuthMenu/PasskeyAuthMenu-qTHAv58Z.css.map +1 -0
- package/dist/esm/react/components/PasskeyAuthMenu/preload.js +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/preload.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/shell.js +52 -13
- package/dist/esm/react/components/PasskeyAuthMenu/shell.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/skeleton.js +4 -2
- package/dist/esm/react/components/PasskeyAuthMenu/skeleton.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js +5 -1
- package/dist/esm/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js.map +1 -1
- package/dist/esm/react/index.js +1 -1
- package/dist/esm/react/src/core/IndexedDBManager/passkeyClientDB.js +2 -2
- package/dist/esm/react/src/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
- package/dist/esm/react/src/core/TatchiPasskey/faucets/createAccountRelayServer.js +9 -8
- package/dist/esm/react/src/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
- package/dist/esm/react/src/core/TatchiPasskey/login.js +1 -1
- package/dist/esm/react/src/core/TatchiPasskey/login.js.map +1 -1
- package/dist/esm/react/src/core/TatchiPasskey/registration.js +67 -56
- package/dist/esm/react/src/core/TatchiPasskey/registration.js.map +1 -1
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js +1 -10
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js.map +1 -1
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +58 -67
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +74 -75
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js +17 -7
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js.map +1 -1
- package/dist/esm/react/src/core/WebAuthnManager/index.js +3 -3
- package/dist/esm/react/src/core/WebAuthnManager/index.js.map +1 -1
- package/dist/esm/react/src/core/defaultConfigs.js +3 -1
- package/dist/esm/react/src/core/defaultConfigs.js.map +1 -1
- package/dist/esm/react/styles/styles.css +2 -0
- package/dist/esm/sdk/{EmailRecovery-Dl8b4ONg.js → EmailRecovery-Y7rurd4B.js} +3 -3
- package/dist/esm/sdk/{EmailRecovery-v9oNO2Tc.js → EmailRecovery-lsjLWApQ.js} +1 -1
- package/dist/esm/sdk/{IndexedDBManager-B1cUvdyY.js → IndexedDBManager-CmdN7smS.js} +3 -3
- package/dist/esm/sdk/{createAdapters-Dv7ZJPf1.js → createAdapters-4c8mBiD5.js} +2 -11
- package/dist/esm/sdk/{createAdapters-Dv7ZJPf1.js.map → createAdapters-4c8mBiD5.js.map} +1 -1
- package/dist/esm/sdk/{createAdapters-1Hmc1vVC.js → createAdapters-DF32SIZa.js} +1 -10
- package/dist/esm/sdk/{defaultConfigs-BmCU1_qI.js → defaultConfigs-BQqiXif-.js} +3 -1
- package/dist/esm/sdk/{emailRecovery-4J-g9tlY.js → emailRecovery-C0LSDleV.js} +5 -5
- package/dist/esm/sdk/{getDeviceNumber-f8bfPB9U.js → getDeviceNumber-WiNzKx1x.js} +4 -2
- package/dist/esm/sdk/{getDeviceNumber-f8bfPB9U.js.map → getDeviceNumber-WiNzKx1x.js.map} +1 -1
- package/dist/esm/sdk/{linkDevice-C98klpcE.js → linkDevice-Ds1GNIDk.js} +4 -4
- package/dist/esm/sdk/{localOnly-40zxrBMm.js → localOnly-COpDBMkm.js} +2 -2
- package/dist/esm/sdk/{localOnly-40zxrBMm.js.map → localOnly-COpDBMkm.js.map} +1 -1
- package/dist/esm/sdk/{localOnly-BZPBj14l.js → localOnly-DQQuqgjJ.js} +1 -1
- package/dist/esm/sdk/{login-DnROv3eA.js → login-BKhTuGcy.js} +3 -3
- package/dist/esm/sdk/offline-export-app.js +29 -19
- package/dist/esm/sdk/offline-export-app.js.map +1 -1
- package/dist/esm/sdk/{registration-BP9M3tE1.js → registration-BR2G9tz_.js} +59 -68
- package/dist/esm/sdk/{registration-MrAOC8Ub.js → registration-R70lvG_o.js} +60 -69
- package/dist/esm/sdk/registration-R70lvG_o.js.map +1 -0
- package/dist/esm/sdk/{router-BEGGuWaB.js → router-2aGn-CTp.js} +1 -1
- package/dist/esm/sdk/{rpcCalls-CMzj_Va_.js → rpcCalls-BPI0icZG.js} +2 -2
- package/dist/esm/sdk/{rpcCalls-B44MZora.js → rpcCalls-BW3M_q3-.js} +1 -1
- package/dist/esm/sdk/{scanDevice-Cp-r-Z2T.js → scanDevice-BBSehlMx.js} +4 -4
- package/dist/esm/sdk/{syncAccount-CqWCmBVb.js → syncAccount-DEZHBiRa.js} +4 -4
- package/dist/esm/sdk/{syncAccount-Dt5jJbEB.js → syncAccount-DHKtl-xh.js} +2 -2
- package/dist/esm/sdk/{transactions-DAZrPW-6.js → transactions-Cg1TIUyK.js} +76 -77
- package/dist/esm/sdk/{transactions-CrjP8yPD.js → transactions-CxsklyCK.js} +77 -78
- package/dist/esm/sdk/transactions-CxsklyCK.js.map +1 -0
- package/dist/esm/sdk/wallet-iframe-host.js +116 -94
- package/dist/esm/server/core/AuthService.js +49 -6
- package/dist/esm/server/core/AuthService.js.map +1 -1
- package/dist/esm/server/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker_bg.wasm +0 -0
- package/dist/types/src/__tests__/setup/bootstrap.d.ts.map +1 -1
- package/dist/types/src/core/IndexedDBManager/passkeyClientDB.d.ts +1 -1
- package/dist/types/src/core/IndexedDBManager/passkeyClientDB.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/faucets/createAccountRelayServer.d.ts +6 -6
- package/dist/types/src/core/TatchiPasskey/faucets/createAccountRelayServer.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/registration.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.d.ts +0 -5
- package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/index.d.ts +1 -1
- package/dist/types/src/core/WebAuthnManager/index.d.ts.map +1 -1
- package/dist/types/src/core/defaultConfigs.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/preload.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/shell.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/skeleton.d.ts +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/skeleton.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.d.ts.map +1 -1
- package/dist/types/src/server/core/AuthService.d.ts.map +1 -1
- package/dist/workers/offline-export-sw.js +156 -1
- package/dist/workers/wasm_vrf_worker_bg.wasm +0 -0
- package/dist/workers/web3authn-signer.worker.js +1360 -2
- package/dist/workers/web3authn-vrf.worker.js +2857 -2
- package/package.json +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/PasskeyAuthMenu-CRlobBrN.css.map +0 -1
- package/dist/esm/react/components/PasskeyAuthMenu/PasskeyAuthMenu-D2VHZ04W.css.map +0 -1
- package/dist/esm/sdk/registration-MrAOC8Ub.js.map +0 -1
- package/dist/esm/sdk/transactions-CrjP8yPD.js.map +0 -1
|
@@ -45,7 +45,7 @@ async function registerPasskeyInternal(context, nearAccountId, options, authenti
|
|
|
45
45
|
step: 1,
|
|
46
46
|
phase: RegistrationPhase.STEP_1_WEBAUTHN_VERIFICATION,
|
|
47
47
|
status: RegistrationStatus.PROGRESS,
|
|
48
|
-
message: "
|
|
48
|
+
message: "Generating passkey credential..."
|
|
49
49
|
});
|
|
50
50
|
const confirmationConfig = {
|
|
51
51
|
uiMode: "modal",
|
|
@@ -61,26 +61,13 @@ async function registerPasskeyInternal(context, nearAccountId, options, authenti
|
|
|
61
61
|
});
|
|
62
62
|
const credential = registrationSession.credential;
|
|
63
63
|
const vrfChallenge = registrationSession.vrfChallenge;
|
|
64
|
+
const transactionContext = registrationSession.transactionContext;
|
|
64
65
|
onEvent?.({
|
|
65
66
|
step: 1,
|
|
66
67
|
phase: RegistrationPhase.STEP_1_WEBAUTHN_VERIFICATION,
|
|
67
68
|
status: RegistrationStatus.SUCCESS,
|
|
68
69
|
message: "WebAuthn ceremony successful"
|
|
69
70
|
});
|
|
70
|
-
const canRegisterUserPromise = webAuthnManager.checkCanRegisterUser({
|
|
71
|
-
contractId: context.configs.contractId,
|
|
72
|
-
credential,
|
|
73
|
-
vrfChallenge,
|
|
74
|
-
onEvent: (progress) => {
|
|
75
|
-
console.debug(`Registration progress: ${progress.step} - ${progress.message}`);
|
|
76
|
-
onEvent?.({
|
|
77
|
-
step: 3,
|
|
78
|
-
phase: RegistrationPhase.STEP_3_CONTRACT_PRE_CHECK,
|
|
79
|
-
status: RegistrationStatus.PROGRESS,
|
|
80
|
-
message: `${progress.message}`
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
71
|
const deterministicVrfKeyResult = await webAuthnManager.deriveVrfKeypair({
|
|
85
72
|
credential,
|
|
86
73
|
nearAccountId,
|
|
@@ -112,12 +99,6 @@ async function registerPasskeyInternal(context, nearAccountId, options, authenti
|
|
|
112
99
|
if (!derived.success || !derived.clientVerifyingShareB64u) throw new Error(derived.error || "Failed to derive threshold client verifying share");
|
|
113
100
|
thresholdClientVerifyingShareB64u = derived.clientVerifyingShareB64u;
|
|
114
101
|
}
|
|
115
|
-
const canRegisterUserResult = await canRegisterUserPromise;
|
|
116
|
-
if (!canRegisterUserResult.verified) {
|
|
117
|
-
console.error(canRegisterUserResult);
|
|
118
|
-
const errorMessage = canRegisterUserResult.error || "User verification failed - account may already exist or contract is unreachable";
|
|
119
|
-
throw new Error(`Web3Authn contract registration check failed: ${errorMessage}`);
|
|
120
|
-
}
|
|
121
102
|
onEvent?.({
|
|
122
103
|
step: 2,
|
|
123
104
|
phase: RegistrationPhase.STEP_2_KEY_GENERATION,
|
|
@@ -143,15 +124,26 @@ async function registerPasskeyInternal(context, nearAccountId, options, authenti
|
|
|
143
124
|
const expectedAccessKeys = [nearPublicKey];
|
|
144
125
|
const thresholdPublicKey = String(accountAndRegistrationResult?.thresholdEd25519?.publicKey || "").trim();
|
|
145
126
|
const relayerKeyId = String(accountAndRegistrationResult?.thresholdEd25519?.relayerKeyId || "").trim();
|
|
146
|
-
const accessKeyVerified = await verifyAccountAccessKeysPresent(context.nearClient, nearAccountId, expectedAccessKeys
|
|
147
|
-
|
|
148
|
-
|
|
127
|
+
const accessKeyVerified = await verifyAccountAccessKeysPresent(context.nearClient, nearAccountId, expectedAccessKeys, {
|
|
128
|
+
attempts: 3,
|
|
129
|
+
delayMs: 200,
|
|
130
|
+
finality: "optimistic"
|
|
131
|
+
});
|
|
132
|
+
if (!accessKeyVerified) {
|
|
133
|
+
console.warn("[Registration] Access key not yet visible after atomic registration; continuing optimistically");
|
|
134
|
+
onEvent?.({
|
|
135
|
+
step: 6,
|
|
136
|
+
phase: RegistrationPhase.STEP_6_ACCOUNT_VERIFICATION,
|
|
137
|
+
status: RegistrationStatus.SUCCESS,
|
|
138
|
+
message: "Access key verification pending (optimistic); continuing..."
|
|
139
|
+
});
|
|
140
|
+
} else onEvent?.({
|
|
149
141
|
step: 6,
|
|
150
142
|
phase: RegistrationPhase.STEP_6_ACCOUNT_VERIFICATION,
|
|
151
143
|
status: RegistrationStatus.SUCCESS,
|
|
152
144
|
message: "Access key verified on-chain"
|
|
153
145
|
});
|
|
154
|
-
|
|
146
|
+
activateThresholdEnrollmentPostRegistration({
|
|
155
147
|
requestedSignerMode: requestedSignerModeStr,
|
|
156
148
|
nearAccountId,
|
|
157
149
|
nearPublicKey,
|
|
@@ -166,7 +158,7 @@ async function registerPasskeyInternal(context, nearAccountId, options, authenti
|
|
|
166
158
|
webAuthnManager: context.webAuthnManager,
|
|
167
159
|
nearClient: context.nearClient,
|
|
168
160
|
onEvent
|
|
169
|
-
});
|
|
161
|
+
}).catch(() => {});
|
|
170
162
|
onEvent?.({
|
|
171
163
|
step: 7,
|
|
172
164
|
phase: RegistrationPhase.STEP_7_DATABASE_STORAGE,
|
|
@@ -188,31 +180,12 @@ async function registerPasskeyInternal(context, nearAccountId, options, authenti
|
|
|
188
180
|
status: RegistrationStatus.SUCCESS,
|
|
189
181
|
message: "Registration metadata stored successfully"
|
|
190
182
|
});
|
|
191
|
-
try {
|
|
192
|
-
context.webAuthnManager.getNonceManager().initializeUser(nearAccountId, nearPublicKey);
|
|
193
|
-
await context.webAuthnManager.getNonceManager().prefetchBlockheight(context.nearClient);
|
|
194
|
-
} catch {}
|
|
195
183
|
let vrfStatus = await webAuthnManager.checkVrfStatus().catch(() => ({ active: false }));
|
|
196
184
|
if (!vrfStatus?.active) {
|
|
197
|
-
const
|
|
198
|
-
const txBlockHash = blockInfo?.header?.hash;
|
|
199
|
-
const txBlockHeight = String(blockInfo.header?.height ?? "");
|
|
200
|
-
const vrfChallenge2 = await webAuthnManager.generateVrfChallengeOnce({
|
|
201
|
-
userId: nearAccountId,
|
|
202
|
-
rpId: webAuthnManager.getRpId(),
|
|
203
|
-
blockHash: txBlockHash,
|
|
204
|
-
blockHeight: txBlockHeight
|
|
205
|
-
});
|
|
206
|
-
const authenticators = await webAuthnManager.getAuthenticatorsByUser(nearAccountId);
|
|
207
|
-
const authCredential = await webAuthnManager.getAuthenticationCredentialsSerializedDualPrf({
|
|
208
|
-
nearAccountId,
|
|
209
|
-
challenge: vrfChallenge2,
|
|
210
|
-
credentialIds: authenticators.map((a) => a.credentialId)
|
|
211
|
-
});
|
|
212
|
-
const unlockResult = await webAuthnManager.unlockVRFKeypair({
|
|
185
|
+
const unlockNoPrompt = await webAuthnManager.unlockVRFKeypair({
|
|
213
186
|
nearAccountId,
|
|
214
187
|
encryptedVrfKeypair: deterministicVrfKeyResult.encryptedVrfKeypair,
|
|
215
|
-
credential
|
|
188
|
+
credential
|
|
216
189
|
}).catch((unlockError) => {
|
|
217
190
|
const message = unlockError && typeof unlockError === "object" && "message" in unlockError ? String(unlockError.message || "") : String(unlockError || "");
|
|
218
191
|
return {
|
|
@@ -220,13 +193,46 @@ async function registerPasskeyInternal(context, nearAccountId, options, authenti
|
|
|
220
193
|
error: message
|
|
221
194
|
};
|
|
222
195
|
});
|
|
223
|
-
if (!
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
196
|
+
if (!unlockNoPrompt.success) {
|
|
197
|
+
let txBlockHash = String(transactionContext?.txBlockHash || "").trim();
|
|
198
|
+
let txBlockHeight = String(transactionContext?.txBlockHeight || "").trim();
|
|
199
|
+
if (!txBlockHash || !txBlockHeight) {
|
|
200
|
+
const blockInfo = await context.nearClient.viewBlock({ finality: "final" });
|
|
201
|
+
txBlockHash = String(blockInfo?.header?.hash || "").trim();
|
|
202
|
+
txBlockHeight = String(blockInfo?.header?.height ?? "").trim();
|
|
203
|
+
}
|
|
204
|
+
const vrfChallenge2 = await webAuthnManager.generateVrfChallengeOnce({
|
|
205
|
+
userId: nearAccountId,
|
|
206
|
+
rpId: webAuthnManager.getRpId(),
|
|
207
|
+
blockHash: txBlockHash,
|
|
208
|
+
blockHeight: txBlockHeight
|
|
209
|
+
});
|
|
210
|
+
const allowCredentialIds = String(credential?.rawId || "").trim() ? [String(credential.rawId)] : (await webAuthnManager.getAuthenticatorsByUser(nearAccountId)).map((a) => a.credentialId);
|
|
211
|
+
const authCredential = await webAuthnManager.getAuthenticationCredentialsSerializedDualPrf({
|
|
212
|
+
nearAccountId,
|
|
213
|
+
challenge: vrfChallenge2,
|
|
214
|
+
credentialIds: allowCredentialIds
|
|
215
|
+
});
|
|
216
|
+
const unlockResult = await webAuthnManager.unlockVRFKeypair({
|
|
217
|
+
nearAccountId,
|
|
218
|
+
encryptedVrfKeypair: deterministicVrfKeyResult.encryptedVrfKeypair,
|
|
219
|
+
credential: authCredential
|
|
220
|
+
}).catch((unlockError) => {
|
|
221
|
+
const message = unlockError && typeof unlockError === "object" && "message" in unlockError ? String(unlockError.message || "") : String(unlockError || "");
|
|
222
|
+
return {
|
|
223
|
+
success: false,
|
|
224
|
+
error: message
|
|
225
|
+
};
|
|
226
|
+
});
|
|
227
|
+
if (!unlockResult.success) {
|
|
228
|
+
console.warn("VRF keypair unlock failed:", unlockResult.error);
|
|
229
|
+
throw new Error(unlockResult.error);
|
|
230
|
+
}
|
|
231
|
+
} else console.debug("Registration: VRF unlocked using registration credential; skipping extra Touch ID unlock");
|
|
227
232
|
} else console.debug("Registration: VRF session already active; skipping extra Touch ID unlock");
|
|
228
233
|
try {
|
|
229
|
-
await webAuthnManager.initializeCurrentUser(nearAccountId
|
|
234
|
+
await webAuthnManager.initializeCurrentUser(nearAccountId);
|
|
235
|
+
webAuthnManager.getNonceManager().prefetchBlockheight(context.nearClient).catch(() => {});
|
|
230
236
|
} catch (initErr) {
|
|
231
237
|
console.warn("Failed to initialize current user after registration:", initErr);
|
|
232
238
|
}
|
|
@@ -387,7 +393,11 @@ async function activateThresholdEnrollmentPostRegistration(opts) {
|
|
|
387
393
|
const signedTx = signed?.signedTransaction;
|
|
388
394
|
if (!signedTx) throw new Error("Failed to sign AddKey(thresholdPublicKey) transaction");
|
|
389
395
|
await opts.nearClient.sendTransaction(signedTx, DEFAULT_WAIT_STATUS.thresholdAddKey);
|
|
390
|
-
const thresholdKeyVerified = await verifyAccountAccessKeysPresent(opts.nearClient, opts.nearAccountId, [opts.nearPublicKey, thresholdPublicKey]
|
|
396
|
+
const thresholdKeyVerified = await verifyAccountAccessKeysPresent(opts.nearClient, opts.nearAccountId, [opts.nearPublicKey, thresholdPublicKey], {
|
|
397
|
+
attempts: 6,
|
|
398
|
+
delayMs: 250,
|
|
399
|
+
finality: "optimistic"
|
|
400
|
+
});
|
|
391
401
|
if (!thresholdKeyVerified) throw new Error("Threshold access key not found on-chain after AddKey");
|
|
392
402
|
await IndexedDBManager.nearKeysDB.storeKeyMaterial({
|
|
393
403
|
kind: "threshold_ed25519_2p_v1",
|
|
@@ -423,14 +433,15 @@ async function verifyAccountAccessKeysPresent(nearClient, nearAccountId, expecte
|
|
|
423
433
|
if (!unique.length) return false;
|
|
424
434
|
const attempts = Math.max(1, Math.floor(opts?.attempts ?? 6));
|
|
425
435
|
const delayMs = Math.max(50, Math.floor(opts?.delayMs ?? 750));
|
|
436
|
+
const finality = opts?.finality ?? "optimistic";
|
|
426
437
|
for (let i = 0; i < attempts; i++) {
|
|
427
438
|
try {
|
|
428
|
-
const
|
|
429
|
-
const keys =
|
|
439
|
+
const accessKeyList = await nearClient.viewAccessKeyList(nearAccountId, { finality });
|
|
440
|
+
const keys = accessKeyList.keys.map((k) => ensureEd25519Prefix(k.public_key)).filter(Boolean);
|
|
430
441
|
const allPresent = unique.every((expected) => keys.includes(expected));
|
|
431
442
|
if (allPresent) return true;
|
|
432
443
|
} catch {}
|
|
433
|
-
await new Promise((res) => setTimeout(res, delayMs));
|
|
444
|
+
if (i < attempts - 1) await new Promise((res) => setTimeout(res, delayMs));
|
|
434
445
|
}
|
|
435
446
|
return false;
|
|
436
447
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registration.js","names":["confirmationConfig: Partial<ConfirmationConfig>","thresholdClientVerifyingShareB64u: string | null","expectedAccessKeys: string[]","error: unknown","rollbackError: unknown"],"sources":["../../../../../../src/core/TatchiPasskey/registration.ts"],"sourcesContent":["import type { NearClient } from '../NearClient';\nimport { ensureEd25519Prefix, validateNearAccountId } from '../../utils/validation';\nimport type {\n RegistrationHooksOptions,\n RegistrationSSEEvent,\n} from '../types/sdkSentEvents';\nimport type { RegistrationResult, TatchiConfigs } from '../types/tatchi';\nimport type { AuthenticatorOptions } from '../types/authenticatorOptions';\nimport { RegistrationPhase, RegistrationStatus } from '../types/sdkSentEvents';\nimport {\n createAccountAndRegisterWithRelayServer\n} from './faucets/createAccountRelayServer';\nimport { PasskeyManagerContext } from './index';\nimport { WebAuthnManager } from '../WebAuthnManager';\nimport { IndexedDBManager } from '../IndexedDBManager';\nimport { VRFChallenge } from '../types/vrf-worker';\nimport { type ConfirmationConfig, type SignerMode, mergeSignerMode } from '../types/signer-worker';\nimport type { WebAuthnRegistrationCredential } from '../types/webauthn';\nimport type { AccountId } from '../types/accountIds';\nimport { getUserFriendlyErrorMessage } from '../../utils/errors';\nimport { authenticatorsToAllowCredentials } from '../WebAuthnManager/touchIdPrompt';\nimport { DEFAULT_WAIT_STATUS } from '../types/rpc';\nimport { buildThresholdEd25519Participants2pV1 } from '../../threshold/participants';\n// Registration forces a visible, clickable confirmation for cross‑origin safety\n\n/**\n * Core registration function that handles passkey registration\n *\n * VRF Registration Flow (Single VRF Keypair):\n * 1. Generate VRF keypair (ed25519) using crypto.randomUUID() + persist in worker memory\n * 2. Generate VRF proof + output using the VRF keypair\n * - VRF input with domain separator + NEAR block height + hash\n * 3. Use VRF output as WebAuthn challenge in registration ceremony\n * 4. Derive AES key from WebAuthn PRF output and encrypt the SAME VRF keypair\n * 5. Store encrypted VRF keypair in IndexedDB\n * 6. Call contract verify_registration_response with VRF proof + WebAuthn registration payload\n * 7. Contract verifies VRF proof and WebAuthn registration (challenges match!)\n * 8. Contract stores VRF pubkey + authenticator credentials on-chain for\n * future stateless authentication\n */\nexport async function registerPasskeyInternal(\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n options: RegistrationHooksOptions,\n authenticatorOptions: AuthenticatorOptions,\n confirmationConfigOverride?: Partial<ConfirmationConfig>\n): Promise<RegistrationResult> {\n\n const { onEvent, onError, afterCall } = options;\n const { webAuthnManager, configs } = context;\n\n // Track registration progress for rollback\n const registrationState = {\n accountCreated: false,\n contractRegistered: false,\n databaseStored: false,\n contractTransactionId: null as string | null,\n };\n\n console.log('⚡ Registration: Passkey registration with VRF WebAuthn');\n onEvent?.({\n step: 1,\n phase: RegistrationPhase.STEP_1_WEBAUTHN_VERIFICATION,\n status: RegistrationStatus.PROGRESS,\n message: `Starting registration for ${nearAccountId}`\n } as RegistrationSSEEvent);\n\n try {\n\n await validateRegistrationInputs(context, nearAccountId, onEvent, onError);\n\n onEvent?.({\n step: 1,\n phase: RegistrationPhase.STEP_1_WEBAUTHN_VERIFICATION,\n status: RegistrationStatus.PROGRESS,\n message: 'Account available, generating credentials...'\n });\n\n const confirmationConfig: Partial<ConfirmationConfig> = {\n uiMode: 'modal',\n behavior: 'requireClick', // cross‑origin safari requirement: must requireClick\n theme: (context.configs?.walletTheme === 'light') ? 'light' : 'dark',\n ...(confirmationConfigOverride ?? options?.confirmationConfig ?? {}),\n };\n\n const registrationSession = await context.webAuthnManager.requestRegistrationCredentialConfirmation({\n nearAccountId: String(nearAccountId),\n deviceNumber: 1,\n confirmerText: options?.confirmerText,\n confirmationConfigOverride: confirmationConfig,\n });\n\n const credential = registrationSession.credential;\n const vrfChallenge = registrationSession.vrfChallenge;\n\n onEvent?.({\n step: 1,\n phase: RegistrationPhase.STEP_1_WEBAUTHN_VERIFICATION,\n status: RegistrationStatus.SUCCESS,\n message: 'WebAuthn ceremony successful'\n });\n\n // Start the contract pre-check, run it in parallel to key derivation (await later)\n const canRegisterUserPromise = webAuthnManager.checkCanRegisterUser({\n contractId: context.configs.contractId,\n credential,\n vrfChallenge,\n onEvent: (progress) => {\n console.debug(`Registration progress: ${progress.step} - ${progress.message}`);\n onEvent?.({\n step: 3,\n phase: RegistrationPhase.STEP_3_CONTRACT_PRE_CHECK,\n status: RegistrationStatus.PROGRESS,\n message: `${progress.message}`\n });\n },\n });\n\n // 1) Ensure VRF keypair is derived and loaded in-memory\n // before deriving WrapKeySeed for NEAR key encryption\n const deterministicVrfKeyResult = await webAuthnManager.deriveVrfKeypair({\n credential,\n nearAccountId,\n saveInMemory: true,\n });\n if (!deterministicVrfKeyResult.success || !deterministicVrfKeyResult.vrfPublicKey) {\n throw new Error('Failed to derive deterministic VRF keypair from PRF');\n }\n\n const baseSignerMode = webAuthnManager.getUserPreferences().getSignerMode();\n const requestedSignerMode = mergeSignerMode(baseSignerMode, options?.signerMode);\n const requestedSignerModeStr = requestedSignerMode.mode;\n\n // 2) Derive/enroll the local NEAR key after VRF keypair exists.\n const nearKeyResult = await webAuthnManager.deriveNearKeypairAndEncryptFromSerialized({\n credential,\n nearAccountId,\n options: { deviceNumber: 1 },\n });\n if (!nearKeyResult.success || !nearKeyResult.publicKey) {\n const reason = nearKeyResult?.error || 'Failed to generate NEAR keypair with PRF';\n throw new Error(reason);\n }\n const nearPublicKey = nearKeyResult.publicKey;\n const wrapKeySalt = String(nearKeyResult.wrapKeySalt || '').trim();\n if (!wrapKeySalt) {\n throw new Error('Missing wrapKeySalt after local key derivation');\n }\n\n // Optional: threshold-signer enrollment during registration.\n // Derive client verifying share (public) and let the relay compute threshold group public key\n // and include it in the on-chain AddKey set during create_account_and_register_user.\n let thresholdClientVerifyingShareB64u: string | null = null;\n if (requestedSignerModeStr === 'threshold-signer') {\n const derived = await webAuthnManager.deriveThresholdEd25519ClientVerifyingShareFromCredential({\n credential,\n nearAccountId,\n wrapKeySalt,\n });\n if (!derived.success || !derived.clientVerifyingShareB64u) {\n throw new Error(derived.error || 'Failed to derive threshold client verifying share');\n }\n thresholdClientVerifyingShareB64u = derived.clientVerifyingShareB64u;\n }\n\n // 3) Await contract registration check (main blocker)\n const canRegisterUserResult = await canRegisterUserPromise;\n if (!canRegisterUserResult.verified) {\n console.error(canRegisterUserResult);\n const errorMessage = canRegisterUserResult.error || 'User verification failed - account may already exist or contract is unreachable';\n throw new Error(`Web3Authn contract registration check failed: ${errorMessage}`);\n }\n\n // Step 4-5: Create account and register with contract using the relay (atomic)\n onEvent?.({\n step: 2,\n phase: RegistrationPhase.STEP_2_KEY_GENERATION,\n status: RegistrationStatus.SUCCESS,\n message: 'Wallet derived successfully from Passkey',\n verified: true,\n nearAccountId: nearAccountId,\n nearPublicKey: nearPublicKey,\n vrfPublicKey: vrfChallenge.vrfPublicKey,\n });\n\n let accountAndRegistrationResult;\n accountAndRegistrationResult = await createAccountAndRegisterWithRelayServer(\n context,\n nearAccountId,\n nearPublicKey,\n credential,\n vrfChallenge,\n deterministicVrfKeyResult.vrfPublicKey,\n authenticatorOptions,\n onEvent,\n {\n thresholdEd25519: thresholdClientVerifyingShareB64u\n ? { clientVerifyingShareB64u: thresholdClientVerifyingShareB64u }\n : undefined,\n },\n );\n\n if (!accountAndRegistrationResult.success) {\n throw new Error(accountAndRegistrationResult.error || 'Account creation and registration failed');\n }\n\n // Update registration state based on results\n registrationState.accountCreated = true;\n registrationState.contractRegistered = true;\n registrationState.contractTransactionId = accountAndRegistrationResult.transactionId || null;\n\n // Step 6: Post-commit verification: ensure on-chain access key matches expected public key\n onEvent?.({\n step: 6,\n phase: RegistrationPhase.STEP_6_ACCOUNT_VERIFICATION,\n status: RegistrationStatus.PROGRESS,\n message: 'Verifying on-chain access key matches expected public key...'\n });\n\n const expectedAccessKeys: string[] = [nearPublicKey];\n const thresholdPublicKey = String(accountAndRegistrationResult?.thresholdEd25519?.publicKey || '').trim();\n const relayerKeyId = String(accountAndRegistrationResult?.thresholdEd25519?.relayerKeyId || '').trim();\n\n const accessKeyVerified = await verifyAccountAccessKeysPresent(\n context.nearClient,\n nearAccountId,\n expectedAccessKeys,\n );\n\n if (!accessKeyVerified) {\n throw new Error('On-chain access key mismatch or not found after registration');\n }\n\n onEvent?.({\n step: 6,\n phase: RegistrationPhase.STEP_6_ACCOUNT_VERIFICATION,\n status: RegistrationStatus.SUCCESS,\n message: 'Access key verified on-chain'\n });\n\n await activateThresholdEnrollmentPostRegistration({\n requestedSignerMode: requestedSignerModeStr,\n nearAccountId,\n nearPublicKey,\n thresholdPublicKey,\n relayerKeyId,\n clientParticipantId: accountAndRegistrationResult?.thresholdEd25519?.clientParticipantId,\n relayerParticipantId: accountAndRegistrationResult?.thresholdEd25519?.relayerParticipantId,\n thresholdClientVerifyingShareB64u,\n relayerVerifyingShareB64u: String(\n accountAndRegistrationResult?.thresholdEd25519?.relayerVerifyingShareB64u || '',\n ).trim(),\n credential,\n wrapKeySalt,\n webAuthnManager: context.webAuthnManager,\n nearClient: context.nearClient,\n onEvent,\n });\n\n // Step 7: Store user data with VRF credentials atomically\n onEvent?.({\n step: 7,\n phase: RegistrationPhase.STEP_7_DATABASE_STORAGE,\n status: RegistrationStatus.PROGRESS,\n message: 'Storing passkey wallet metadata...'\n });\n\n await webAuthnManager.atomicStoreRegistrationData({\n nearAccountId,\n credential,\n publicKey: nearPublicKey,\n encryptedVrfKeypair: deterministicVrfKeyResult.encryptedVrfKeypair,\n vrfPublicKey: deterministicVrfKeyResult.vrfPublicKey,\n serverEncryptedVrfKeypair: deterministicVrfKeyResult.serverEncryptedVrfKeypair,\n });\n\n // Mark database as stored for rollback tracking\n registrationState.databaseStored = true;\n\n onEvent?.({\n step: 7,\n phase: RegistrationPhase.STEP_7_DATABASE_STORAGE,\n status: RegistrationStatus.SUCCESS,\n message: 'Registration metadata stored successfully'\n });\n\n // Initialize NonceManager with newly stored user before fetching block/nonce\n try {\n context.webAuthnManager.getNonceManager().initializeUser(nearAccountId, nearPublicKey);\n await context.webAuthnManager.getNonceManager().prefetchBlockheight(context.nearClient);\n } catch {}\n\n // Step 7: Ensure VRF session is active for auto-login\n // If VRF keypair is already in-memory (saved earlier), skip an extra Touch ID prompt.\n let vrfStatus = await webAuthnManager.checkVrfStatus().catch(() => ({ active: false }));\n if (!vrfStatus?.active) {\n // Obtain an authentication credential for VRF unlock (separate from registration credential)\n // IMPORTANT: Immediately after account creation, the new access key may not be queryable yet on some RPC nodes.\n // We only need fresh block info for the VRF challenge here, so fetch the block directly to avoid AK lookup failures.\n const blockInfo = await context.nearClient.viewBlock({ finality: 'final' });\n const txBlockHash = blockInfo?.header?.hash;\n const txBlockHeight = String(blockInfo.header?.height ?? '');\n const vrfChallenge2 = await webAuthnManager.generateVrfChallengeOnce({\n userId: nearAccountId,\n rpId: webAuthnManager.getRpId(),\n blockHash: txBlockHash,\n blockHeight: txBlockHeight,\n });\n const authenticators = await webAuthnManager.getAuthenticatorsByUser(nearAccountId);\n const authCredential = await webAuthnManager.getAuthenticationCredentialsSerializedDualPrf({\n nearAccountId,\n challenge: vrfChallenge2,\n credentialIds: authenticators.map((a) => a.credentialId),\n });\n const unlockResult = await webAuthnManager.unlockVRFKeypair({\n nearAccountId: nearAccountId,\n encryptedVrfKeypair: deterministicVrfKeyResult.encryptedVrfKeypair,\n credential: authCredential,\n }).catch((unlockError: unknown) => {\n const message = (unlockError && typeof unlockError === 'object' && 'message' in unlockError)\n ? String((unlockError as { message?: unknown }).message || '')\n : String(unlockError || '');\n return { success: false, error: message };\n });\n\n if (!unlockResult.success) {\n console.warn('VRF keypair unlock failed:', unlockResult.error);\n throw new Error(unlockResult.error);\n }\n } else {\n console.debug('Registration: VRF session already active; skipping extra Touch ID unlock');\n }\n\n // Initialize current user only after a successful unlock\n try {\n await webAuthnManager.initializeCurrentUser(nearAccountId, context.nearClient);\n } catch (initErr) {\n console.warn('Failed to initialize current user after registration:', initErr);\n }\n\n onEvent?.({\n step: 8,\n phase: RegistrationPhase.STEP_8_REGISTRATION_COMPLETE,\n status: RegistrationStatus.SUCCESS,\n message: 'Registration completed!'\n });\n\n const successResult = {\n success: true,\n nearAccountId: nearAccountId,\n clientNearPublicKey: nearPublicKey,\n transactionId: registrationState.contractTransactionId,\n vrfRegistration: {\n success: true,\n vrfPublicKey: vrfChallenge.vrfPublicKey,\n encryptedVrfKeypair: deterministicVrfKeyResult.encryptedVrfKeypair,\n contractVerified: accountAndRegistrationResult.success,\n }\n };\n\n afterCall?.(true, successResult);\n return successResult;\n\n } catch (error: unknown) {\n const message = (error && typeof error === 'object' && 'message' in error)\n ? String((error as { message?: unknown }).message || '')\n : String(error || '');\n const stack = (error && typeof error === 'object' && 'stack' in error)\n ? String((error as { stack?: unknown }).stack || '')\n : '';\n console.error('Registration failed:', message, stack);\n\n // Perform rollback based on registration state\n await performRegistrationRollback(\n registrationState,\n nearAccountId,\n webAuthnManager,\n onEvent\n );\n\n // Use centralized error handling\n const errorMessage = getUserFriendlyErrorMessage(error, 'registration', nearAccountId);\n\n const errorObject = new Error(errorMessage);\n onError?.(errorObject);\n\n onEvent?.({\n step: 0,\n phase: RegistrationPhase.REGISTRATION_ERROR,\n status: RegistrationStatus.ERROR,\n message: errorMessage,\n error: errorMessage\n } as RegistrationSSEEvent);\n\n const result = { success: false, error: errorMessage };\n afterCall?.(false);\n return result;\n }\n}\n\n// Backward-compatible wrapper without explicit confirmationConfig override\nexport async function registerPasskey(\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n options: RegistrationHooksOptions,\n authenticatorOptions: AuthenticatorOptions\n): Promise<RegistrationResult> {\n return registerPasskeyInternal(context, nearAccountId, options, authenticatorOptions, undefined);\n}\n\n//////////////////////////////////////\n// HELPER FUNCTIONS\n//////////////////////////////////////\n\n/**\n * Generate a VRF keypair + challenge in VRF wasm worker for WebAuthn registration ceremony bootstrapping\n *\n * ARCHITECTURE: This function solves the chicken-and-egg problem with a single VRF keypair:\n * 1. Generate VRF keypair + challenge (no PRF needed)\n * 2. Persist VRF keypair in worker memory (NOT encrypted yet)\n * 3. Use VRF challenge for WebAuthn ceremony → get PRF output\n * 4. Encrypt the SAME VRF keypair (still in memory) with PRF\n *\n * @param webAuthnManager - WebAuthn manager instance\n * @param nearAccountId - NEAR account ID for VRF input\n * @param blockHeight - Current NEAR block height for freshness\n * @param blockHashBytes - Current NEAR block hash bytes for entropy\n * @returns VRF challenge data (VRF keypair persisted in worker memory)\n */\nexport async function generateBootstrapVrfChallenge(\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n): Promise<VRFChallenge> {\n\n const { webAuthnManager, nearClient } = context;\n\n const blockInfo = await nearClient.viewBlock({ finality: 'final' });\n\n // Generate VRF keypair and persist in worker memory\n const vrfResult = await webAuthnManager.generateVrfKeypairBootstrap({\n vrfInputData: {\n userId: nearAccountId,\n // Keep VRF rpId consistent with WebAuthn rpId selection logic.\n rpId: webAuthnManager.getRpId(),\n blockHeight: String(blockInfo.header.height),\n blockHash: blockInfo.header.hash,\n },\n saveInMemory: true,\n // VRF keypair persists in worker memory until PRF encryption\n });\n\n if (!vrfResult.vrfChallenge) {\n throw new Error('Registration VRF keypair generation failed');\n }\n return vrfResult.vrfChallenge;\n}\n\n/**\n * Validates registration inputs and throws errors if invalid\n * @param nearAccountId - NEAR account ID to validate\n * @param onEvent - Optional callback for registration progress events\n * @param onError - Optional callback for error handling\n */\nconst validateRegistrationInputs = async (\n context: {\n configs: TatchiConfigs,\n webAuthnManager: WebAuthnManager,\n nearClient: NearClient,\n },\n nearAccountId: AccountId,\n onEvent?: (event: RegistrationSSEEvent) => void,\n onError?: (error: Error) => void,\n) => {\n\n onEvent?.({\n step: 1,\n phase: RegistrationPhase.STEP_1_WEBAUTHN_VERIFICATION,\n status: RegistrationStatus.PROGRESS,\n message: 'Validating registration inputs...'\n } as RegistrationSSEEvent);\n\n // Validation\n if (!nearAccountId) {\n const error = new Error('NEAR account ID is required for registration.');\n onError?.(error);\n throw error;\n }\n // Validate the account ID format\n const validation = validateNearAccountId(nearAccountId);\n if (!validation.valid) {\n const error = new Error(`Invalid NEAR account ID: ${validation.error}`);\n onError?.(error);\n throw error;\n }\n if (!window.isSecureContext) {\n const error = new Error('Passkey operations require a secure context (HTTPS or localhost).');\n onError?.(error);\n throw error;\n }\n\n // on-chain account existence check is performed later via signer worker (checkCanRegisterUser),\n onEvent?.({\n step: 1,\n phase: RegistrationPhase.STEP_1_WEBAUTHN_VERIFICATION,\n status: RegistrationStatus.PROGRESS,\n message: `Account format validated, preparing confirmation`\n } as RegistrationSSEEvent);\n return;\n}\n\n/**\n * Rollback registration data in case of errors\n */\nasync function performRegistrationRollback(\n registrationState: {\n accountCreated: boolean;\n contractRegistered: boolean;\n databaseStored: boolean;\n contractTransactionId: string | null;\n },\n nearAccountId: AccountId,\n webAuthnManager: WebAuthnManager,\n onEvent?: (event: RegistrationSSEEvent) => void\n): Promise<void> {\n console.debug('Starting registration rollback...', registrationState);\n\n // Rollback in reverse order\n try {\n // 1. Always clear any in-memory VRF session established during bootstrap\n await webAuthnManager.clearVrfSession();\n\n // 2. Rollback database storage\n if (registrationState.databaseStored) {\n console.debug('Rolling back database storage...');\n onEvent?.({\n step: 0,\n phase: RegistrationPhase.REGISTRATION_ERROR,\n status: RegistrationStatus.ERROR,\n message: 'Rolling back database storage...',\n error: 'Registration failed - rolling back database storage'\n } as RegistrationSSEEvent);\n\n await webAuthnManager.rollbackUserRegistration(nearAccountId);\n console.debug('Database rollback completed');\n }\n\n // 3. Contract rollback on the Web3Authn contract\n // NOT NEEDED - account creation and contract registration are atomic in the relay server flow\n if (registrationState.contractRegistered) {\n console.debug('Contract registration cannot be rolled back (immutable blockchain state)');\n onEvent?.({\n step: 0,\n phase: RegistrationPhase.REGISTRATION_ERROR,\n status: RegistrationStatus.ERROR,\n message: `Contract registration (tx: ${registrationState.contractTransactionId}) cannot be rolled back`,\n error: 'Registration failed - contract state is immutable'\n } as RegistrationSSEEvent);\n }\n console.debug('Registration rollback completed');\n\n } catch (rollbackError: unknown) {\n console.error('Rollback failed:', rollbackError);\n onEvent?.({\n step: 0,\n phase: RegistrationPhase.REGISTRATION_ERROR,\n status: RegistrationStatus.ERROR,\n message: `Rollback failed: ${\n (rollbackError && typeof rollbackError === 'object' && 'message' in rollbackError)\n ? String((rollbackError as { message?: unknown }).message || '')\n : String(rollbackError || '')\n }`,\n error: 'Both registration and rollback failed'\n } as RegistrationSSEEvent);\n }\n}\n\nasync function activateThresholdEnrollmentPostRegistration(opts: {\n requestedSignerMode: SignerMode['mode'];\n nearAccountId: AccountId;\n nearPublicKey: string;\n thresholdPublicKey: string;\n relayerKeyId: string;\n clientParticipantId?: number;\n relayerParticipantId?: number;\n thresholdClientVerifyingShareB64u: string | null;\n relayerVerifyingShareB64u: string;\n credential: WebAuthnRegistrationCredential;\n wrapKeySalt: string;\n webAuthnManager: WebAuthnManager;\n nearClient: NearClient;\n onEvent?: (event: RegistrationSSEEvent) => void;\n}): Promise<void> {\n // Optional: activate threshold enrollment post-registration by having the client\n // submit AddKey(thresholdPublicKey) signed with the local key.\n if (opts.requestedSignerMode !== 'threshold-signer') return;\n\n const thresholdPublicKey = String(opts.thresholdPublicKey || '').trim();\n const relayerKeyId = String(opts.relayerKeyId || '').trim();\n const clientVerifyingShareB64u = String(opts.thresholdClientVerifyingShareB64u || '').trim();\n const relayerVerifyingShareB64u = String(opts.relayerVerifyingShareB64u || '').trim();\n\n if (!thresholdPublicKey || !relayerKeyId || !clientVerifyingShareB64u || !relayerVerifyingShareB64u) {\n console.warn('[Registration] threshold-signer requested but threshold enrollment details are missing; continuing with local-signer only');\n return;\n }\n\n try {\n opts.onEvent?.({\n step: 6,\n phase: RegistrationPhase.STEP_6_ACCOUNT_VERIFICATION,\n status: RegistrationStatus.PROGRESS,\n message: 'Activating threshold access key onchain...'\n });\n\n // Prepare a single AddKey transaction signed with the local key (no extra TouchID prompt).\n try {\n opts.webAuthnManager.getNonceManager().initializeUser(opts.nearAccountId, opts.nearPublicKey);\n } catch {}\n const txContext = await opts.webAuthnManager.getNonceManager().getNonceBlockHashAndHeight(\n opts.nearClient,\n { force: true },\n );\n\n const signed = await opts.webAuthnManager.signAddKeyThresholdPublicKeyNoPrompt({\n nearAccountId: opts.nearAccountId,\n credential: opts.credential,\n wrapKeySalt: opts.wrapKeySalt,\n transactionContext: txContext,\n thresholdPublicKey,\n relayerVerifyingShareB64u,\n clientParticipantId: opts.clientParticipantId,\n relayerParticipantId: opts.relayerParticipantId,\n });\n\n const signedTx = signed?.signedTransaction;\n if (!signedTx) throw new Error('Failed to sign AddKey(thresholdPublicKey) transaction');\n\n await opts.nearClient.sendTransaction(signedTx, DEFAULT_WAIT_STATUS.thresholdAddKey);\n\n const thresholdKeyVerified = await verifyAccountAccessKeysPresent(\n opts.nearClient,\n opts.nearAccountId,\n [opts.nearPublicKey, thresholdPublicKey],\n );\n if (!thresholdKeyVerified) {\n throw new Error('Threshold access key not found on-chain after AddKey');\n }\n\n await IndexedDBManager.nearKeysDB.storeKeyMaterial({\n kind: 'threshold_ed25519_2p_v1',\n nearAccountId: opts.nearAccountId,\n deviceNumber: 1,\n publicKey: thresholdPublicKey,\n wrapKeySalt: opts.wrapKeySalt,\n relayerKeyId,\n clientShareDerivation: 'prf_first_v1',\n participants: buildThresholdEd25519Participants2pV1({\n clientParticipantId: opts.clientParticipantId,\n relayerParticipantId: opts.relayerParticipantId,\n relayerKeyId,\n relayerUrl: opts.webAuthnManager.tatchiPasskeyConfigs?.relayer?.url,\n clientVerifyingShareB64u,\n relayerVerifyingShareB64u,\n clientShareDerivation: 'prf_first_v1',\n }),\n timestamp: Date.now(),\n });\n\n opts.onEvent?.({\n step: 6,\n phase: RegistrationPhase.STEP_6_ACCOUNT_VERIFICATION,\n status: RegistrationStatus.SUCCESS,\n message: 'Threshold access key activated on-chain'\n });\n } catch (e) {\n console.warn('[Registration] threshold enrollment activation failed; continuing with local-signer only:', e);\n }\n}\n\nasync function verifyAccountAccessKeysPresent(\n nearClient: NearClient,\n nearAccountId: string,\n expectedPublicKeys: string[],\n opts?: { attempts?: number; delayMs?: number },\n): Promise<boolean> {\n const unique = Array.from(\n new Set(expectedPublicKeys.map((k) => ensureEd25519Prefix(k)).filter(Boolean)),\n );\n if (!unique.length) return false;\n\n const attempts = Math.max(1, Math.floor(opts?.attempts ?? 6));\n const delayMs = Math.max(50, Math.floor(opts?.delayMs ?? 750));\n\n for (let i = 0; i < attempts; i++) {\n try {\n const { fullAccessKeys, functionCallAccessKeys } = await nearClient.getAccessKeys({ account: nearAccountId });\n const keys = [...fullAccessKeys, ...functionCallAccessKeys].map((k) => ensureEd25519Prefix(k.public_key));\n const allPresent = unique.every((expected) => keys.includes(expected));\n if (allPresent) return true;\n } catch {\n // tolerate transient view errors during propagation; retry\n }\n await new Promise((res) => setTimeout(res, delayMs));\n }\n return false;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"registration.js","names":["confirmationConfig: Partial<ConfirmationConfig>","thresholdClientVerifyingShareB64u: string | null","expectedAccessKeys: string[]","error: unknown","rollbackError: unknown"],"sources":["../../../../../../src/core/TatchiPasskey/registration.ts"],"sourcesContent":["import type { NearClient } from '../NearClient';\nimport { ensureEd25519Prefix, validateNearAccountId } from '../../utils/validation';\nimport type {\n RegistrationHooksOptions,\n RegistrationSSEEvent,\n} from '../types/sdkSentEvents';\nimport type { RegistrationResult, TatchiConfigs } from '../types/tatchi';\nimport type { AuthenticatorOptions } from '../types/authenticatorOptions';\nimport { RegistrationPhase, RegistrationStatus } from '../types/sdkSentEvents';\nimport {\n createAccountAndRegisterWithRelayServer\n} from './faucets/createAccountRelayServer';\nimport { PasskeyManagerContext } from './index';\nimport { WebAuthnManager } from '../WebAuthnManager';\nimport { IndexedDBManager } from '../IndexedDBManager';\nimport { VRFChallenge } from '../types/vrf-worker';\nimport { type ConfirmationConfig, type SignerMode, mergeSignerMode } from '../types/signer-worker';\nimport type { WebAuthnRegistrationCredential } from '../types/webauthn';\nimport type { AccountId } from '../types/accountIds';\nimport { getUserFriendlyErrorMessage } from '../../utils/errors';\nimport { authenticatorsToAllowCredentials } from '../WebAuthnManager/touchIdPrompt';\nimport { DEFAULT_WAIT_STATUS } from '../types/rpc';\nimport { buildThresholdEd25519Participants2pV1 } from '../../threshold/participants';\n// Registration forces a visible, clickable confirmation for cross‑origin safety\n\n/**\n * Core registration function that handles passkey registration\n *\n * VRF Registration Flow (Single VRF Keypair):\n * 1. Generate VRF keypair (ed25519) using crypto.randomUUID() + persist in worker memory\n * 2. Generate VRF proof + output using the VRF keypair\n * - VRF input with domain separator + NEAR block height + hash\n * 3. Use VRF output as WebAuthn challenge in registration ceremony\n * 4. Derive AES key from WebAuthn PRF output and encrypt the SAME VRF keypair\n * 5. Store encrypted VRF keypair in IndexedDB\n * 6. Call contract verify_registration_response with VRF proof + WebAuthn registration payload\n * 7. Contract verifies VRF proof and WebAuthn registration (challenges match!)\n * 8. Contract stores VRF pubkey + authenticator credentials on-chain for\n * future stateless authentication\n */\nexport async function registerPasskeyInternal(\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n options: RegistrationHooksOptions,\n authenticatorOptions: AuthenticatorOptions,\n confirmationConfigOverride?: Partial<ConfirmationConfig>\n): Promise<RegistrationResult> {\n\n const { onEvent, onError, afterCall } = options;\n const { webAuthnManager, configs } = context;\n\n // Track registration progress for rollback\n const registrationState = {\n accountCreated: false,\n contractRegistered: false,\n databaseStored: false,\n contractTransactionId: null as string | null,\n };\n\n console.log('⚡ Registration: Passkey registration with VRF WebAuthn');\n onEvent?.({\n step: 1,\n phase: RegistrationPhase.STEP_1_WEBAUTHN_VERIFICATION,\n status: RegistrationStatus.PROGRESS,\n message: `Starting registration for ${nearAccountId}`\n } as RegistrationSSEEvent);\n\n try {\n\n await validateRegistrationInputs(context, nearAccountId, onEvent, onError);\n\n onEvent?.({\n step: 1,\n phase: RegistrationPhase.STEP_1_WEBAUTHN_VERIFICATION,\n status: RegistrationStatus.PROGRESS,\n message: 'Generating passkey credential...'\n });\n\n const confirmationConfig: Partial<ConfirmationConfig> = {\n uiMode: 'modal',\n behavior: 'requireClick', // cross‑origin safari requirement: must requireClick\n theme: (context.configs?.walletTheme === 'light') ? 'light' : 'dark',\n ...(confirmationConfigOverride ?? options?.confirmationConfig ?? {}),\n };\n\n const registrationSession = await context.webAuthnManager.requestRegistrationCredentialConfirmation({\n nearAccountId: String(nearAccountId),\n deviceNumber: 1,\n confirmerText: options?.confirmerText,\n confirmationConfigOverride: confirmationConfig,\n });\n\n const credential = registrationSession.credential;\n const vrfChallenge = registrationSession.vrfChallenge;\n const transactionContext = registrationSession.transactionContext;\n\n onEvent?.({\n step: 1,\n phase: RegistrationPhase.STEP_1_WEBAUTHN_VERIFICATION,\n status: RegistrationStatus.SUCCESS,\n message: 'WebAuthn ceremony successful'\n });\n\n // 1) Ensure VRF keypair is derived and loaded in-memory\n // before deriving WrapKeySeed for NEAR key encryption\n const deterministicVrfKeyResult = await webAuthnManager.deriveVrfKeypair({\n credential,\n nearAccountId,\n saveInMemory: true,\n });\n if (!deterministicVrfKeyResult.success || !deterministicVrfKeyResult.vrfPublicKey) {\n throw new Error('Failed to derive deterministic VRF keypair from PRF');\n }\n\n const baseSignerMode = webAuthnManager.getUserPreferences().getSignerMode();\n const requestedSignerMode = mergeSignerMode(baseSignerMode, options?.signerMode);\n const requestedSignerModeStr = requestedSignerMode.mode;\n\n // 2) Derive/enroll the local NEAR key after VRF keypair exists.\n const nearKeyResult = await webAuthnManager.deriveNearKeypairAndEncryptFromSerialized({\n credential,\n nearAccountId,\n options: { deviceNumber: 1 },\n });\n if (!nearKeyResult.success || !nearKeyResult.publicKey) {\n const reason = nearKeyResult?.error || 'Failed to generate NEAR keypair with PRF';\n throw new Error(reason);\n }\n const nearPublicKey = nearKeyResult.publicKey;\n const wrapKeySalt = String(nearKeyResult.wrapKeySalt || '').trim();\n if (!wrapKeySalt) {\n throw new Error('Missing wrapKeySalt after local key derivation');\n }\n\n // Optional: threshold-signer enrollment during registration.\n // Derive client verifying share (public) and let the relay compute threshold group public key\n // and include it in the on-chain AddKey set during create_account_and_register_user.\n let thresholdClientVerifyingShareB64u: string | null = null;\n if (requestedSignerModeStr === 'threshold-signer') {\n const derived = await webAuthnManager.deriveThresholdEd25519ClientVerifyingShareFromCredential({\n credential,\n nearAccountId,\n wrapKeySalt,\n });\n if (!derived.success || !derived.clientVerifyingShareB64u) {\n throw new Error(derived.error || 'Failed to derive threshold client verifying share');\n }\n thresholdClientVerifyingShareB64u = derived.clientVerifyingShareB64u;\n }\n\n // Step 4-5: Create account and register with contract using the relay (atomic)\n onEvent?.({\n step: 2,\n phase: RegistrationPhase.STEP_2_KEY_GENERATION,\n status: RegistrationStatus.SUCCESS,\n message: 'Wallet derived successfully from Passkey',\n verified: true,\n nearAccountId: nearAccountId,\n nearPublicKey: nearPublicKey,\n vrfPublicKey: vrfChallenge.vrfPublicKey,\n });\n\n let accountAndRegistrationResult;\n accountAndRegistrationResult = await createAccountAndRegisterWithRelayServer(\n context,\n nearAccountId,\n nearPublicKey,\n credential,\n vrfChallenge,\n deterministicVrfKeyResult.vrfPublicKey,\n authenticatorOptions,\n onEvent,\n {\n thresholdEd25519: thresholdClientVerifyingShareB64u\n ? { clientVerifyingShareB64u: thresholdClientVerifyingShareB64u }\n : undefined,\n },\n );\n\n if (!accountAndRegistrationResult.success) {\n throw new Error(accountAndRegistrationResult.error || 'Account creation and registration failed');\n }\n\n // Update registration state based on results\n registrationState.accountCreated = true;\n registrationState.contractRegistered = true;\n registrationState.contractTransactionId = accountAndRegistrationResult.transactionId || null;\n\n // Step 6: Post-commit verification: ensure on-chain access key matches expected public key\n onEvent?.({\n step: 6,\n phase: RegistrationPhase.STEP_6_ACCOUNT_VERIFICATION,\n status: RegistrationStatus.PROGRESS,\n message: 'Verifying on-chain access key matches expected public key...'\n });\n\n const expectedAccessKeys: string[] = [nearPublicKey];\n const thresholdPublicKey = String(accountAndRegistrationResult?.thresholdEd25519?.publicKey || '').trim();\n const relayerKeyId = String(accountAndRegistrationResult?.thresholdEd25519?.relayerKeyId || '').trim();\n\n const accessKeyVerified = await verifyAccountAccessKeysPresent(\n context.nearClient,\n nearAccountId,\n expectedAccessKeys,\n { attempts: 3, delayMs: 200, finality: 'optimistic' },\n );\n\n if (!accessKeyVerified) {\n console.warn('[Registration] Access key not yet visible after atomic registration; continuing optimistically');\n onEvent?.({\n step: 6,\n phase: RegistrationPhase.STEP_6_ACCOUNT_VERIFICATION,\n status: RegistrationStatus.SUCCESS,\n message: 'Access key verification pending (optimistic); continuing...'\n });\n } else {\n onEvent?.({\n step: 6,\n phase: RegistrationPhase.STEP_6_ACCOUNT_VERIFICATION,\n status: RegistrationStatus.SUCCESS,\n message: 'Access key verified on-chain'\n });\n }\n\n // Threshold enrollment can take an extra on-chain tx + key propagation; don't block registration on it.\n activateThresholdEnrollmentPostRegistration({\n requestedSignerMode: requestedSignerModeStr,\n nearAccountId,\n nearPublicKey,\n thresholdPublicKey,\n relayerKeyId,\n clientParticipantId: accountAndRegistrationResult?.thresholdEd25519?.clientParticipantId,\n relayerParticipantId: accountAndRegistrationResult?.thresholdEd25519?.relayerParticipantId,\n thresholdClientVerifyingShareB64u,\n relayerVerifyingShareB64u: String(\n accountAndRegistrationResult?.thresholdEd25519?.relayerVerifyingShareB64u || '',\n ).trim(),\n credential,\n wrapKeySalt,\n webAuthnManager: context.webAuthnManager,\n nearClient: context.nearClient,\n onEvent,\n }).catch(() => {});\n\n // Step 7: Store user data with VRF credentials atomically\n onEvent?.({\n step: 7,\n phase: RegistrationPhase.STEP_7_DATABASE_STORAGE,\n status: RegistrationStatus.PROGRESS,\n message: 'Storing passkey wallet metadata...'\n });\n\n await webAuthnManager.atomicStoreRegistrationData({\n nearAccountId,\n credential,\n publicKey: nearPublicKey,\n encryptedVrfKeypair: deterministicVrfKeyResult.encryptedVrfKeypair,\n vrfPublicKey: deterministicVrfKeyResult.vrfPublicKey,\n serverEncryptedVrfKeypair: deterministicVrfKeyResult.serverEncryptedVrfKeypair,\n });\n\n // Mark database as stored for rollback tracking\n registrationState.databaseStored = true;\n\n onEvent?.({\n step: 7,\n phase: RegistrationPhase.STEP_7_DATABASE_STORAGE,\n status: RegistrationStatus.SUCCESS,\n message: 'Registration metadata stored successfully'\n });\n\n // Step 7: Ensure VRF session is active for auto-login\n // If VRF keypair is already in-memory (saved earlier), skip an extra Touch ID prompt.\n let vrfStatus = await webAuthnManager.checkVrfStatus().catch(() => ({ active: false }));\n if (!vrfStatus?.active) {\n // Prefer a no-prompt unlock using the existing registration credential (PRF already available).\n // Fallback to an explicit authentication ceremony only if needed.\n const unlockNoPrompt = await webAuthnManager.unlockVRFKeypair({\n nearAccountId: nearAccountId,\n encryptedVrfKeypair: deterministicVrfKeyResult.encryptedVrfKeypair,\n credential,\n }).catch((unlockError: unknown) => {\n const message = (unlockError && typeof unlockError === 'object' && 'message' in unlockError)\n ? String((unlockError as { message?: unknown }).message || '')\n : String(unlockError || '');\n return { success: false, error: message };\n });\n\n if (!unlockNoPrompt.success) {\n // Obtain an authentication credential for VRF unlock (separate from registration credential)\n // IMPORTANT: Immediately after account creation, the new access key may not be queryable yet on some RPC nodes.\n // We only need fresh block info for the VRF challenge here, so fetch the block directly to avoid AK lookup failures.\n let txBlockHash = String(transactionContext?.txBlockHash || '').trim();\n let txBlockHeight = String(transactionContext?.txBlockHeight || '').trim();\n if (!txBlockHash || !txBlockHeight) {\n const blockInfo = await context.nearClient.viewBlock({ finality: 'final' });\n txBlockHash = String(blockInfo?.header?.hash || '').trim();\n txBlockHeight = String(blockInfo?.header?.height ?? '').trim();\n }\n const vrfChallenge2 = await webAuthnManager.generateVrfChallengeOnce({\n userId: nearAccountId,\n rpId: webAuthnManager.getRpId(),\n blockHash: txBlockHash,\n blockHeight: txBlockHeight,\n });\n const allowCredentialIds = String(credential?.rawId || '').trim()\n ? [String(credential.rawId)]\n : (await webAuthnManager.getAuthenticatorsByUser(nearAccountId)).map((a) => a.credentialId);\n const authCredential = await webAuthnManager.getAuthenticationCredentialsSerializedDualPrf({\n nearAccountId,\n challenge: vrfChallenge2,\n credentialIds: allowCredentialIds,\n });\n const unlockResult = await webAuthnManager.unlockVRFKeypair({\n nearAccountId: nearAccountId,\n encryptedVrfKeypair: deterministicVrfKeyResult.encryptedVrfKeypair,\n credential: authCredential,\n }).catch((unlockError: unknown) => {\n const message = (unlockError && typeof unlockError === 'object' && 'message' in unlockError)\n ? String((unlockError as { message?: unknown }).message || '')\n : String(unlockError || '');\n return { success: false, error: message };\n });\n\n if (!unlockResult.success) {\n console.warn('VRF keypair unlock failed:', unlockResult.error);\n throw new Error(unlockResult.error);\n }\n } else {\n console.debug('Registration: VRF unlocked using registration credential; skipping extra Touch ID unlock');\n }\n } else {\n console.debug('Registration: VRF session already active; skipping extra Touch ID unlock');\n }\n\n // Initialize current user only after a successful unlock\n try {\n await webAuthnManager.initializeCurrentUser(nearAccountId);\n webAuthnManager.getNonceManager().prefetchBlockheight(context.nearClient).catch(() => {});\n } catch (initErr) {\n console.warn('Failed to initialize current user after registration:', initErr);\n }\n\n onEvent?.({\n step: 8,\n phase: RegistrationPhase.STEP_8_REGISTRATION_COMPLETE,\n status: RegistrationStatus.SUCCESS,\n message: 'Registration completed!'\n });\n\n const successResult = {\n success: true,\n nearAccountId: nearAccountId,\n clientNearPublicKey: nearPublicKey,\n transactionId: registrationState.contractTransactionId,\n vrfRegistration: {\n success: true,\n vrfPublicKey: vrfChallenge.vrfPublicKey,\n encryptedVrfKeypair: deterministicVrfKeyResult.encryptedVrfKeypair,\n contractVerified: accountAndRegistrationResult.success,\n }\n };\n\n afterCall?.(true, successResult);\n return successResult;\n\n } catch (error: unknown) {\n const message = (error && typeof error === 'object' && 'message' in error)\n ? String((error as { message?: unknown }).message || '')\n : String(error || '');\n const stack = (error && typeof error === 'object' && 'stack' in error)\n ? String((error as { stack?: unknown }).stack || '')\n : '';\n console.error('Registration failed:', message, stack);\n\n // Perform rollback based on registration state\n await performRegistrationRollback(\n registrationState,\n nearAccountId,\n webAuthnManager,\n onEvent\n );\n\n // Use centralized error handling\n const errorMessage = getUserFriendlyErrorMessage(error, 'registration', nearAccountId);\n\n const errorObject = new Error(errorMessage);\n onError?.(errorObject);\n\n onEvent?.({\n step: 0,\n phase: RegistrationPhase.REGISTRATION_ERROR,\n status: RegistrationStatus.ERROR,\n message: errorMessage,\n error: errorMessage\n } as RegistrationSSEEvent);\n\n const result = { success: false, error: errorMessage };\n afterCall?.(false);\n return result;\n }\n}\n\n// Backward-compatible wrapper without explicit confirmationConfig override\nexport async function registerPasskey(\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n options: RegistrationHooksOptions,\n authenticatorOptions: AuthenticatorOptions\n): Promise<RegistrationResult> {\n return registerPasskeyInternal(context, nearAccountId, options, authenticatorOptions, undefined);\n}\n\n//////////////////////////////////////\n// HELPER FUNCTIONS\n//////////////////////////////////////\n\n/**\n * Generate a VRF keypair + challenge in VRF wasm worker for WebAuthn registration ceremony bootstrapping\n *\n * ARCHITECTURE: This function solves the chicken-and-egg problem with a single VRF keypair:\n * 1. Generate VRF keypair + challenge (no PRF needed)\n * 2. Persist VRF keypair in worker memory (NOT encrypted yet)\n * 3. Use VRF challenge for WebAuthn ceremony → get PRF output\n * 4. Encrypt the SAME VRF keypair (still in memory) with PRF\n *\n * @param webAuthnManager - WebAuthn manager instance\n * @param nearAccountId - NEAR account ID for VRF input\n * @param blockHeight - Current NEAR block height for freshness\n * @param blockHashBytes - Current NEAR block hash bytes for entropy\n * @returns VRF challenge data (VRF keypair persisted in worker memory)\n */\nexport async function generateBootstrapVrfChallenge(\n context: PasskeyManagerContext,\n nearAccountId: AccountId,\n): Promise<VRFChallenge> {\n\n const { webAuthnManager, nearClient } = context;\n\n const blockInfo = await nearClient.viewBlock({ finality: 'final' });\n\n // Generate VRF keypair and persist in worker memory\n const vrfResult = await webAuthnManager.generateVrfKeypairBootstrap({\n vrfInputData: {\n userId: nearAccountId,\n // Keep VRF rpId consistent with WebAuthn rpId selection logic.\n rpId: webAuthnManager.getRpId(),\n blockHeight: String(blockInfo.header.height),\n blockHash: blockInfo.header.hash,\n },\n saveInMemory: true,\n // VRF keypair persists in worker memory until PRF encryption\n });\n\n if (!vrfResult.vrfChallenge) {\n throw new Error('Registration VRF keypair generation failed');\n }\n return vrfResult.vrfChallenge;\n}\n\n/**\n * Validates registration inputs and throws errors if invalid\n * @param nearAccountId - NEAR account ID to validate\n * @param onEvent - Optional callback for registration progress events\n * @param onError - Optional callback for error handling\n */\nconst validateRegistrationInputs = async (\n context: {\n configs: TatchiConfigs,\n webAuthnManager: WebAuthnManager,\n nearClient: NearClient,\n },\n nearAccountId: AccountId,\n onEvent?: (event: RegistrationSSEEvent) => void,\n onError?: (error: Error) => void,\n) => {\n\n onEvent?.({\n step: 1,\n phase: RegistrationPhase.STEP_1_WEBAUTHN_VERIFICATION,\n status: RegistrationStatus.PROGRESS,\n message: 'Validating registration inputs...'\n } as RegistrationSSEEvent);\n\n // Validation\n if (!nearAccountId) {\n const error = new Error('NEAR account ID is required for registration.');\n onError?.(error);\n throw error;\n }\n // Validate the account ID format\n const validation = validateNearAccountId(nearAccountId);\n if (!validation.valid) {\n const error = new Error(`Invalid NEAR account ID: ${validation.error}`);\n onError?.(error);\n throw error;\n }\n if (!window.isSecureContext) {\n const error = new Error('Passkey operations require a secure context (HTTPS or localhost).');\n onError?.(error);\n throw error;\n }\n\n // On-chain account existence / contract validation is performed by the relay + contract\n // during the atomic create_account_and_register_user call.\n onEvent?.({\n step: 1,\n phase: RegistrationPhase.STEP_1_WEBAUTHN_VERIFICATION,\n status: RegistrationStatus.PROGRESS,\n message: `Account format validated, preparing confirmation`\n } as RegistrationSSEEvent);\n return;\n}\n\n/**\n * Rollback registration data in case of errors\n */\nasync function performRegistrationRollback(\n registrationState: {\n accountCreated: boolean;\n contractRegistered: boolean;\n databaseStored: boolean;\n contractTransactionId: string | null;\n },\n nearAccountId: AccountId,\n webAuthnManager: WebAuthnManager,\n onEvent?: (event: RegistrationSSEEvent) => void\n): Promise<void> {\n console.debug('Starting registration rollback...', registrationState);\n\n // Rollback in reverse order\n try {\n // 1. Always clear any in-memory VRF session established during bootstrap\n await webAuthnManager.clearVrfSession();\n\n // 2. Rollback database storage\n if (registrationState.databaseStored) {\n console.debug('Rolling back database storage...');\n onEvent?.({\n step: 0,\n phase: RegistrationPhase.REGISTRATION_ERROR,\n status: RegistrationStatus.ERROR,\n message: 'Rolling back database storage...',\n error: 'Registration failed - rolling back database storage'\n } as RegistrationSSEEvent);\n\n await webAuthnManager.rollbackUserRegistration(nearAccountId);\n console.debug('Database rollback completed');\n }\n\n // 3. Contract rollback on the Web3Authn contract\n // NOT NEEDED - account creation and contract registration are atomic in the relay server flow\n if (registrationState.contractRegistered) {\n console.debug('Contract registration cannot be rolled back (immutable blockchain state)');\n onEvent?.({\n step: 0,\n phase: RegistrationPhase.REGISTRATION_ERROR,\n status: RegistrationStatus.ERROR,\n message: `Contract registration (tx: ${registrationState.contractTransactionId}) cannot be rolled back`,\n error: 'Registration failed - contract state is immutable'\n } as RegistrationSSEEvent);\n }\n console.debug('Registration rollback completed');\n\n } catch (rollbackError: unknown) {\n console.error('Rollback failed:', rollbackError);\n onEvent?.({\n step: 0,\n phase: RegistrationPhase.REGISTRATION_ERROR,\n status: RegistrationStatus.ERROR,\n message: `Rollback failed: ${\n (rollbackError && typeof rollbackError === 'object' && 'message' in rollbackError)\n ? String((rollbackError as { message?: unknown }).message || '')\n : String(rollbackError || '')\n }`,\n error: 'Both registration and rollback failed'\n } as RegistrationSSEEvent);\n }\n}\n\nasync function activateThresholdEnrollmentPostRegistration(opts: {\n requestedSignerMode: SignerMode['mode'];\n nearAccountId: AccountId;\n nearPublicKey: string;\n thresholdPublicKey: string;\n relayerKeyId: string;\n clientParticipantId?: number;\n relayerParticipantId?: number;\n thresholdClientVerifyingShareB64u: string | null;\n relayerVerifyingShareB64u: string;\n credential: WebAuthnRegistrationCredential;\n wrapKeySalt: string;\n webAuthnManager: WebAuthnManager;\n nearClient: NearClient;\n onEvent?: (event: RegistrationSSEEvent) => void;\n}): Promise<void> {\n // Optional: activate threshold enrollment post-registration by having the client\n // submit AddKey(thresholdPublicKey) signed with the local key.\n if (opts.requestedSignerMode !== 'threshold-signer') return;\n\n const thresholdPublicKey = String(opts.thresholdPublicKey || '').trim();\n const relayerKeyId = String(opts.relayerKeyId || '').trim();\n const clientVerifyingShareB64u = String(opts.thresholdClientVerifyingShareB64u || '').trim();\n const relayerVerifyingShareB64u = String(opts.relayerVerifyingShareB64u || '').trim();\n\n if (!thresholdPublicKey || !relayerKeyId || !clientVerifyingShareB64u || !relayerVerifyingShareB64u) {\n console.warn('[Registration] threshold-signer requested but threshold enrollment details are missing; continuing with local-signer only');\n return;\n }\n\n try {\n opts.onEvent?.({\n step: 6,\n phase: RegistrationPhase.STEP_6_ACCOUNT_VERIFICATION,\n status: RegistrationStatus.PROGRESS,\n message: 'Activating threshold access key onchain...'\n });\n\n // Prepare a single AddKey transaction signed with the local key (no extra TouchID prompt).\n try {\n opts.webAuthnManager.getNonceManager().initializeUser(opts.nearAccountId, opts.nearPublicKey);\n } catch {}\n const txContext = await opts.webAuthnManager.getNonceManager().getNonceBlockHashAndHeight(\n opts.nearClient,\n { force: true },\n );\n\n const signed = await opts.webAuthnManager.signAddKeyThresholdPublicKeyNoPrompt({\n nearAccountId: opts.nearAccountId,\n credential: opts.credential,\n wrapKeySalt: opts.wrapKeySalt,\n transactionContext: txContext,\n thresholdPublicKey,\n relayerVerifyingShareB64u,\n clientParticipantId: opts.clientParticipantId,\n relayerParticipantId: opts.relayerParticipantId,\n });\n\n const signedTx = signed?.signedTransaction;\n if (!signedTx) throw new Error('Failed to sign AddKey(thresholdPublicKey) transaction');\n\n await opts.nearClient.sendTransaction(signedTx, DEFAULT_WAIT_STATUS.thresholdAddKey);\n\n const thresholdKeyVerified = await verifyAccountAccessKeysPresent(\n opts.nearClient,\n opts.nearAccountId,\n [opts.nearPublicKey, thresholdPublicKey],\n { attempts: 6, delayMs: 250, finality: 'optimistic' },\n );\n if (!thresholdKeyVerified) {\n throw new Error('Threshold access key not found on-chain after AddKey');\n }\n\n await IndexedDBManager.nearKeysDB.storeKeyMaterial({\n kind: 'threshold_ed25519_2p_v1',\n nearAccountId: opts.nearAccountId,\n deviceNumber: 1,\n publicKey: thresholdPublicKey,\n wrapKeySalt: opts.wrapKeySalt,\n relayerKeyId,\n clientShareDerivation: 'prf_first_v1',\n participants: buildThresholdEd25519Participants2pV1({\n clientParticipantId: opts.clientParticipantId,\n relayerParticipantId: opts.relayerParticipantId,\n relayerKeyId,\n relayerUrl: opts.webAuthnManager.tatchiPasskeyConfigs?.relayer?.url,\n clientVerifyingShareB64u,\n relayerVerifyingShareB64u,\n clientShareDerivation: 'prf_first_v1',\n }),\n timestamp: Date.now(),\n });\n\n opts.onEvent?.({\n step: 6,\n phase: RegistrationPhase.STEP_6_ACCOUNT_VERIFICATION,\n status: RegistrationStatus.SUCCESS,\n message: 'Threshold access key activated on-chain'\n });\n } catch (e) {\n console.warn('[Registration] threshold enrollment activation failed; continuing with local-signer only:', e);\n }\n}\n\nasync function verifyAccountAccessKeysPresent(\n nearClient: NearClient,\n nearAccountId: string,\n expectedPublicKeys: string[],\n opts?: { attempts?: number; delayMs?: number; finality?: 'optimistic' | 'final' },\n): Promise<boolean> {\n const unique = Array.from(\n new Set(expectedPublicKeys.map((k) => ensureEd25519Prefix(k)).filter(Boolean)),\n );\n if (!unique.length) return false;\n\n const attempts = Math.max(1, Math.floor(opts?.attempts ?? 6));\n const delayMs = Math.max(50, Math.floor(opts?.delayMs ?? 750));\n const finality = opts?.finality ?? 'optimistic';\n\n for (let i = 0; i < attempts; i++) {\n try {\n const accessKeyList = await nearClient.viewAccessKeyList(\n nearAccountId,\n { finality } as any,\n );\n const keys = accessKeyList.keys.map((k) => ensureEd25519Prefix(k.public_key)).filter(Boolean);\n const allPresent = unique.every((expected) => keys.includes(expected));\n if (allPresent) return true;\n } catch {\n // tolerate transient view errors during propagation; retry\n }\n if (i < attempts - 1) {\n await new Promise((res) => setTimeout(res, delayMs));\n }\n }\n return false;\n}\n"],"mappings":"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"}
|
package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js
CHANGED
|
@@ -31,20 +31,11 @@ function createConfirmSession({ adapters, worker, request, confirmationConfig, t
|
|
|
31
31
|
adapters.ui.closeModalSafely(!!decision.confirmed, confirmHandle);
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
|
-
const cleanupAndRethrow = (err) => {
|
|
35
|
-
try {
|
|
36
|
-
adapters.near.releaseReservedNonces(reservedNonces);
|
|
37
|
-
adapters.ui.closeModalSafely(false, confirmHandle);
|
|
38
|
-
} finally {
|
|
39
|
-
throw err;
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
34
|
return {
|
|
43
35
|
setReservedNonces,
|
|
44
36
|
updateUI,
|
|
45
37
|
promptUser,
|
|
46
|
-
confirmAndCloseModal
|
|
47
|
-
cleanupAndRethrow
|
|
38
|
+
confirmAndCloseModal
|
|
48
39
|
};
|
|
49
40
|
}
|
|
50
41
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","names":["reservedNonces: string[] | undefined","confirmHandle: ConfirmUIHandle | undefined"],"sources":["../../../../../../../../../src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.ts"],"sourcesContent":["import type { ConfirmationConfig } from '../../../../types/signer-worker';\nimport type { ConfirmUIHandle, ConfirmUIUpdate } from '../../../LitComponents/confirm-ui';\nimport type { KnownSecureConfirmRequest, SecureConfirmDecision, TransactionSummary } from '../types';\nimport type { VRFChallenge } from '../../../../types';\nimport { sendConfirmResponse } from './common';\nimport type { ConfirmTxFlowAdapters } from './interfaces';\n\nexport function createConfirmSession({\n adapters,\n worker,\n request,\n confirmationConfig,\n transactionSummary,\n}: {\n adapters: ConfirmTxFlowAdapters;\n worker: Worker;\n request: KnownSecureConfirmRequest;\n confirmationConfig: ConfirmationConfig;\n transactionSummary: TransactionSummary;\n}): {\n setReservedNonces: (nonces?: string[]) => void;\n updateUI: (props: ConfirmUIUpdate) => void;\n promptUser: (args: { vrfChallenge?: Partial<VRFChallenge> }) => Promise<{ confirmed: boolean; error?: string }>;\n /**\n * Send decision back to worker and perform standard cleanup.\n * - On `confirmed: false`, releases any reserved nonces.\n * - Always closes the confirm UI handle when present.\n */\n confirmAndCloseModal: (decision: SecureConfirmDecision) => void;\n
|
|
1
|
+
{"version":3,"file":"session.js","names":["reservedNonces: string[] | undefined","confirmHandle: ConfirmUIHandle | undefined"],"sources":["../../../../../../../../../src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.ts"],"sourcesContent":["import type { ConfirmationConfig } from '../../../../types/signer-worker';\nimport type { ConfirmUIHandle, ConfirmUIUpdate } from '../../../LitComponents/confirm-ui';\nimport type { KnownSecureConfirmRequest, SecureConfirmDecision, TransactionSummary } from '../types';\nimport type { VRFChallenge } from '../../../../types';\nimport { sendConfirmResponse } from './common';\nimport type { ConfirmTxFlowAdapters } from './interfaces';\n\nexport function createConfirmSession({\n adapters,\n worker,\n request,\n confirmationConfig,\n transactionSummary,\n}: {\n adapters: ConfirmTxFlowAdapters;\n worker: Worker;\n request: KnownSecureConfirmRequest;\n confirmationConfig: ConfirmationConfig;\n transactionSummary: TransactionSummary;\n}): {\n setReservedNonces: (nonces?: string[]) => void;\n updateUI: (props: ConfirmUIUpdate) => void;\n promptUser: (args: { vrfChallenge?: Partial<VRFChallenge> }) => Promise<{ confirmed: boolean; error?: string }>;\n /**\n * Send decision back to worker and perform standard cleanup.\n * - On `confirmed: false`, releases any reserved nonces.\n * - Always closes the confirm UI handle when present.\n */\n confirmAndCloseModal: (decision: SecureConfirmDecision) => void;\n} {\n let reservedNonces: string[] | undefined;\n let confirmHandle: ConfirmUIHandle | undefined;\n\n const setReservedNonces = (nonces?: string[]) => {\n reservedNonces = nonces;\n };\n\n const updateUI = (props: ConfirmUIUpdate) => {\n confirmHandle?.update?.(props);\n };\n\n const promptUser = async ({ vrfChallenge }: { vrfChallenge?: Partial<VRFChallenge> }) => {\n const { confirmed, confirmHandle: handle, error } = await adapters.ui.renderConfirmUI({\n request,\n confirmationConfig,\n transactionSummary,\n vrfChallenge,\n });\n confirmHandle = handle;\n return { confirmed, error };\n };\n\n const confirmAndCloseModal = (decision: SecureConfirmDecision) => {\n try {\n sendConfirmResponse(worker, decision);\n } finally {\n if (!decision.confirmed) {\n adapters.near.releaseReservedNonces(reservedNonces);\n }\n adapters.ui.closeModalSafely(!!decision.confirmed, confirmHandle);\n }\n };\n\n return {\n setReservedNonces,\n updateUI,\n promptUser,\n confirmAndCloseModal,\n };\n}\n"],"mappings":";;;AAOA,SAAgB,qBAAqB,EACnC,UACA,QACA,SACA,oBACA,sBAiBA;CACA,IAAIA;CACJ,IAAIC;CAEJ,MAAM,qBAAqB,WAAsB;AAC/C,mBAAiB;;CAGnB,MAAM,YAAY,UAA2B;AAC3C,iBAAe,SAAS;;CAG1B,MAAM,aAAa,OAAO,EAAE,mBAA6D;EACvF,MAAM,EAAE,WAAW,eAAe,QAAQ,UAAU,MAAM,SAAS,GAAG,gBAAgB;GACpF;GACA;GACA;GACA;;AAEF,kBAAgB;AAChB,SAAO;GAAE;GAAW;;;CAGtB,MAAM,wBAAwB,aAAoC;AAChE,MAAI;AACF,uBAAoB,QAAQ;YACpB;AACR,OAAI,CAAC,SAAS,UACZ,UAAS,KAAK,sBAAsB;AAEtC,YAAS,GAAG,iBAAiB,CAAC,CAAC,SAAS,WAAW;;;AAIvD,QAAO;EACL;EACA;EACA;EACA"}
|
package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js
CHANGED
|
@@ -18,78 +18,70 @@ async function handleRegistrationFlow(ctx, request, worker, opts) {
|
|
|
18
18
|
transactionSummary
|
|
19
19
|
});
|
|
20
20
|
const nearAccountId = getNearAccountId(request);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const nearRpc = await adapters.near.fetchNearContext({
|
|
29
|
-
nearAccountId,
|
|
30
|
-
txCount: 1,
|
|
31
|
-
reserveNonces: true
|
|
32
|
-
});
|
|
33
|
-
if (nearRpc.error && !nearRpc.transactionContext) return session.confirmAndCloseModal({
|
|
34
|
-
requestId: request.requestId,
|
|
35
|
-
intentDigest: getIntentDigest(request),
|
|
36
|
-
confirmed: false,
|
|
37
|
-
error: `${ERROR_MESSAGES.nearRpcFailed}: ${nearRpc.details}`
|
|
38
|
-
});
|
|
39
|
-
const transactionContext = nearRpc.transactionContext;
|
|
40
|
-
session.setReservedNonces(nearRpc.reservedNonces);
|
|
41
|
-
const computeBoundIntentDigestB64u = async () => {
|
|
42
|
-
const uiIntentDigest = getIntentDigest(request);
|
|
43
|
-
if (!uiIntentDigest) throw new Error("Missing intentDigest for registration flow");
|
|
44
|
-
return sha256Base64UrlUtf8(uiIntentDigest);
|
|
45
|
-
};
|
|
46
|
-
const rpId = adapters.vrf.getRpId();
|
|
47
|
-
const boundIntentDigestB64u = await computeBoundIntentDigestB64u();
|
|
48
|
-
const bootstrap = await adapters.vrf.generateVrfKeypairBootstrap({
|
|
49
|
-
vrfInputData: {
|
|
50
|
-
userId: nearAccountId,
|
|
51
|
-
rpId,
|
|
52
|
-
blockHeight: transactionContext.txBlockHeight,
|
|
53
|
-
blockHash: transactionContext.txBlockHash,
|
|
54
|
-
intentDigest: boundIntentDigestB64u
|
|
55
|
-
},
|
|
56
|
-
saveInMemory: true,
|
|
57
|
-
sessionId: request.requestId
|
|
58
|
-
});
|
|
59
|
-
let uiVrfChallenge = bootstrap.vrfChallenge;
|
|
60
|
-
console.debug("[RegistrationFlow] VRF bootstrap ok", { blockHeight: uiVrfChallenge.blockHeight });
|
|
61
|
-
const { confirmed, error: uiError } = await session.promptUser({ vrfChallenge: uiVrfChallenge });
|
|
62
|
-
if (!confirmed) {
|
|
63
|
-
console.debug("[RegistrationFlow] user cancelled");
|
|
64
|
-
return session.confirmAndCloseModal({
|
|
21
|
+
try {
|
|
22
|
+
const nearRpc = await adapters.near.fetchNearContext({
|
|
23
|
+
nearAccountId,
|
|
24
|
+
txCount: 1,
|
|
25
|
+
reserveNonces: true
|
|
26
|
+
});
|
|
27
|
+
if (nearRpc.error && !nearRpc.transactionContext) return session.confirmAndCloseModal({
|
|
65
28
|
requestId: request.requestId,
|
|
66
29
|
intentDigest: getIntentDigest(request),
|
|
67
30
|
confirmed: false,
|
|
68
|
-
error:
|
|
31
|
+
error: `${ERROR_MESSAGES.nearRpcFailed}: ${nearRpc.details}`
|
|
69
32
|
});
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
33
|
+
const transactionContext = nearRpc.transactionContext;
|
|
34
|
+
session.setReservedNonces(nearRpc.reservedNonces);
|
|
35
|
+
const computeBoundIntentDigestB64u = async () => {
|
|
36
|
+
const uiIntentDigest = getIntentDigest(request);
|
|
37
|
+
if (!uiIntentDigest) throw new Error("Missing intentDigest for registration flow");
|
|
38
|
+
return sha256Base64UrlUtf8(uiIntentDigest);
|
|
39
|
+
};
|
|
40
|
+
const rpId = adapters.vrf.getRpId();
|
|
41
|
+
const boundIntentDigestB64u = await computeBoundIntentDigestB64u();
|
|
42
|
+
const bootstrap = await adapters.vrf.generateVrfKeypairBootstrap({
|
|
43
|
+
vrfInputData: {
|
|
44
|
+
userId: nearAccountId,
|
|
45
|
+
rpId,
|
|
46
|
+
blockHeight: transactionContext.txBlockHeight,
|
|
47
|
+
blockHash: transactionContext.txBlockHash,
|
|
48
|
+
intentDigest: boundIntentDigestB64u
|
|
49
|
+
},
|
|
50
|
+
saveInMemory: true,
|
|
51
|
+
sessionId: request.requestId
|
|
87
52
|
});
|
|
88
|
-
|
|
89
|
-
|
|
53
|
+
let uiVrfChallenge = bootstrap.vrfChallenge;
|
|
54
|
+
console.debug("[RegistrationFlow] VRF bootstrap ok", { blockHeight: uiVrfChallenge.blockHeight });
|
|
55
|
+
const { confirmed, error: uiError } = await session.promptUser({ vrfChallenge: uiVrfChallenge });
|
|
56
|
+
if (!confirmed) {
|
|
57
|
+
console.debug("[RegistrationFlow] user cancelled");
|
|
58
|
+
return session.confirmAndCloseModal({
|
|
59
|
+
requestId: request.requestId,
|
|
60
|
+
intentDigest: getIntentDigest(request),
|
|
61
|
+
confirmed: false,
|
|
62
|
+
error: uiError
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const refreshed = await adapters.vrf.maybeRefreshVrfChallenge(request, nearAccountId);
|
|
67
|
+
uiVrfChallenge = refreshed.vrfChallenge;
|
|
68
|
+
session.updateUI({ vrfChallenge: uiVrfChallenge });
|
|
69
|
+
console.debug("[RegistrationFlow] VRF JIT refresh ok", { blockHeight: uiVrfChallenge.blockHeight });
|
|
70
|
+
} catch (e) {
|
|
71
|
+
console.debug("[RegistrationFlow] VRF JIT refresh skipped", e);
|
|
72
|
+
}
|
|
73
|
+
let credential;
|
|
74
|
+
let deviceNumber = request.payload?.deviceNumber ?? 1;
|
|
75
|
+
const tryCreate = async (dn) => {
|
|
76
|
+
console.debug("[RegistrationFlow] navigator.credentials.create start", { deviceNumber: dn });
|
|
77
|
+
return await adapters.webauthn.createRegistrationCredential({
|
|
78
|
+
nearAccountId,
|
|
79
|
+
challenge: uiVrfChallenge,
|
|
80
|
+
deviceNumber: dn
|
|
81
|
+
});
|
|
82
|
+
};
|
|
90
83
|
try {
|
|
91
84
|
credential = await tryCreate(deviceNumber);
|
|
92
|
-
console.debug("[RegistrationFlow] credentials.create ok");
|
|
93
85
|
} catch (e) {
|
|
94
86
|
const err = toError(e);
|
|
95
87
|
const name = String(err?.name || "");
|
|
@@ -140,14 +132,13 @@ async function handleRegistrationFlow(ctx, request, worker, opts) {
|
|
|
140
132
|
} catch (err) {
|
|
141
133
|
const cancelled = isUserCancelledSecureConfirm(err);
|
|
142
134
|
const msg = String(toError(err)?.message || err || "");
|
|
143
|
-
if (/Missing PRF result/i.test(msg) || /Missing PRF results/i.test(msg)) return session.cleanupAndRethrow(err);
|
|
144
135
|
if (cancelled) window.parent?.postMessage({ type: "WALLET_UI_CLOSED" }, "*");
|
|
145
136
|
const isPrfBrowserUnsupported = /WebAuthn PRF output is missing from navigator\.credentials\.create\(\)/i.test(msg) || /does not fully support the WebAuthn PRF extension during registration/i.test(msg) || /roaming hardware authenticators .* not supported in this flow/i.test(msg);
|
|
146
137
|
return session.confirmAndCloseModal({
|
|
147
138
|
requestId: request.requestId,
|
|
148
139
|
intentDigest: getIntentDigest(request),
|
|
149
140
|
confirmed: false,
|
|
150
|
-
error: cancelled ? ERROR_MESSAGES.cancelled : isPrfBrowserUnsupported ? msg : ERROR_MESSAGES.collectCredentialsFailed
|
|
141
|
+
error: cancelled ? ERROR_MESSAGES.cancelled : isPrfBrowserUnsupported ? msg : msg || ERROR_MESSAGES.collectCredentialsFailed
|
|
151
142
|
});
|
|
152
143
|
}
|
|
153
144
|
}
|