@metamask-previews/seedless-onboarding-controller 0.0.0-preview-c4b7ea2e → 0.0.0-preview-e9f8bc88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/dist/SecretMetadata.cjs.map +1 -1
- package/dist/SecretMetadata.d.cts +0 -5
- package/dist/SecretMetadata.d.cts.map +1 -1
- package/dist/SecretMetadata.d.mts +0 -5
- package/dist/SecretMetadata.d.mts.map +1 -1
- package/dist/SecretMetadata.mjs.map +1 -1
- package/dist/SeedlessOnboardingController.cjs +328 -117
- package/dist/SeedlessOnboardingController.cjs.map +1 -1
- package/dist/SeedlessOnboardingController.d.cts +47 -1
- package/dist/SeedlessOnboardingController.d.cts.map +1 -1
- package/dist/SeedlessOnboardingController.d.mts +47 -1
- package/dist/SeedlessOnboardingController.d.mts.map +1 -1
- package/dist/SeedlessOnboardingController.mjs +330 -119
- package/dist/SeedlessOnboardingController.mjs.map +1 -1
- package/dist/constants.cjs +5 -1
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +5 -1
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.mts +5 -1
- package/dist/constants.d.mts.map +1 -1
- package/dist/constants.mjs +4 -0
- package/dist/constants.mjs.map +1 -1
- package/dist/errors.cjs +26 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +13 -0
- package/dist/errors.d.cts.map +1 -1
- package/dist/errors.d.mts +13 -0
- package/dist/errors.d.mts.map +1 -1
- package/dist/errors.mjs +24 -0
- package/dist/errors.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +16 -1
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +16 -1
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -10,13 +10,14 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
11
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
12
|
};
|
|
13
|
-
var _SeedlessOnboardingController_instances, _SeedlessOnboardingController_vaultEncryptor, _SeedlessOnboardingController_vaultOperationMutex, _SeedlessOnboardingController_isUnlocked, _SeedlessOnboardingController_setUnlocked, _SeedlessOnboardingController_persistOprfKey, _SeedlessOnboardingController_recoverEncKey, _SeedlessOnboardingController_changeEncryptionKey, _SeedlessOnboardingController_encryptAndStoreSeedPhraseBackup, _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey, _SeedlessOnboardingController_withPersistedSeedPhraseBackupsState, _SeedlessOnboardingController_filterDupesAndUpdateSocialBackupsMetadata, _SeedlessOnboardingController_createNewVaultWithAuthData, _SeedlessOnboardingController_updateVault, _SeedlessOnboardingController_withVaultLock, _SeedlessOnboardingController_serializeKeyData, _SeedlessOnboardingController_parseVaultData, _SeedlessOnboardingController_assertIsUnlocked, _SeedlessOnboardingController_assertIsAuthenticatedUser, _SeedlessOnboardingController_assertIsValidVaultData;
|
|
13
|
+
var _SeedlessOnboardingController_instances, _SeedlessOnboardingController_vaultEncryptor, _SeedlessOnboardingController_controllerOperationMutex, _SeedlessOnboardingController_vaultOperationMutex, _SeedlessOnboardingController_isUnlocked, _SeedlessOnboardingController_recoverPassword, _SeedlessOnboardingController_setUnlocked, _SeedlessOnboardingController_persistOprfKey, _SeedlessOnboardingController_persistAuthPubKey, _SeedlessOnboardingController_recoverAuthPubKey, _SeedlessOnboardingController_recoverEncKey, _SeedlessOnboardingController_changeEncryptionKey, _SeedlessOnboardingController_encryptAndStoreSeedPhraseBackup, _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey, _SeedlessOnboardingController_withPersistedSeedPhraseBackupsState, _SeedlessOnboardingController_filterDupesAndUpdateSocialBackupsMetadata, _SeedlessOnboardingController_createNewVaultWithAuthData, _SeedlessOnboardingController_updateVault, _SeedlessOnboardingController_withControllerLock, _SeedlessOnboardingController_withVaultLock, _SeedlessOnboardingController_serializeKeyData, _SeedlessOnboardingController_parseVaultData, _SeedlessOnboardingController_assertIsUnlocked, _SeedlessOnboardingController_assertIsAuthenticatedUser, _SeedlessOnboardingController_assertIsSRPBackedUpUser, _SeedlessOnboardingController_assertPasswordInSync, _SeedlessOnboardingController_resetPasswordOutdatedCache, _SeedlessOnboardingController_assertIsValidVaultData;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.SeedlessOnboardingController = exports.getDefaultSeedlessOnboardingControllerState = void 0;
|
|
16
16
|
const auth_network_utils_1 = require("@metamask/auth-network-utils");
|
|
17
17
|
const base_controller_1 = require("@metamask/base-controller");
|
|
18
18
|
const toprf_secure_backup_1 = require("@metamask/toprf-secure-backup");
|
|
19
19
|
const utils_1 = require("@metamask/utils");
|
|
20
|
+
const secp256k1_1 = require("@noble/curves/secp256k1");
|
|
20
21
|
const async_mutex_1 = require("async-mutex");
|
|
21
22
|
const constants_1 = require("./constants.cjs");
|
|
22
23
|
const errors_1 = require("./errors.cjs");
|
|
@@ -51,7 +52,7 @@ const seedlessOnboardingMetadata = {
|
|
|
51
52
|
anonymous: true,
|
|
52
53
|
},
|
|
53
54
|
nodeAuthTokens: {
|
|
54
|
-
persist:
|
|
55
|
+
persist: true,
|
|
55
56
|
anonymous: true,
|
|
56
57
|
},
|
|
57
58
|
authConnection: {
|
|
@@ -82,6 +83,14 @@ const seedlessOnboardingMetadata = {
|
|
|
82
83
|
persist: false,
|
|
83
84
|
anonymous: true,
|
|
84
85
|
},
|
|
86
|
+
authPubKey: {
|
|
87
|
+
persist: true,
|
|
88
|
+
anonymous: true,
|
|
89
|
+
},
|
|
90
|
+
passwordOutdatedCache: {
|
|
91
|
+
persist: true,
|
|
92
|
+
anonymous: true,
|
|
93
|
+
},
|
|
85
94
|
};
|
|
86
95
|
class SeedlessOnboardingController extends base_controller_1.BaseController {
|
|
87
96
|
/**
|
|
@@ -105,6 +114,7 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
|
|
|
105
114
|
});
|
|
106
115
|
_SeedlessOnboardingController_instances.add(this);
|
|
107
116
|
_SeedlessOnboardingController_vaultEncryptor.set(this, void 0);
|
|
117
|
+
_SeedlessOnboardingController_controllerOperationMutex.set(this, new async_mutex_1.Mutex());
|
|
108
118
|
_SeedlessOnboardingController_vaultOperationMutex.set(this, new async_mutex_1.Mutex());
|
|
109
119
|
/**
|
|
110
120
|
* Controller lock state.
|
|
@@ -140,35 +150,37 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
|
|
|
140
150
|
* @returns A promise that resolves to the authentication result.
|
|
141
151
|
*/
|
|
142
152
|
async authenticate(params) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
state
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
153
|
+
return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, async () => {
|
|
154
|
+
try {
|
|
155
|
+
const { idTokens, authConnectionId, groupedAuthConnectionId, userId, authConnection, socialLoginEmail, } = params;
|
|
156
|
+
const hashedIdTokenHexes = idTokens.map((idToken) => {
|
|
157
|
+
return (0, utils_1.remove0x)((0, auth_network_utils_1.keccak256AndHexify)((0, utils_1.stringToBytes)(idToken)));
|
|
158
|
+
});
|
|
159
|
+
const authenticationResult = await this.toprfClient.authenticate({
|
|
160
|
+
authConnectionId: groupedAuthConnectionId || authConnectionId,
|
|
161
|
+
userId,
|
|
162
|
+
idTokens: hashedIdTokenHexes,
|
|
163
|
+
groupedAuthConnectionParams: {
|
|
164
|
+
authConnectionId,
|
|
165
|
+
idTokens,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
// update the state with the authenticated user info
|
|
169
|
+
this.update((state) => {
|
|
170
|
+
state.nodeAuthTokens = authenticationResult.nodeAuthTokens;
|
|
171
|
+
state.authConnectionId = authConnectionId;
|
|
172
|
+
state.groupedAuthConnectionId = groupedAuthConnectionId;
|
|
173
|
+
state.userId = userId;
|
|
174
|
+
state.authConnection = authConnection;
|
|
175
|
+
state.socialLoginEmail = socialLoginEmail;
|
|
176
|
+
});
|
|
177
|
+
return authenticationResult;
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
log('Error authenticating user', error);
|
|
181
|
+
throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.AuthenticationError);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
172
184
|
}
|
|
173
185
|
/**
|
|
174
186
|
* Create a new TOPRF encryption key using given password and backups the provided seed phrase.
|
|
@@ -182,27 +194,32 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
|
|
|
182
194
|
// to make sure that fail fast,
|
|
183
195
|
// assert that the user is authenticated before creating the TOPRF key and backing up the seed phrase
|
|
184
196
|
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsAuthenticatedUser).call(this, this.state);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
197
|
+
return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, async () => {
|
|
198
|
+
// locally evaluate the encryption key from the password
|
|
199
|
+
const { encKey, authKeyPair, oprfKey } = this.toprfClient.createLocalKey({
|
|
200
|
+
password,
|
|
201
|
+
});
|
|
202
|
+
// encrypt and store the seed phrase backup
|
|
203
|
+
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_encryptAndStoreSeedPhraseBackup).call(this, {
|
|
204
|
+
keyringId,
|
|
205
|
+
seedPhrase,
|
|
206
|
+
encKey,
|
|
207
|
+
authKeyPair,
|
|
208
|
+
});
|
|
209
|
+
// store/persist the encryption key shares
|
|
210
|
+
// We store the seed phrase metadata in the metadata store first. If this operation fails,
|
|
211
|
+
// we avoid persisting the encryption key shares to prevent a situation where a user appears
|
|
212
|
+
// to have an account but with no associated data.
|
|
213
|
+
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_persistOprfKey).call(this, oprfKey, authKeyPair.pk);
|
|
214
|
+
// create a new vault with the resulting authentication data
|
|
215
|
+
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_createNewVaultWithAuthData).call(this, {
|
|
216
|
+
password,
|
|
217
|
+
rawToprfEncryptionKey: encKey,
|
|
218
|
+
rawToprfAuthKeyPair: authKeyPair,
|
|
219
|
+
});
|
|
220
|
+
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_persistAuthPubKey).call(this, {
|
|
221
|
+
authPubKey: authKeyPair.pk,
|
|
222
|
+
});
|
|
206
223
|
});
|
|
207
224
|
}
|
|
208
225
|
/**
|
|
@@ -213,15 +230,21 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
|
|
|
213
230
|
* @returns A promise that resolves to the success of the operation.
|
|
214
231
|
*/
|
|
215
232
|
async addNewSeedPhraseBackup(seedPhrase, keyringId) {
|
|
216
|
-
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m",
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
233
|
+
return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, async () => {
|
|
234
|
+
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsUnlocked).call(this);
|
|
235
|
+
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertPasswordInSync).call(this, {
|
|
236
|
+
skipCache: true,
|
|
237
|
+
skipLock: true, // skip lock since we already have the lock
|
|
238
|
+
});
|
|
239
|
+
// verify the password and unlock the vault
|
|
240
|
+
const { toprfEncryptionKey, toprfAuthKeyPair } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey).call(this);
|
|
241
|
+
// encrypt and store the seed phrase backup
|
|
242
|
+
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_encryptAndStoreSeedPhraseBackup).call(this, {
|
|
243
|
+
keyringId,
|
|
244
|
+
seedPhrase,
|
|
245
|
+
encKey: toprfEncryptionKey,
|
|
246
|
+
authKeyPair: toprfAuthKeyPair,
|
|
247
|
+
});
|
|
225
248
|
});
|
|
226
249
|
}
|
|
227
250
|
/**
|
|
@@ -235,40 +258,45 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
|
|
|
235
258
|
async fetchAllSeedPhrases(password) {
|
|
236
259
|
// assert that the user is authenticated before fetching the seed phrases
|
|
237
260
|
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsAuthenticatedUser).call(this, this.state);
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (secretData?.length > 0 && password) {
|
|
258
|
-
// if password is provided, we need to create a new vault with the auth data. (supposedly the user is trying to rehydrate the wallet)
|
|
259
|
-
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_createNewVaultWithAuthData).call(this, {
|
|
260
|
-
password,
|
|
261
|
-
rawToprfEncryptionKey: encKey,
|
|
262
|
-
rawToprfAuthKeyPair: authKeyPair,
|
|
261
|
+
return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, async () => {
|
|
262
|
+
let encKey;
|
|
263
|
+
let authKeyPair;
|
|
264
|
+
if (password) {
|
|
265
|
+
const recoverEncKeyResult = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_recoverEncKey).call(this, password);
|
|
266
|
+
encKey = recoverEncKeyResult.encKey;
|
|
267
|
+
authKeyPair = recoverEncKeyResult.authKeyPair;
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsUnlocked).call(this);
|
|
271
|
+
// verify the password and unlock the vault
|
|
272
|
+
const keysFromVault = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey).call(this);
|
|
273
|
+
encKey = keysFromVault.toprfEncryptionKey;
|
|
274
|
+
authKeyPair = keysFromVault.toprfAuthKeyPair;
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
const secretData = await this.toprfClient.fetchAllSecretDataItems({
|
|
278
|
+
decKey: encKey,
|
|
279
|
+
authKeyPair,
|
|
263
280
|
});
|
|
281
|
+
if (secretData?.length > 0 && password) {
|
|
282
|
+
// if password is provided, we need to create a new vault with the auth data. (supposedly the user is trying to rehydrate the wallet)
|
|
283
|
+
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_createNewVaultWithAuthData).call(this, {
|
|
284
|
+
password,
|
|
285
|
+
rawToprfEncryptionKey: encKey,
|
|
286
|
+
rawToprfAuthKeyPair: authKeyPair,
|
|
287
|
+
});
|
|
288
|
+
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_persistAuthPubKey).call(this, {
|
|
289
|
+
authPubKey: authKeyPair.pk,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
const secrets = SecretMetadata_1.SecretMetadata.parseSecretsFromMetadataStore(secretData, constants_1.SecretType.Mnemonic);
|
|
293
|
+
return secrets.map((secret) => secret.data);
|
|
264
294
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.FailedToFetchSeedPhraseMetadata);
|
|
271
|
-
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
log('Error fetching seed phrase metadata', error);
|
|
297
|
+
throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.FailedToFetchSeedPhraseMetadata);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
272
300
|
}
|
|
273
301
|
/**
|
|
274
302
|
* Update the password of the seedless onboarding flow.
|
|
@@ -277,25 +305,38 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
|
|
|
277
305
|
*
|
|
278
306
|
* @param newPassword - The new password to update.
|
|
279
307
|
* @param oldPassword - The old password to verify.
|
|
308
|
+
* @returns A promise that resolves to the success of the operation.
|
|
280
309
|
*/
|
|
281
310
|
async changePassword(newPassword, oldPassword) {
|
|
282
|
-
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m",
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const { encKey: newEncKey, authKeyPair: newAuthKeyPair } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_changeEncryptionKey).call(this, newPassword, oldPassword);
|
|
288
|
-
// update and encrypt the vault with new password
|
|
289
|
-
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_createNewVaultWithAuthData).call(this, {
|
|
290
|
-
password: newPassword,
|
|
291
|
-
rawToprfEncryptionKey: newEncKey,
|
|
292
|
-
rawToprfAuthKeyPair: newAuthKeyPair,
|
|
311
|
+
return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, async () => {
|
|
312
|
+
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsUnlocked).call(this);
|
|
313
|
+
// verify the old password of the encrypted vault
|
|
314
|
+
await this.verifyVaultPassword(oldPassword, {
|
|
315
|
+
skipLock: true, // skip lock since we already have the lock
|
|
293
316
|
});
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
317
|
+
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertPasswordInSync).call(this, {
|
|
318
|
+
skipCache: true,
|
|
319
|
+
skipLock: true, // skip lock since we already have the lock
|
|
320
|
+
});
|
|
321
|
+
try {
|
|
322
|
+
// update the encryption key with new password and update the Metadata Store
|
|
323
|
+
const { encKey: newEncKey, authKeyPair: newAuthKeyPair } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_changeEncryptionKey).call(this, newPassword, oldPassword);
|
|
324
|
+
// update and encrypt the vault with new password
|
|
325
|
+
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_createNewVaultWithAuthData).call(this, {
|
|
326
|
+
password: newPassword,
|
|
327
|
+
rawToprfEncryptionKey: newEncKey,
|
|
328
|
+
rawToprfAuthKeyPair: newAuthKeyPair,
|
|
329
|
+
});
|
|
330
|
+
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_persistAuthPubKey).call(this, {
|
|
331
|
+
authPubKey: newAuthKeyPair.pk,
|
|
332
|
+
});
|
|
333
|
+
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_resetPasswordOutdatedCache).call(this);
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
log('Error changing password', error);
|
|
337
|
+
throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.FailedToChangePassword);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
299
340
|
}
|
|
300
341
|
/**
|
|
301
342
|
* Update the backup metadata state for the given seed phrase.
|
|
@@ -312,13 +353,21 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
|
|
|
312
353
|
* Verify the password validity by decrypting the vault.
|
|
313
354
|
*
|
|
314
355
|
* @param password - The password to verify.
|
|
356
|
+
* @param options - Optional options object.
|
|
357
|
+
* @param options.skipLock - Whether to skip the lock acquisition.
|
|
358
|
+
* @returns A promise that resolves to the success of the operation.
|
|
315
359
|
* @throws {Error} If the password is invalid or the vault is not initialized.
|
|
316
360
|
*/
|
|
317
|
-
async
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
361
|
+
async verifyVaultPassword(password, options) {
|
|
362
|
+
const doVerify = async () => {
|
|
363
|
+
if (!this.state.vault) {
|
|
364
|
+
throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.VaultError);
|
|
365
|
+
}
|
|
366
|
+
await __classPrivateFieldGet(this, _SeedlessOnboardingController_vaultEncryptor, "f").decrypt(password, this.state.vault);
|
|
367
|
+
};
|
|
368
|
+
return options?.skipLock
|
|
369
|
+
? await doVerify()
|
|
370
|
+
: await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, doVerify);
|
|
322
371
|
}
|
|
323
372
|
/**
|
|
324
373
|
* Get the hash of the seed phrase backup for the given seed phrase, from the state.
|
|
@@ -341,10 +390,13 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
|
|
|
341
390
|
* This operation is useful when user performs some actions that requires the user password/encryption key. e.g. add new srp backup
|
|
342
391
|
*
|
|
343
392
|
* @param password - The password to submit.
|
|
393
|
+
* @returns A promise that resolves to the success of the operation.
|
|
344
394
|
*/
|
|
345
395
|
async submitPassword(password) {
|
|
346
|
-
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m",
|
|
347
|
-
|
|
396
|
+
return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, async () => {
|
|
397
|
+
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey).call(this, password);
|
|
398
|
+
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_setUnlocked).call(this);
|
|
399
|
+
});
|
|
348
400
|
}
|
|
349
401
|
/**
|
|
350
402
|
* Set the controller to locked state, and deallocate the secrets (vault encryption key and salt).
|
|
@@ -358,6 +410,98 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
|
|
|
358
410
|
});
|
|
359
411
|
__classPrivateFieldSet(this, _SeedlessOnboardingController_isUnlocked, false, "f");
|
|
360
412
|
}
|
|
413
|
+
/**
|
|
414
|
+
* Sync the latest global password to the controller.
|
|
415
|
+
* reset vault with latest globalPassword,
|
|
416
|
+
* persist the latest global password authPubKey
|
|
417
|
+
*
|
|
418
|
+
* @param params - The parameters for syncing the latest global password.
|
|
419
|
+
* @param params.oldPassword - The old password to verify.
|
|
420
|
+
* @param params.globalPassword - The latest global password.
|
|
421
|
+
* @returns A promise that resolves to the success of the operation.
|
|
422
|
+
*/
|
|
423
|
+
async syncLatestGlobalPassword({ oldPassword, globalPassword, }) {
|
|
424
|
+
return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, async () => {
|
|
425
|
+
// verify correct old password
|
|
426
|
+
await this.verifyVaultPassword(oldPassword, {
|
|
427
|
+
skipLock: true, // skip lock since we already have the lock
|
|
428
|
+
});
|
|
429
|
+
// update vault with latest globalPassword
|
|
430
|
+
const { encKey, authKeyPair } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_recoverEncKey).call(this, globalPassword);
|
|
431
|
+
// update and encrypt the vault with new password
|
|
432
|
+
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_createNewVaultWithAuthData).call(this, {
|
|
433
|
+
password: globalPassword,
|
|
434
|
+
rawToprfEncryptionKey: encKey,
|
|
435
|
+
rawToprfAuthKeyPair: authKeyPair,
|
|
436
|
+
});
|
|
437
|
+
// persist the latest global password authPubKey
|
|
438
|
+
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_persistAuthPubKey).call(this, {
|
|
439
|
+
authPubKey: authKeyPair.pk,
|
|
440
|
+
});
|
|
441
|
+
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_resetPasswordOutdatedCache).call(this);
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* @description Fetch the password corresponding to the current authPubKey in state (current device password which is already out of sync with the current global password).
|
|
446
|
+
* then we use this recovered old password to unlock the vault and set the password to the new global password.
|
|
447
|
+
*
|
|
448
|
+
* @param params - The parameters for fetching the password.
|
|
449
|
+
* @param params.globalPassword - The latest global password.
|
|
450
|
+
* @returns A promise that resolves to the password corresponding to the current authPubKey in state.
|
|
451
|
+
*/
|
|
452
|
+
async recoverCurrentDevicePassword({ globalPassword, }) {
|
|
453
|
+
return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, async () => {
|
|
454
|
+
const currentDeviceAuthPubKey = __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_recoverAuthPubKey).call(this);
|
|
455
|
+
const { password: currentDevicePassword } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_recoverPassword).call(this, {
|
|
456
|
+
targetPwPubKey: currentDeviceAuthPubKey,
|
|
457
|
+
globalPassword,
|
|
458
|
+
});
|
|
459
|
+
return {
|
|
460
|
+
password: currentDevicePassword,
|
|
461
|
+
};
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* @description Check if the current password is outdated compare to the global password.
|
|
466
|
+
*
|
|
467
|
+
* @param options - Optional options object.
|
|
468
|
+
* @param options.skipCache - If true, bypass the cache and force a fresh check.
|
|
469
|
+
* @param options.skipLock - Whether to skip the lock acquisition.
|
|
470
|
+
* @returns A promise that resolves to true if the password is outdated, false otherwise.
|
|
471
|
+
*/
|
|
472
|
+
async checkIsPasswordOutdated(options) {
|
|
473
|
+
// cache result to reduce load on infra
|
|
474
|
+
// Check cache first unless skipCache is true
|
|
475
|
+
if (!options?.skipCache) {
|
|
476
|
+
const { passwordOutdatedCache } = this.state;
|
|
477
|
+
const now = Date.now();
|
|
478
|
+
const isCacheValid = passwordOutdatedCache &&
|
|
479
|
+
now - passwordOutdatedCache.timestamp < constants_1.PASSWORD_OUTDATED_CACHE_TTL_MS;
|
|
480
|
+
if (isCacheValid) {
|
|
481
|
+
return passwordOutdatedCache.isExpiredPwd;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
const doCheck = async () => {
|
|
485
|
+
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsAuthenticatedUser).call(this, this.state);
|
|
486
|
+
const { nodeAuthTokens, authConnectionId, groupedAuthConnectionId, userId, } = this.state;
|
|
487
|
+
const currentDeviceAuthPubKey = __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_recoverAuthPubKey).call(this);
|
|
488
|
+
const { authPubKey: globalAuthPubKey } = await this.toprfClient.fetchAuthPubKey({
|
|
489
|
+
nodeAuthTokens,
|
|
490
|
+
authConnectionId: groupedAuthConnectionId || authConnectionId,
|
|
491
|
+
userId,
|
|
492
|
+
});
|
|
493
|
+
// use noble lib to deserialize and compare curve point
|
|
494
|
+
const isExpiredPwd = !secp256k1_1.secp256k1.ProjectivePoint.fromHex(currentDeviceAuthPubKey).equals(secp256k1_1.secp256k1.ProjectivePoint.fromHex(globalAuthPubKey));
|
|
495
|
+
// Cache the result in state
|
|
496
|
+
this.update((state) => {
|
|
497
|
+
state.passwordOutdatedCache = { isExpiredPwd, timestamp: Date.now() };
|
|
498
|
+
});
|
|
499
|
+
return isExpiredPwd;
|
|
500
|
+
};
|
|
501
|
+
return options?.skipLock
|
|
502
|
+
? await doCheck()
|
|
503
|
+
: await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, doCheck);
|
|
504
|
+
}
|
|
361
505
|
/**
|
|
362
506
|
* Clears the current state of the SeedlessOnboardingController.
|
|
363
507
|
*/
|
|
@@ -369,7 +513,29 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
|
|
|
369
513
|
}
|
|
370
514
|
}
|
|
371
515
|
exports.SeedlessOnboardingController = SeedlessOnboardingController;
|
|
372
|
-
_SeedlessOnboardingController_vaultEncryptor = new WeakMap(), _SeedlessOnboardingController_vaultOperationMutex = new WeakMap(), _SeedlessOnboardingController_isUnlocked = new WeakMap(), _SeedlessOnboardingController_instances = new WeakSet(),
|
|
516
|
+
_SeedlessOnboardingController_vaultEncryptor = new WeakMap(), _SeedlessOnboardingController_controllerOperationMutex = new WeakMap(), _SeedlessOnboardingController_vaultOperationMutex = new WeakMap(), _SeedlessOnboardingController_isUnlocked = new WeakMap(), _SeedlessOnboardingController_instances = new WeakSet(), _SeedlessOnboardingController_recoverPassword =
|
|
517
|
+
/**
|
|
518
|
+
* @description Fetch the password corresponding to the targetPwPubKey.
|
|
519
|
+
*
|
|
520
|
+
* @param params - The parameters for fetching the password.
|
|
521
|
+
* @param params.targetPwPubKey - The target public key of the password to recover.
|
|
522
|
+
* @param params.globalPassword - The latest global password.
|
|
523
|
+
* @returns A promise that resolves to the password corresponding to the current authPubKey in state.
|
|
524
|
+
*/
|
|
525
|
+
async function _SeedlessOnboardingController_recoverPassword({ targetPwPubKey, globalPassword, }) {
|
|
526
|
+
const { encKey: latestPwEncKey, authKeyPair: latestPwAuthKeyPair } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_recoverEncKey).call(this, globalPassword);
|
|
527
|
+
try {
|
|
528
|
+
const res = await this.toprfClient.recoverPassword({
|
|
529
|
+
targetPwPubKey,
|
|
530
|
+
curEncKey: latestPwEncKey,
|
|
531
|
+
curAuthKeyPair: latestPwAuthKeyPair,
|
|
532
|
+
});
|
|
533
|
+
return res;
|
|
534
|
+
}
|
|
535
|
+
catch (error) {
|
|
536
|
+
throw errors_1.PasswordSyncError.getInstance(error);
|
|
537
|
+
}
|
|
538
|
+
}, _SeedlessOnboardingController_setUnlocked = function _SeedlessOnboardingController_setUnlocked() {
|
|
373
539
|
__classPrivateFieldSet(this, _SeedlessOnboardingController_isUnlocked, true, "f");
|
|
374
540
|
}, _SeedlessOnboardingController_persistOprfKey =
|
|
375
541
|
/**
|
|
@@ -395,6 +561,14 @@ async function _SeedlessOnboardingController_persistOprfKey(oprfKey, authPubKey)
|
|
|
395
561
|
log('Error persisting local encryption key', error);
|
|
396
562
|
throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.FailedToPersistOprfKey);
|
|
397
563
|
}
|
|
564
|
+
}, _SeedlessOnboardingController_persistAuthPubKey = function _SeedlessOnboardingController_persistAuthPubKey(params) {
|
|
565
|
+
this.update((state) => {
|
|
566
|
+
state.authPubKey = (0, utils_1.bytesToBase64)(params.authPubKey);
|
|
567
|
+
});
|
|
568
|
+
}, _SeedlessOnboardingController_recoverAuthPubKey = function _SeedlessOnboardingController_recoverAuthPubKey() {
|
|
569
|
+
__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsSRPBackedUpUser).call(this, this.state);
|
|
570
|
+
const { authPubKey } = this.state;
|
|
571
|
+
return (0, utils_1.base64ToBytes)(authPubKey);
|
|
398
572
|
}, _SeedlessOnboardingController_recoverEncKey =
|
|
399
573
|
/**
|
|
400
574
|
* Recover the encryption key from password.
|
|
@@ -631,6 +805,21 @@ async function _SeedlessOnboardingController_updateVault({ password, serializedV
|
|
|
631
805
|
state.vaultEncryptionSalt = JSON.parse(vault).salt;
|
|
632
806
|
});
|
|
633
807
|
});
|
|
808
|
+
}, _SeedlessOnboardingController_withControllerLock =
|
|
809
|
+
/**
|
|
810
|
+
* Lock the controller mutex before executing the given function,
|
|
811
|
+
* and release it after the function is resolved or after an
|
|
812
|
+
* error is thrown.
|
|
813
|
+
*
|
|
814
|
+
* This wrapper ensures that each mutable operation that interacts with the
|
|
815
|
+
* controller and that changes its state is executed in a mutually exclusive way,
|
|
816
|
+
* preventing unsafe concurrent access that could lead to unpredictable behavior.
|
|
817
|
+
*
|
|
818
|
+
* @param callback - The function to execute while the controller mutex is locked.
|
|
819
|
+
* @returns The result of the function.
|
|
820
|
+
*/
|
|
821
|
+
async function _SeedlessOnboardingController_withControllerLock(callback) {
|
|
822
|
+
return await withLock(__classPrivateFieldGet(this, _SeedlessOnboardingController_controllerOperationMutex, "f"), callback);
|
|
634
823
|
}, _SeedlessOnboardingController_withVaultLock =
|
|
635
824
|
/**
|
|
636
825
|
* Lock the vault mutex before executing the given function,
|
|
@@ -698,6 +887,28 @@ async function _SeedlessOnboardingController_withVaultLock(callback) {
|
|
|
698
887
|
) {
|
|
699
888
|
throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.InsufficientAuthToken);
|
|
700
889
|
}
|
|
890
|
+
}, _SeedlessOnboardingController_assertIsSRPBackedUpUser = function _SeedlessOnboardingController_assertIsSRPBackedUpUser(value) {
|
|
891
|
+
if (!this.state.authPubKey) {
|
|
892
|
+
throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.SRPNotBackedUpError);
|
|
893
|
+
}
|
|
894
|
+
}, _SeedlessOnboardingController_assertPasswordInSync =
|
|
895
|
+
/**
|
|
896
|
+
* Assert that the password is in sync with the global password.
|
|
897
|
+
*
|
|
898
|
+
* @param options - The options for asserting the password is in sync.
|
|
899
|
+
* @param options.skipCache - Whether to skip the cache check.
|
|
900
|
+
* @param options.skipLock - Whether to skip the lock acquisition.
|
|
901
|
+
* @throws If the password is outdated.
|
|
902
|
+
*/
|
|
903
|
+
async function _SeedlessOnboardingController_assertPasswordInSync(options) {
|
|
904
|
+
const isPasswordOutdated = await this.checkIsPasswordOutdated(options);
|
|
905
|
+
if (isPasswordOutdated) {
|
|
906
|
+
throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.OutdatedPassword);
|
|
907
|
+
}
|
|
908
|
+
}, _SeedlessOnboardingController_resetPasswordOutdatedCache = function _SeedlessOnboardingController_resetPasswordOutdatedCache() {
|
|
909
|
+
this.update((state) => {
|
|
910
|
+
delete state.passwordOutdatedCache;
|
|
911
|
+
});
|
|
701
912
|
}, _SeedlessOnboardingController_assertIsValidVaultData = function _SeedlessOnboardingController_assertIsValidVaultData(value) {
|
|
702
913
|
// value is not valid vault data if any of the following conditions are true:
|
|
703
914
|
if (!value || // value is not defined
|