@metamask-previews/seedless-onboarding-controller 7.1.0-preview-40468f94 → 7.1.0-preview-fb7aa07ff

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,14 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ### Added
11
-
12
- - **BREAKING** The `encryptor` constructor param requires `encryptWithKey` method. ([#7800](https://github.com/MetaMask/core/pull/7800))
13
- - The method is to encrypt the vault with cached encryption key while the wallet is unlocked.
14
- - Added new public method, `getAccessToken`. ([#7800](https://github.com/MetaMask/core/pull/7800))
15
- - Clients can use this method to get `accessToken` from the controller, instead of directly accessing from the state.
16
- - This method also adds refresh token mechanism when `accessToken` is expired, hence preventing expired token usage in the clients.
17
-
18
10
  ### Changed
19
11
 
20
12
  - Update StateMetadata's `includeInStateLogs` property. ([#7750](https://github.com/MetaMask/core/pull/7750))
@@ -22,10 +14,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
22
14
  - Bump `@metamask/keyring-controller` from `^25.0.0` to `^25.1.0` ([#7713](https://github.com/MetaMask/core/pull/7713))
23
15
  - Upgrade `@metamask/utils` from `^11.8.1` to `^11.9.0` ([#7511](https://github.com/MetaMask/core/pull/7511))
24
16
 
25
- ### Fixed
26
-
27
- - Fixed new `accessToken` not being persisted in the vault after the token refresh. ([#7800](https://github.com/MetaMask/core/pull/7800))
28
-
29
17
  ## [7.1.0]
30
18
 
31
19
  ### Added
@@ -10,12 +10,11 @@ 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_renewRefreshToken, _SeedlessOnboardingController_passwordOutdatedCacheTTL, _SeedlessOnboardingController_isUnlocked, _SeedlessOnboardingController_cachedDecryptedVaultData, _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_unlockVaultAndGetVaultData, _SeedlessOnboardingController_decryptAndParseVaultData, _SeedlessOnboardingController_withPersistedSecretMetadataBackupsState, _SeedlessOnboardingController_filterDupesAndUpdateSocialBackupsMetadata, _SeedlessOnboardingController_createNewVaultWithAuthData, _SeedlessOnboardingController_updateVault, _SeedlessOnboardingController_getAccessTokenAndRevokeToken, _SeedlessOnboardingController_withControllerLock, _SeedlessOnboardingController_withVaultLock, _SeedlessOnboardingController_parseVaultData, _SeedlessOnboardingController_assertIsUnlocked, _SeedlessOnboardingController_assertIsAuthenticatedUser, _SeedlessOnboardingController_assertPasswordInSync, _SeedlessOnboardingController_resetPasswordOutdatedCache, _SeedlessOnboardingController_addRefreshTokenToRevokeList, _SeedlessOnboardingController_isAuthTokenError, _SeedlessOnboardingController_isMaxKeyChainLengthError, _SeedlessOnboardingController_executeWithTokenRefresh, _SeedlessOnboardingController_updateVaultAfterAuthTokenRefresh, _SeedlessOnboardingController_checkTokensExpired;
13
+ var _SeedlessOnboardingController_instances, _SeedlessOnboardingController_vaultEncryptor, _SeedlessOnboardingController_controllerOperationMutex, _SeedlessOnboardingController_vaultOperationMutex, _SeedlessOnboardingController_refreshJWTToken, _SeedlessOnboardingController_revokeRefreshToken, _SeedlessOnboardingController_renewRefreshToken, _SeedlessOnboardingController_passwordOutdatedCacheTTL, _SeedlessOnboardingController_isUnlocked, _SeedlessOnboardingController_cachedDecryptedVaultData, _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_unlockVaultAndGetVaultData, _SeedlessOnboardingController_decryptAndParseVaultData, _SeedlessOnboardingController_withPersistedSecretMetadataBackupsState, _SeedlessOnboardingController_filterDupesAndUpdateSocialBackupsMetadata, _SeedlessOnboardingController_createNewVaultWithAuthData, _SeedlessOnboardingController_updateVault, _SeedlessOnboardingController_getAccessTokenAndRevokeToken, _SeedlessOnboardingController_withControllerLock, _SeedlessOnboardingController_withVaultLock, _SeedlessOnboardingController_parseVaultData, _SeedlessOnboardingController_assertIsUnlocked, _SeedlessOnboardingController_assertIsAuthenticatedUser, _SeedlessOnboardingController_assertPasswordInSync, _SeedlessOnboardingController_resetPasswordOutdatedCache, _SeedlessOnboardingController_addRefreshTokenToRevokeList, _SeedlessOnboardingController_isAuthTokenError, _SeedlessOnboardingController_isMaxKeyChainLengthError, _SeedlessOnboardingController_executeWithTokenRefresh;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.SeedlessOnboardingController = exports.getInitialSeedlessOnboardingControllerStateWithDefaults = 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
- const messenger_1 = require("@metamask/messenger");
19
18
  const toprf_secure_backup_1 = require("@metamask/toprf-secure-backup");
20
19
  const utils_1 = require("@metamask/utils");
21
20
  const aes_1 = require("@noble/ciphers/aes");
@@ -243,7 +242,6 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
243
242
  __classPrivateFieldSet(this, _SeedlessOnboardingController_refreshJWTToken, refreshJWTToken, "f");
244
243
  __classPrivateFieldSet(this, _SeedlessOnboardingController_revokeRefreshToken, revokeRefreshToken, "f");
245
244
  __classPrivateFieldSet(this, _SeedlessOnboardingController_renewRefreshToken, renewRefreshToken, "f");
246
- this.messenger.registerActionHandler('SeedlessOnboardingController:getAccessToken', this.getAccessToken.bind(this));
247
245
  }
248
246
  async fetchMetadataAccessCreds() {
249
247
  const { metadataAccessToken } = this.state;
@@ -554,7 +552,7 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
554
552
  /**
555
553
  * Submit the password to the controller, verify the password validity and unlock the controller.
556
554
  *
557
- * This method will be used especially when user unlock the wallet.
555
+ * This method will be used especially when user rehydrate/unlock the wallet.
558
556
  * The provided password will be verified against the encrypted vault, encryption key will be derived and saved in the controller state.
559
557
  *
560
558
  * This operation is useful when user performs some actions that requires the user password/encryption key. e.g. add new srp backup
@@ -564,37 +562,7 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
564
562
  */
565
563
  async submitPassword(password) {
566
564
  return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, async () => {
567
- // get the access token from the state before unlocking, it might be the new token set from the `refreshAuthTokens` method.
568
- const { accessToken: accessTokenBeforeUnlock } = this.state;
569
- const deserializedVaultData = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetVaultData).call(this, {
570
- password,
571
- });
572
- const { accessToken: accessTokenAfterUnlock } = this.state;
573
- let latestAccessToken = accessTokenAfterUnlock;
574
- // compare the access token from state before unlocking and after unlocking, take the latest access token if it's different.
575
- if (accessTokenBeforeUnlock &&
576
- accessTokenAfterUnlock &&
577
- accessTokenBeforeUnlock !== accessTokenAfterUnlock) {
578
- // If there was an access token in the state before unlocking, it might be the token set from the `refreshAuthTokens` method.
579
- // Compare it with the access token value from decrypted vault after unlocking and take the latest access token.
580
- latestAccessToken = (0, utils_3.compareAndGetLatestToken)(accessTokenBeforeUnlock, accessTokenAfterUnlock);
581
- }
582
- // update the state and vault with the latest access token if it's different from the current access token in the state.
583
- if (latestAccessToken && latestAccessToken !== accessTokenAfterUnlock) {
584
- // update the access token in the state with the latest access token if it's different from the decrypted access token after unlocking
585
- this.update((state) => {
586
- state.accessToken = latestAccessToken;
587
- });
588
- const updatedVaultData = {
589
- ...deserializedVaultData,
590
- accessToken: latestAccessToken,
591
- };
592
- await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_updateVault).call(this, {
593
- password,
594
- vaultData: updatedVaultData,
595
- pwEncKey: deserializedVaultData.toprfPwEncryptionKey,
596
- });
597
- }
565
+ await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetVaultData).call(this, { password });
598
566
  __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_setUnlocked).call(this);
599
567
  });
600
568
  }
@@ -800,8 +768,6 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
800
768
  refreshToken,
801
769
  skipLock: true,
802
770
  });
803
- // update the vault with new access token
804
- await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_updateVaultAfterAuthTokenRefresh).call(this, accessToken);
805
771
  }
806
772
  catch (error) {
807
773
  log('Error refreshing node auth tokens', error);
@@ -892,21 +858,6 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
892
858
  }
893
859
  });
894
860
  }
895
- /**
896
- * Get the access token from the state.
897
- *
898
- * If the tokens are expired, the method will refresh them and return the new access token.
899
- *
900
- * @returns The access token.
901
- */
902
- async getAccessToken() {
903
- return __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, async () => {
904
- __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsAuthenticatedUser).call(this, this.state);
905
- return __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_executeWithTokenRefresh).call(this, async () => {
906
- return this.state.accessToken;
907
- }, 'getAccessToken');
908
- });
909
- }
910
861
  /**
911
862
  * Check if the current node auth token is expired.
912
863
  *
@@ -975,39 +926,22 @@ _SeedlessOnboardingController_vaultEncryptor = new WeakMap(), _SeedlessOnboardin
975
926
  * corresponding to the current authPubKey in state.
976
927
  */
977
928
  async function _SeedlessOnboardingController_submitGlobalPassword({ targetAuthPubKey, globalPassword, maxKeyChainLength, }) {
978
- const { pwEncKey: globalPwEncKey, authKeyPair: globalAuthKeyPair } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_recoverEncKey).call(this, globalPassword);
929
+ const { pwEncKey: curPwEncKey, authKeyPair: curAuthKeyPair } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_recoverEncKey).call(this, globalPassword);
979
930
  try {
980
931
  // Recover vault encryption key.
981
932
  const res = await this.toprfClient.recoverPwEncKey({
982
933
  targetAuthPubKey,
983
- curPwEncKey: globalPwEncKey,
984
- curAuthKeyPair: globalAuthKeyPair,
934
+ curPwEncKey,
935
+ curAuthKeyPair,
985
936
  maxPwChainLength: maxKeyChainLength,
986
937
  });
987
938
  const { pwEncKey } = res;
988
939
  const vaultKey = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_loadSeedlessEncryptionKey).call(this, pwEncKey);
989
- // accessToken before unlocking vault and flooding the state with values from the decrypted vault
990
- // it might be the new token set from the `refreshAuthTokens` method.
991
- const { accessToken: accessTokenBeforeUnlock } = this.state;
992
940
  // Unlock the controller
993
- const decryptedVaultData = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetVaultData).call(this, {
941
+ await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetVaultData).call(this, {
994
942
  encryptionKey: vaultKey,
995
943
  });
996
944
  __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_setUnlocked).call(this);
997
- // accessToken from decrypted vault
998
- const accessTokenFromDecryptedVault = decryptedVaultData.accessToken;
999
- // compare the two access tokens, take the latest access token if it's different.
1000
- if (accessTokenBeforeUnlock &&
1001
- accessTokenFromDecryptedVault &&
1002
- accessTokenBeforeUnlock !== accessTokenFromDecryptedVault) {
1003
- const latestAccessToken = (0, utils_3.compareAndGetLatestToken)(accessTokenBeforeUnlock, accessTokenFromDecryptedVault);
1004
- // update the access token in the state with the latest access token if it's different from the decrypted access token after unlocking
1005
- // later when we call `syncLatestGlobalPassword`, the encrypted vault will be updated with the latest access token.
1006
- this.update((state) => {
1007
- state.accessToken = latestAccessToken;
1008
- });
1009
- }
1010
- __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_resetPasswordOutdatedCache).call(this);
1011
945
  }
1012
946
  catch (error) {
1013
947
  if (__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_isAuthTokenError).call(this, error)) {
@@ -1437,14 +1371,14 @@ async function _SeedlessOnboardingController_updateVault({ password, vaultData,
1437
1371
  // from the password using an intentionally slow key derivation function.
1438
1372
  // We should make sure that we only call it very intentionally.
1439
1373
  const { vault, exportedKeyString } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_vaultEncryptor, "f").encryptWithDetail(password, serializedVaultData);
1374
+ // Encrypt vault key.
1440
1375
  const aes = (0, webcrypto_1.managedNonce)(aes_1.gcm)(pwEncKey);
1441
1376
  const encryptedKey = aes.encrypt((0, utils_2.utf8ToBytes)(exportedKeyString));
1442
- const encryptedSeedlessEncryptionKey = (0, utils_1.bytesToBase64)(encryptedKey);
1443
1377
  this.update((state) => {
1444
1378
  state.vault = vault;
1445
1379
  state.vaultEncryptionKey = exportedKeyString;
1446
1380
  state.vaultEncryptionSalt = JSON.parse(vault).salt;
1447
- state.encryptedSeedlessEncryptionKey = encryptedSeedlessEncryptionKey;
1381
+ state.encryptedSeedlessEncryptionKey = (0, utils_1.bytesToBase64)(encryptedKey);
1448
1382
  });
1449
1383
  });
1450
1384
  }, _SeedlessOnboardingController_getAccessTokenAndRevokeToken =
@@ -1603,7 +1537,18 @@ async function _SeedlessOnboardingController_assertPasswordInSync(options) {
1603
1537
  */
1604
1538
  async function _SeedlessOnboardingController_executeWithTokenRefresh(operation, operationName) {
1605
1539
  try {
1606
- if (__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_checkTokensExpired).call(this)) {
1540
+ // proactively check for expired tokens and refresh them if needed
1541
+ const isNodeAuthTokenExpired = this.checkNodeAuthTokenExpired();
1542
+ const isMetadataAccessTokenExpired = this.checkMetadataAccessTokenExpired();
1543
+ // access token is only accessible when the vault is unlocked
1544
+ // so skip the check if the vault is locked
1545
+ let isAccessTokenExpired = false;
1546
+ if (__classPrivateFieldGet(this, _SeedlessOnboardingController_isUnlocked, "f")) {
1547
+ isAccessTokenExpired = this.checkAccessTokenExpired();
1548
+ }
1549
+ if (isNodeAuthTokenExpired ||
1550
+ isMetadataAccessTokenExpired ||
1551
+ isAccessTokenExpired) {
1607
1552
  log(`JWT token expired during ${operationName}, attempting to refresh tokens`, 'node auth token exp check');
1608
1553
  await this.refreshAuthTokens();
1609
1554
  }
@@ -1629,58 +1574,6 @@ async function _SeedlessOnboardingController_executeWithTokenRefresh(operation,
1629
1574
  throw error;
1630
1575
  }
1631
1576
  }
1632
- }, _SeedlessOnboardingController_updateVaultAfterAuthTokenRefresh = async function _SeedlessOnboardingController_updateVaultAfterAuthTokenRefresh(accessToken) {
1633
- await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withVaultLock).call(this, async () => {
1634
- if (!__classPrivateFieldGet(this, _SeedlessOnboardingController_isUnlocked, "f")) {
1635
- // we just temporarily store the access token in the state
1636
- // when user attempts to unlock the vault, we will use this access token to update the vault
1637
- this.update((state) => {
1638
- state.accessToken = accessToken;
1639
- });
1640
- return;
1641
- }
1642
- const { vaultEncryptionKey, vaultEncryptionSalt } = this.state;
1643
- if (!vaultEncryptionKey ||
1644
- !vaultEncryptionSalt ||
1645
- !__classPrivateFieldGet(this, _SeedlessOnboardingController_cachedDecryptedVaultData, "f")) {
1646
- throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.MissingCredentials);
1647
- }
1648
- const serializedVaultData = (0, utils_3.serializeVaultData)({
1649
- ...__classPrivateFieldGet(this, _SeedlessOnboardingController_cachedDecryptedVaultData, "f"),
1650
- accessToken,
1651
- });
1652
- const encryptionKey = await __classPrivateFieldGet(this, _SeedlessOnboardingController_vaultEncryptor, "f").importKey(vaultEncryptionKey);
1653
- const updatedEncVault = await __classPrivateFieldGet(this, _SeedlessOnboardingController_vaultEncryptor, "f").encryptWithKey(encryptionKey, serializedVaultData);
1654
- // NOTE: Referenced from keyring-controller!
1655
- // We need to include the salt used to derive
1656
- // the encryption key, to be able to derive it
1657
- // from password again.
1658
- updatedEncVault.salt = vaultEncryptionSalt;
1659
- this.update((state) => {
1660
- state.vault = JSON.stringify(updatedEncVault);
1661
- state.vaultEncryptionSalt = vaultEncryptionSalt;
1662
- state.accessToken = accessToken;
1663
- state.vaultEncryptionKey = vaultEncryptionKey;
1664
- });
1665
- // update the cached decrypted vault data with the new access token
1666
- __classPrivateFieldSet(this, _SeedlessOnboardingController_cachedDecryptedVaultData, {
1667
- ...__classPrivateFieldGet(this, _SeedlessOnboardingController_cachedDecryptedVaultData, "f"),
1668
- accessToken,
1669
- }, "f");
1670
- });
1671
- }, _SeedlessOnboardingController_checkTokensExpired = function _SeedlessOnboardingController_checkTokensExpired() {
1672
- // proactively check for expired tokens and refresh them if needed
1673
- const isNodeAuthTokenExpired = this.checkNodeAuthTokenExpired();
1674
- const isMetadataAccessTokenExpired = this.checkMetadataAccessTokenExpired();
1675
- // access token is only accessible when the vault is unlocked
1676
- // so skip the check if the vault is locked
1677
- let isAccessTokenExpired = false;
1678
- if (__classPrivateFieldGet(this, _SeedlessOnboardingController_isUnlocked, "f")) {
1679
- isAccessTokenExpired = this.checkAccessTokenExpired();
1680
- }
1681
- return (isNodeAuthTokenExpired ||
1682
- isMetadataAccessTokenExpired ||
1683
- isAccessTokenExpired);
1684
1577
  };
1685
1578
  /**
1686
1579
  * Assert that the provided password is a valid non-empty string.