@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.
Files changed (173) hide show
  1. package/dist/cjs/core/IndexedDBManager/passkeyClientDB.js +2 -2
  2. package/dist/cjs/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
  3. package/dist/cjs/core/TatchiPasskey/faucets/createAccountRelayServer.js +9 -8
  4. package/dist/cjs/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
  5. package/dist/cjs/core/TatchiPasskey/login.js +1 -1
  6. package/dist/cjs/core/TatchiPasskey/login.js.map +1 -1
  7. package/dist/cjs/core/TatchiPasskey/registration.js +67 -56
  8. package/dist/cjs/core/TatchiPasskey/registration.js.map +1 -1
  9. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js +1 -10
  10. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js.map +1 -1
  11. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +58 -67
  12. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
  13. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +74 -75
  14. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
  15. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js +17 -7
  16. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js.map +1 -1
  17. package/dist/cjs/core/WebAuthnManager/index.js +3 -3
  18. package/dist/cjs/core/WebAuthnManager/index.js.map +1 -1
  19. package/dist/cjs/core/defaultConfigs.js +3 -1
  20. package/dist/cjs/core/defaultConfigs.js.map +1 -1
  21. package/dist/cjs/react/components/AccountMenuButton/TransactionSettingsSection.js +3 -3
  22. package/dist/cjs/react/components/AccountMenuButton/TransactionSettingsSection.js.map +1 -1
  23. package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CRlobBrN.css → PasskeyAuthMenu-D2eRb2-S.css} +3 -1
  24. package/dist/cjs/react/components/PasskeyAuthMenu/PasskeyAuthMenu-D2eRb2-S.css.map +1 -0
  25. package/dist/cjs/react/components/PasskeyAuthMenu/preload.js +1 -1
  26. package/dist/cjs/react/components/PasskeyAuthMenu/preload.js.map +1 -1
  27. package/dist/cjs/react/components/PasskeyAuthMenu/shell.js +52 -13
  28. package/dist/cjs/react/components/PasskeyAuthMenu/shell.js.map +1 -1
  29. package/dist/cjs/react/components/PasskeyAuthMenu/skeleton.js +4 -2
  30. package/dist/cjs/react/components/PasskeyAuthMenu/skeleton.js.map +1 -1
  31. package/dist/cjs/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js +5 -1
  32. package/dist/cjs/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js.map +1 -1
  33. package/dist/cjs/react/index.js +1 -1
  34. package/dist/cjs/react/src/core/IndexedDBManager/passkeyClientDB.js +2 -2
  35. package/dist/cjs/react/src/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
  36. package/dist/cjs/react/src/core/TatchiPasskey/faucets/createAccountRelayServer.js +9 -8
  37. package/dist/cjs/react/src/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
  38. package/dist/cjs/react/src/core/TatchiPasskey/login.js +1 -1
  39. package/dist/cjs/react/src/core/TatchiPasskey/login.js.map +1 -1
  40. package/dist/cjs/react/src/core/TatchiPasskey/registration.js +67 -56
  41. package/dist/cjs/react/src/core/TatchiPasskey/registration.js.map +1 -1
  42. package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js +1 -10
  43. package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js.map +1 -1
  44. package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +58 -67
  45. package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
  46. package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +74 -75
  47. package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
  48. package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js +17 -7
  49. package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js.map +1 -1
  50. package/dist/cjs/react/src/core/WebAuthnManager/index.js +3 -3
  51. package/dist/cjs/react/src/core/WebAuthnManager/index.js.map +1 -1
  52. package/dist/cjs/react/src/core/defaultConfigs.js +3 -1
  53. package/dist/cjs/react/src/core/defaultConfigs.js.map +1 -1
  54. package/dist/cjs/server/core/AuthService.js +49 -6
  55. package/dist/cjs/server/core/AuthService.js.map +1 -1
  56. package/dist/cjs/server/sdk/src/core/defaultConfigs.js.map +1 -1
  57. package/dist/esm/core/IndexedDBManager/passkeyClientDB.js +2 -2
  58. package/dist/esm/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
  59. package/dist/esm/core/TatchiPasskey/faucets/createAccountRelayServer.js +9 -8
  60. package/dist/esm/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
  61. package/dist/esm/core/TatchiPasskey/login.js +1 -1
  62. package/dist/esm/core/TatchiPasskey/login.js.map +1 -1
  63. package/dist/esm/core/TatchiPasskey/registration.js +67 -56
  64. package/dist/esm/core/TatchiPasskey/registration.js.map +1 -1
  65. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js +1 -10
  66. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js.map +1 -1
  67. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +58 -67
  68. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
  69. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +74 -75
  70. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
  71. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js +17 -7
  72. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js.map +1 -1
  73. package/dist/esm/core/WebAuthnManager/index.js +3 -3
  74. package/dist/esm/core/WebAuthnManager/index.js.map +1 -1
  75. package/dist/esm/core/defaultConfigs.js +3 -1
  76. package/dist/esm/core/defaultConfigs.js.map +1 -1
  77. package/dist/esm/react/components/AccountMenuButton/TransactionSettingsSection.js +3 -3
  78. package/dist/esm/react/components/AccountMenuButton/TransactionSettingsSection.js.map +1 -1
  79. package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-D2VHZ04W.css → PasskeyAuthMenu-qTHAv58Z.css} +3 -1
  80. package/dist/esm/react/components/PasskeyAuthMenu/PasskeyAuthMenu-qTHAv58Z.css.map +1 -0
  81. package/dist/esm/react/components/PasskeyAuthMenu/preload.js +1 -1
  82. package/dist/esm/react/components/PasskeyAuthMenu/preload.js.map +1 -1
  83. package/dist/esm/react/components/PasskeyAuthMenu/shell.js +52 -13
  84. package/dist/esm/react/components/PasskeyAuthMenu/shell.js.map +1 -1
  85. package/dist/esm/react/components/PasskeyAuthMenu/skeleton.js +4 -2
  86. package/dist/esm/react/components/PasskeyAuthMenu/skeleton.js.map +1 -1
  87. package/dist/esm/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js +5 -1
  88. package/dist/esm/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js.map +1 -1
  89. package/dist/esm/react/index.js +1 -1
  90. package/dist/esm/react/src/core/IndexedDBManager/passkeyClientDB.js +2 -2
  91. package/dist/esm/react/src/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
  92. package/dist/esm/react/src/core/TatchiPasskey/faucets/createAccountRelayServer.js +9 -8
  93. package/dist/esm/react/src/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
  94. package/dist/esm/react/src/core/TatchiPasskey/login.js +1 -1
  95. package/dist/esm/react/src/core/TatchiPasskey/login.js.map +1 -1
  96. package/dist/esm/react/src/core/TatchiPasskey/registration.js +67 -56
  97. package/dist/esm/react/src/core/TatchiPasskey/registration.js.map +1 -1
  98. package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js +1 -10
  99. package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js.map +1 -1
  100. package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +58 -67
  101. package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
  102. package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +74 -75
  103. package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
  104. package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js +17 -7
  105. package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js.map +1 -1
  106. package/dist/esm/react/src/core/WebAuthnManager/index.js +3 -3
  107. package/dist/esm/react/src/core/WebAuthnManager/index.js.map +1 -1
  108. package/dist/esm/react/src/core/defaultConfigs.js +3 -1
  109. package/dist/esm/react/src/core/defaultConfigs.js.map +1 -1
  110. package/dist/esm/react/styles/styles.css +2 -0
  111. package/dist/esm/sdk/{EmailRecovery-Dl8b4ONg.js → EmailRecovery-Y7rurd4B.js} +3 -3
  112. package/dist/esm/sdk/{EmailRecovery-v9oNO2Tc.js → EmailRecovery-lsjLWApQ.js} +1 -1
  113. package/dist/esm/sdk/{IndexedDBManager-B1cUvdyY.js → IndexedDBManager-CmdN7smS.js} +3 -3
  114. package/dist/esm/sdk/{createAdapters-Dv7ZJPf1.js → createAdapters-4c8mBiD5.js} +2 -11
  115. package/dist/esm/sdk/{createAdapters-Dv7ZJPf1.js.map → createAdapters-4c8mBiD5.js.map} +1 -1
  116. package/dist/esm/sdk/{createAdapters-1Hmc1vVC.js → createAdapters-DF32SIZa.js} +1 -10
  117. package/dist/esm/sdk/{defaultConfigs-BmCU1_qI.js → defaultConfigs-BQqiXif-.js} +3 -1
  118. package/dist/esm/sdk/{emailRecovery-4J-g9tlY.js → emailRecovery-C0LSDleV.js} +5 -5
  119. package/dist/esm/sdk/{getDeviceNumber-f8bfPB9U.js → getDeviceNumber-WiNzKx1x.js} +4 -2
  120. package/dist/esm/sdk/{getDeviceNumber-f8bfPB9U.js.map → getDeviceNumber-WiNzKx1x.js.map} +1 -1
  121. package/dist/esm/sdk/{linkDevice-C98klpcE.js → linkDevice-Ds1GNIDk.js} +4 -4
  122. package/dist/esm/sdk/{localOnly-40zxrBMm.js → localOnly-COpDBMkm.js} +2 -2
  123. package/dist/esm/sdk/{localOnly-40zxrBMm.js.map → localOnly-COpDBMkm.js.map} +1 -1
  124. package/dist/esm/sdk/{localOnly-BZPBj14l.js → localOnly-DQQuqgjJ.js} +1 -1
  125. package/dist/esm/sdk/{login-DnROv3eA.js → login-BKhTuGcy.js} +3 -3
  126. package/dist/esm/sdk/offline-export-app.js +29 -19
  127. package/dist/esm/sdk/offline-export-app.js.map +1 -1
  128. package/dist/esm/sdk/{registration-BP9M3tE1.js → registration-BR2G9tz_.js} +59 -68
  129. package/dist/esm/sdk/{registration-MrAOC8Ub.js → registration-R70lvG_o.js} +60 -69
  130. package/dist/esm/sdk/registration-R70lvG_o.js.map +1 -0
  131. package/dist/esm/sdk/{router-BEGGuWaB.js → router-2aGn-CTp.js} +1 -1
  132. package/dist/esm/sdk/{rpcCalls-CMzj_Va_.js → rpcCalls-BPI0icZG.js} +2 -2
  133. package/dist/esm/sdk/{rpcCalls-B44MZora.js → rpcCalls-BW3M_q3-.js} +1 -1
  134. package/dist/esm/sdk/{scanDevice-Cp-r-Z2T.js → scanDevice-BBSehlMx.js} +4 -4
  135. package/dist/esm/sdk/{syncAccount-CqWCmBVb.js → syncAccount-DEZHBiRa.js} +4 -4
  136. package/dist/esm/sdk/{syncAccount-Dt5jJbEB.js → syncAccount-DHKtl-xh.js} +2 -2
  137. package/dist/esm/sdk/{transactions-DAZrPW-6.js → transactions-Cg1TIUyK.js} +76 -77
  138. package/dist/esm/sdk/{transactions-CrjP8yPD.js → transactions-CxsklyCK.js} +77 -78
  139. package/dist/esm/sdk/transactions-CxsklyCK.js.map +1 -0
  140. package/dist/esm/sdk/wallet-iframe-host.js +116 -94
  141. package/dist/esm/server/core/AuthService.js +49 -6
  142. package/dist/esm/server/core/AuthService.js.map +1 -1
  143. package/dist/esm/server/sdk/src/core/defaultConfigs.js.map +1 -1
  144. package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker_bg.wasm +0 -0
  145. package/dist/types/src/__tests__/setup/bootstrap.d.ts.map +1 -1
  146. package/dist/types/src/core/IndexedDBManager/passkeyClientDB.d.ts +1 -1
  147. package/dist/types/src/core/IndexedDBManager/passkeyClientDB.d.ts.map +1 -1
  148. package/dist/types/src/core/TatchiPasskey/faucets/createAccountRelayServer.d.ts +6 -6
  149. package/dist/types/src/core/TatchiPasskey/faucets/createAccountRelayServer.d.ts.map +1 -1
  150. package/dist/types/src/core/TatchiPasskey/registration.d.ts.map +1 -1
  151. package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.d.ts +0 -5
  152. package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.d.ts.map +1 -1
  153. package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.d.ts.map +1 -1
  154. package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.d.ts.map +1 -1
  155. package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.d.ts.map +1 -1
  156. package/dist/types/src/core/WebAuthnManager/index.d.ts +1 -1
  157. package/dist/types/src/core/WebAuthnManager/index.d.ts.map +1 -1
  158. package/dist/types/src/core/defaultConfigs.d.ts.map +1 -1
  159. package/dist/types/src/react/components/PasskeyAuthMenu/preload.d.ts.map +1 -1
  160. package/dist/types/src/react/components/PasskeyAuthMenu/shell.d.ts.map +1 -1
  161. package/dist/types/src/react/components/PasskeyAuthMenu/skeleton.d.ts +1 -1
  162. package/dist/types/src/react/components/PasskeyAuthMenu/skeleton.d.ts.map +1 -1
  163. package/dist/types/src/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.d.ts.map +1 -1
  164. package/dist/types/src/server/core/AuthService.d.ts.map +1 -1
  165. package/dist/workers/offline-export-sw.js +156 -1
  166. package/dist/workers/wasm_vrf_worker_bg.wasm +0 -0
  167. package/dist/workers/web3authn-signer.worker.js +1360 -2
  168. package/dist/workers/web3authn-vrf.worker.js +2857 -2
  169. package/package.json +1 -1
  170. package/dist/cjs/react/components/PasskeyAuthMenu/PasskeyAuthMenu-CRlobBrN.css.map +0 -1
  171. package/dist/esm/react/components/PasskeyAuthMenu/PasskeyAuthMenu-D2VHZ04W.css.map +0 -1
  172. package/dist/esm/sdk/registration-MrAOC8Ub.js.map +0 -1
  173. 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: "Account available, generating credentials..."
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
- if (!accessKeyVerified) throw new Error("On-chain access key mismatch or not found after registration");
148
- onEvent?.({
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
- await activateThresholdEnrollmentPostRegistration({
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 blockInfo = await context.nearClient.viewBlock({ finality: "final" });
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: authCredential
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 (!unlockResult.success) {
224
- console.warn("VRF keypair unlock failed:", unlockResult.error);
225
- throw new Error(unlockResult.error);
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, context.nearClient);
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 { fullAccessKeys, functionCallAccessKeys } = await nearClient.getAccessKeys({ account: nearAccountId });
429
- const keys = [...fullAccessKeys, ...functionCallAccessKeys].map((k) => ensureEd25519Prefix(k.public_key));
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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"}
@@ -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 /**\n * Cleanup + rethrow helper for invariant failures (e.g. missing PRF outputs),\n * where tests/logic expect no worker response envelope.\n */\n cleanupAndRethrow: (err: unknown) => never;\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 const cleanupAndRethrow = (err: unknown): never => {\n try {\n adapters.near.releaseReservedNonces(reservedNonces);\n adapters.ui.closeModalSafely(false, confirmHandle);\n } finally {\n throw err;\n }\n };\n\n return {\n setReservedNonces,\n updateUI,\n promptUser,\n confirmAndCloseModal,\n cleanupAndRethrow,\n };\n}\n"],"mappings":";;;AAOA,SAAgB,qBAAqB,EACnC,UACA,QACA,SACA,oBACA,sBAsBA;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;;;CAIvD,MAAM,qBAAqB,QAAwB;AACjD,MAAI;AACF,YAAS,KAAK,sBAAsB;AACpC,YAAS,GAAG,iBAAiB,OAAO;YAC5B;AACR,SAAM;;;AAIV,QAAO;EACL;EACA;EACA;EACA;EACA"}
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"}
@@ -18,78 +18,70 @@ async function handleRegistrationFlow(ctx, request, worker, opts) {
18
18
  transactionSummary
19
19
  });
20
20
  const nearAccountId = getNearAccountId(request);
21
- console.debug("[RegistrationFlow] start", {
22
- nearAccountId,
23
- uiMode: confirmationConfig?.uiMode,
24
- behavior: confirmationConfig?.behavior,
25
- theme: confirmationConfig?.theme,
26
- intentDigest: transactionSummary?.intentDigest
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: uiError
31
+ error: `${ERROR_MESSAGES.nearRpcFailed}: ${nearRpc.details}`
69
32
  });
70
- }
71
- try {
72
- const refreshed = await adapters.vrf.maybeRefreshVrfChallenge(request, nearAccountId);
73
- uiVrfChallenge = refreshed.vrfChallenge;
74
- session.updateUI({ vrfChallenge: uiVrfChallenge });
75
- console.debug("[RegistrationFlow] VRF JIT refresh ok", { blockHeight: uiVrfChallenge.blockHeight });
76
- } catch (e) {
77
- console.debug("[RegistrationFlow] VRF JIT refresh skipped", e);
78
- }
79
- let credential;
80
- let deviceNumber = request.payload?.deviceNumber ?? 1;
81
- const tryCreate = async (dn) => {
82
- console.debug("[RegistrationFlow] navigator.credentials.create start", { deviceNumber: dn });
83
- return await adapters.webauthn.createRegistrationCredential({
84
- nearAccountId,
85
- challenge: uiVrfChallenge,
86
- deviceNumber: dn
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
- try {
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
  }