@metamask-previews/seedless-onboarding-controller 2.2.0-preview-fd1ef74 → 2.3.0-preview-3713f9f

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 CHANGED
@@ -7,12 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.3.0]
11
+
12
+ ### Uncategorized
13
+
14
+ - Seedless onboarding controller: Remove usage of `Buffer` ([#6140](https://github.com/MetaMask/core/pull/6140))
15
+
16
+ ### Added
17
+
18
+ - Added a optional param `maxKeyChainLength` in `submitGlobalPassword` function.([#6134](https://github.com/MetaMask/core/pull/6134))
19
+ - Separated vault update logic from `revokeRefreshToken`, `revokeRefreshToken` now accepts a revokeToken instead of password. ([#6134](https://github.com/MetaMask/core/pull/6134))
20
+
21
+ ### Changed
22
+
23
+ - `revokeRefreshToken` is removed and a private function named `revokeRefreshTokenAndUpdateState` is added as a replacement.([#6136](https://github.com/MetaMask/core/pull/6136))
24
+
10
25
  ## [2.2.0]
11
26
 
12
27
  ### Fixed
13
28
 
14
- - Bug fix ([#6135](https://github.com/MetaMask/core/pull/6135))
15
- - remove `access_token` validation when the wallet is locked. ([#6133](https://github.com/MetaMask/core/pull/6133))
29
+ - Removed `access_token` validation when the wallet is locked. ([#6133](https://github.com/MetaMask/core/pull/6133))
30
+ - Removed `revoke_token` validation from `#parseVault` and `createNewVaultWithAuthData` to handle the case when max key chain length exceeds. ([#6136](https://github.com/MetaMask/core/pull/6136))
16
31
 
17
32
  ## [2.1.0]
18
33
 
@@ -93,7 +108,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
93
108
  - `checkIsPasswordOutdated`: Check if the password is current device is outdated, i.e. user changed password in another device.
94
109
  - `clearState`: Reset the state of the controller to the defaults.
95
110
 
96
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@2.2.0...HEAD
111
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@2.3.0...HEAD
112
+ [2.3.0]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@2.2.0...@metamask/seedless-onboarding-controller@2.3.0
97
113
  [2.2.0]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@2.1.0...@metamask/seedless-onboarding-controller@2.2.0
98
114
  [2.1.0]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@2.0.1...@metamask/seedless-onboarding-controller@2.1.0
99
115
  [2.0.1]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@2.0.0...@metamask/seedless-onboarding-controller@2.0.1
@@ -10,7 +10,7 @@ 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_controllerOperationMutex, _SeedlessOnboardingController_vaultOperationMutex, _SeedlessOnboardingController_refreshJWTToken, _SeedlessOnboardingController_revokeRefreshToken, _SeedlessOnboardingController_isUnlocked, _SeedlessOnboardingController_submitGlobalPassword, _SeedlessOnboardingController_setUnlocked, _SeedlessOnboardingController_persistOprfKey, _SeedlessOnboardingController_persistAuthPubKey, _SeedlessOnboardingController_storeKeyringEncryptionKey, _SeedlessOnboardingController_loadKeyringEncryptionKey, _SeedlessOnboardingController_loadSeedlessEncryptionKey, _SeedlessOnboardingController_recoverAuthPubKey, _SeedlessOnboardingController_recoverEncKey, _SeedlessOnboardingController_fetchAllSecretDataFromMetadataStore, _SeedlessOnboardingController_changeEncryptionKey, _SeedlessOnboardingController_encryptAndStoreSecretData, _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey, _SeedlessOnboardingController_withPersistedSecretMetadataBackupsState, _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, _SeedlessOnboardingController_isTokenExpiredError, _SeedlessOnboardingController_executeWithTokenRefresh;
13
+ var _SeedlessOnboardingController_instances, _SeedlessOnboardingController_vaultEncryptor, _SeedlessOnboardingController_controllerOperationMutex, _SeedlessOnboardingController_vaultOperationMutex, _SeedlessOnboardingController_refreshJWTToken, _SeedlessOnboardingController_revokeRefreshToken, _SeedlessOnboardingController_isUnlocked, _SeedlessOnboardingController_submitGlobalPassword, _SeedlessOnboardingController_setUnlocked, _SeedlessOnboardingController_persistOprfKey, _SeedlessOnboardingController_persistAuthPubKey, _SeedlessOnboardingController_storeKeyringEncryptionKey, _SeedlessOnboardingController_loadKeyringEncryptionKey, _SeedlessOnboardingController_loadSeedlessEncryptionKey, _SeedlessOnboardingController_recoverAuthPubKey, _SeedlessOnboardingController_recoverEncKey, _SeedlessOnboardingController_fetchAllSecretDataFromMetadataStore, _SeedlessOnboardingController_changeEncryptionKey, _SeedlessOnboardingController_encryptAndStoreSecretData, _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey, _SeedlessOnboardingController_withPersistedSecretMetadataBackupsState, _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_revokeRefreshTokenAndUpdateState, _SeedlessOnboardingController_isTokenExpiredError, _SeedlessOnboardingController_isMaxKeyChainLengthError, _SeedlessOnboardingController_executeWithTokenRefresh;
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");
@@ -22,6 +22,7 @@ const utils_2 = require("@noble/ciphers/utils");
22
22
  const webcrypto_1 = require("@noble/ciphers/webcrypto");
23
23
  const secp256k1_1 = require("@noble/curves/secp256k1");
24
24
  const async_mutex_1 = require("async-mutex");
25
+ const assertions_1 = require("./assertions.cjs");
25
26
  const constants_1 = require("./constants.cjs");
26
27
  const errors_1 = require("./errors.cjs");
27
28
  const logger_1 = require("./logger.cjs");
@@ -484,10 +485,18 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
484
485
  */
485
486
  async submitPassword(password) {
486
487
  return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, async () => {
487
- await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey).call(this, password);
488
+ const { toprfEncryptionKey, toprfPwEncryptionKey, toprfAuthKeyPair, revokeToken, } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey).call(this, password);
488
489
  __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_setUnlocked).call(this);
489
- // revoke and recyle refresh token after unlock to keep refresh token fresh, avoid malicious use of leaked refresh token
490
- await this.revokeRefreshToken(password);
490
+ if (revokeToken) {
491
+ await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_revokeRefreshTokenAndUpdateState).call(this, revokeToken);
492
+ // re-creating vault to persist the new revoke token
493
+ await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_createNewVaultWithAuthData).call(this, {
494
+ password,
495
+ rawToprfEncryptionKey: toprfEncryptionKey,
496
+ rawToprfPwEncryptionKey: toprfPwEncryptionKey,
497
+ rawToprfAuthKeyPair: toprfAuthKeyPair,
498
+ });
499
+ }
491
500
  });
492
501
  }
493
502
  /**
@@ -535,16 +544,18 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
535
544
  * @description Unlock the controller with the latest global password.
536
545
  *
537
546
  * @param params - The parameters for unlocking the controller.
547
+ * @param params.maxKeyChainLength - The maximum chain length of the pwd encryption keys.
538
548
  * @param params.globalPassword - The latest global password.
539
549
  * @returns A promise that resolves to the success of the operation.
540
550
  */
541
- async submitGlobalPassword({ globalPassword, }) {
551
+ async submitGlobalPassword({ globalPassword, maxKeyChainLength = 5, }) {
542
552
  return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, async () => {
543
553
  return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_executeWithTokenRefresh).call(this, async () => {
544
554
  const currentDeviceAuthPubKey = __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_recoverAuthPubKey).call(this);
545
555
  await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_submitGlobalPassword).call(this, {
546
556
  targetAuthPubKey: currentDeviceAuthPubKey,
547
557
  globalPassword,
558
+ maxKeyChainLength,
548
559
  });
549
560
  }, 'submitGlobalPassword');
550
561
  });
@@ -655,34 +666,6 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
655
666
  throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.AuthenticationError);
656
667
  }
657
668
  }
658
- /**
659
- * Revoke the refresh token and get new refresh token and new revoke token.
660
- * This method is to be called after unlock
661
- *
662
- * @param password - The password to re-encrypt new token in the vault.
663
- */
664
- async revokeRefreshToken(password) {
665
- __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsUnlocked).call(this);
666
- __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsAuthenticatedUser).call(this, this.state);
667
- // get revoke token and backup encryption key from vault (should be unlocked already)
668
- const { revokeToken, toprfEncryptionKey, toprfPwEncryptionKey, toprfAuthKeyPair, } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey).call(this);
669
- const { newRevokeToken, newRefreshToken } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_revokeRefreshToken, "f").call(this, {
670
- connection: this.state.authConnection,
671
- revokeToken,
672
- });
673
- this.update((state) => {
674
- // set new revoke token in state temporarily for persisting in vault
675
- state.revokeToken = newRevokeToken;
676
- // set new refresh token to persist in state
677
- state.refreshToken = newRefreshToken;
678
- });
679
- await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_createNewVaultWithAuthData).call(this, {
680
- password,
681
- rawToprfEncryptionKey: toprfEncryptionKey,
682
- rawToprfPwEncryptionKey: toprfPwEncryptionKey,
683
- rawToprfAuthKeyPair: toprfAuthKeyPair,
684
- });
685
- }
686
669
  /**
687
670
  * Check if the current node auth token is expired.
688
671
  *
@@ -743,13 +726,14 @@ _SeedlessOnboardingController_vaultEncryptor = new WeakMap(), _SeedlessOnboardin
743
726
  * password validity and unlock the controller.
744
727
  *
745
728
  * @param params - The parameters for submitting the global password.
729
+ * @param params.maxKeyChainLength - The maximum chain length of the pwd encryption keys.
746
730
  * @param params.targetAuthPubKey - The target public key of the keyring
747
731
  * encryption key to recover.
748
732
  * @param params.globalPassword - The latest global password.
749
733
  * @returns A promise that resolves to the keyring encryption key
750
734
  * corresponding to the current authPubKey in state.
751
735
  */
752
- async function _SeedlessOnboardingController_submitGlobalPassword({ targetAuthPubKey, globalPassword, }) {
736
+ async function _SeedlessOnboardingController_submitGlobalPassword({ targetAuthPubKey, globalPassword, maxKeyChainLength, }) {
753
737
  const { pwEncKey: curPwEncKey, authKeyPair: curAuthKeyPair } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_recoverEncKey).call(this, globalPassword);
754
738
  try {
755
739
  // Recover vault encryption key.
@@ -757,19 +741,32 @@ async function _SeedlessOnboardingController_submitGlobalPassword({ targetAuthPu
757
741
  targetAuthPubKey,
758
742
  curPwEncKey,
759
743
  curAuthKeyPair,
744
+ maxPwChainLength: maxKeyChainLength,
760
745
  });
761
746
  const { pwEncKey } = res;
762
747
  const vaultKey = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_loadSeedlessEncryptionKey).call(this, pwEncKey);
763
748
  // Unlock the controller
764
- await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey).call(this, undefined, vaultKey);
749
+ const { revokeToken, toprfEncryptionKey, toprfPwEncryptionKey, toprfAuthKeyPair, } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey).call(this, undefined, vaultKey);
765
750
  __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_setUnlocked).call(this);
766
- // revoke and recyle refresh token after unlock to keep refresh token fresh, avoid malicious use of leaked refresh token
767
- await this.revokeRefreshToken(globalPassword);
751
+ if (revokeToken) {
752
+ // revoke and recyle refresh token after unlock to keep refresh token fresh, avoid malicious use of leaked refresh token
753
+ await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_revokeRefreshTokenAndUpdateState).call(this, revokeToken);
754
+ // re-creating vault to persist the new revoke token
755
+ await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_createNewVaultWithAuthData).call(this, {
756
+ password: globalPassword,
757
+ rawToprfEncryptionKey: toprfEncryptionKey,
758
+ rawToprfPwEncryptionKey: toprfPwEncryptionKey,
759
+ rawToprfAuthKeyPair: toprfAuthKeyPair,
760
+ });
761
+ }
768
762
  }
769
763
  catch (error) {
770
764
  if (__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_isTokenExpiredError).call(this, error)) {
771
765
  throw error;
772
766
  }
767
+ if (__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_isMaxKeyChainLengthError).call(this, error)) {
768
+ throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.MaxKeyChainLengthExceeded);
769
+ }
773
770
  throw errors_1.PasswordSyncError.getInstance(error);
774
771
  }
775
772
  }, _SeedlessOnboardingController_setUnlocked = function _SeedlessOnboardingController_setUnlocked() {
@@ -1116,9 +1113,6 @@ async function _SeedlessOnboardingController_withPersistedSecretMetadataBackupsS
1116
1113
  */
1117
1114
  async function _SeedlessOnboardingController_createNewVaultWithAuthData({ password, rawToprfEncryptionKey, rawToprfPwEncryptionKey, rawToprfAuthKeyPair, }) {
1118
1115
  __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsAuthenticatedUser).call(this, this.state);
1119
- if (!this.state.revokeToken) {
1120
- throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.InvalidRevokeToken);
1121
- }
1122
1116
  if (!this.state.accessToken) {
1123
1117
  throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.InvalidAccessToken);
1124
1118
  }
@@ -1219,7 +1213,7 @@ async function _SeedlessOnboardingController_withVaultLock(callback) {
1219
1213
  catch {
1220
1214
  throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.InvalidVaultData);
1221
1215
  }
1222
- __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsValidVaultData).call(this, parsedVaultData);
1216
+ (0, assertions_1.assertIsValidVaultData)(parsedVaultData);
1223
1217
  const rawToprfEncryptionKey = (0, utils_1.base64ToBytes)(parsedVaultData.toprfEncryptionKey);
1224
1218
  const rawToprfPwEncryptionKey = (0, utils_1.base64ToBytes)(parsedVaultData.toprfPwEncryptionKey);
1225
1219
  const parsedToprfAuthKeyPair = JSON.parse(parsedVaultData.toprfAuthKeyPair);
@@ -1283,29 +1277,38 @@ async function _SeedlessOnboardingController_assertPasswordInSync(options) {
1283
1277
  this.update((state) => {
1284
1278
  delete state.passwordOutdatedCache;
1285
1279
  });
1286
- }, _SeedlessOnboardingController_assertIsValidVaultData = function _SeedlessOnboardingController_assertIsValidVaultData(value) {
1287
- // value is not valid vault data if any of the following conditions are true:
1288
- if (!value || // value is not defined
1289
- typeof value !== 'object' || // value is not an object
1290
- !('toprfEncryptionKey' in value) || // toprfEncryptionKey is not defined
1291
- typeof value.toprfEncryptionKey !== 'string' || // toprfEncryptionKey is not a string
1292
- !('toprfPwEncryptionKey' in value) || // toprfPwEncryptionKey is not defined
1293
- typeof value.toprfPwEncryptionKey !== 'string' || // toprfPwEncryptionKey is not a string
1294
- !('toprfAuthKeyPair' in value) || // toprfAuthKeyPair is not defined
1295
- typeof value.toprfAuthKeyPair !== 'string' || // toprfAuthKeyPair is not a string
1296
- !('revokeToken' in value) || // revokeToken is not defined
1297
- typeof value.revokeToken !== 'string' || // revokeToken is not a string
1298
- !('accessToken' in value) || // accessToken is not defined
1299
- typeof value.accessToken !== 'string' // accessToken is not a string
1300
- ) {
1301
- throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.VaultDataError);
1302
- }
1280
+ }, _SeedlessOnboardingController_revokeRefreshTokenAndUpdateState =
1281
+ /**
1282
+ * Revoke the refresh token and get new refresh token and new revoke token.
1283
+ * This method is to be called after user is authenticated.
1284
+ *
1285
+ * @param revokeToken - The revoke token to use for revoking the refresh token.
1286
+ */
1287
+ async function _SeedlessOnboardingController_revokeRefreshTokenAndUpdateState(revokeToken) {
1288
+ __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsAuthenticatedUser).call(this, this.state);
1289
+ const { newRevokeToken, newRefreshToken } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_revokeRefreshToken, "f").call(this, {
1290
+ connection: this.state.authConnection,
1291
+ revokeToken,
1292
+ });
1293
+ this.update((state) => {
1294
+ // set new revoke token in state temporarily for persisting in vault
1295
+ state.revokeToken = newRevokeToken;
1296
+ // set new refresh token to persist in state
1297
+ state.refreshToken = newRefreshToken;
1298
+ });
1303
1299
  }, _SeedlessOnboardingController_isTokenExpiredError = function _SeedlessOnboardingController_isTokenExpiredError(error) {
1304
1300
  if (error instanceof toprf_secure_backup_1.TOPRFError) {
1305
1301
  // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison
1306
1302
  return error.code === toprf_secure_backup_1.TOPRFErrorCode.AuthTokenExpired;
1307
1303
  }
1308
1304
  return false;
1305
+ }, _SeedlessOnboardingController_isMaxKeyChainLengthError = function _SeedlessOnboardingController_isMaxKeyChainLengthError(error) {
1306
+ if (error instanceof toprf_secure_backup_1.TOPRFError) {
1307
+ // todo: update this when the error message to error code once toprf sdk is updated.
1308
+ return (error.message ===
1309
+ 'Could not fetch password. Exceeded maximum password chain length');
1310
+ }
1311
+ return false;
1309
1312
  }, _SeedlessOnboardingController_executeWithTokenRefresh =
1310
1313
  /**
1311
1314
  * Executes an operation with automatic token refresh on expiration.