@metamask-previews/seedless-onboarding-controller 1.0.0-preview-4e3fd4c0 → 1.0.0-preview-3636a128

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.
@@ -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_recoverKeyringEncryptionKey, _SeedlessOnboardingController_setUnlocked, _SeedlessOnboardingController_persistOprfKey, _SeedlessOnboardingController_persistAuthPubKey, _SeedlessOnboardingController_storeKeyringEncryptionKey, _SeedlessOnboardingController_loadKeyringEncryptionKey, _SeedlessOnboardingController_recoverAuthPubKey, _SeedlessOnboardingController_recoverEncKey, _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_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;
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");
@@ -102,6 +102,10 @@ const seedlessOnboardingMetadata = {
102
102
  persist: false,
103
103
  anonymous: true,
104
104
  },
105
+ encryptedSeedlessEncryptionKey: {
106
+ persist: true,
107
+ anonymous: true,
108
+ },
105
109
  encryptedKeyringEncryptionKey: {
106
110
  persist: true,
107
111
  anonymous: true,
@@ -517,18 +521,15 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
517
521
  * @param params.globalPassword - The latest global password.
518
522
  * @returns A promise that resolves to the password corresponding to the current authPubKey in state.
519
523
  */
520
- async recoverKeyringEncryptionKey({ globalPassword, }) {
524
+ async submitGlobalPassword({ globalPassword, }) {
521
525
  return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withControllerLock).call(this, async () => {
522
526
  return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_executeWithTokenRefresh).call(this, async () => {
523
527
  const currentDeviceAuthPubKey = __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_recoverAuthPubKey).call(this);
524
- const { keyringEncryptionKey } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_recoverKeyringEncryptionKey).call(this, {
528
+ await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_submitGlobalPassword).call(this, {
525
529
  targetAuthPubKey: currentDeviceAuthPubKey,
526
530
  globalPassword,
527
531
  });
528
- return {
529
- keyringEncryptionKey,
530
- };
531
- }, 'recoverKeyringEncryptionKey');
532
+ }, 'submitGlobalPassword');
532
533
  });
533
534
  }
534
535
  /**
@@ -688,7 +689,7 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
688
689
  }
689
690
  }
690
691
  exports.SeedlessOnboardingController = SeedlessOnboardingController;
691
- _SeedlessOnboardingController_vaultEncryptor = new WeakMap(), _SeedlessOnboardingController_controllerOperationMutex = new WeakMap(), _SeedlessOnboardingController_vaultOperationMutex = new WeakMap(), _SeedlessOnboardingController_refreshJWTToken = new WeakMap(), _SeedlessOnboardingController_revokeRefreshToken = new WeakMap(), _SeedlessOnboardingController_isUnlocked = new WeakMap(), _SeedlessOnboardingController_instances = new WeakSet(), _SeedlessOnboardingController_recoverKeyringEncryptionKey =
692
+ _SeedlessOnboardingController_vaultEncryptor = new WeakMap(), _SeedlessOnboardingController_controllerOperationMutex = new WeakMap(), _SeedlessOnboardingController_vaultOperationMutex = new WeakMap(), _SeedlessOnboardingController_refreshJWTToken = new WeakMap(), _SeedlessOnboardingController_revokeRefreshToken = new WeakMap(), _SeedlessOnboardingController_isUnlocked = new WeakMap(), _SeedlessOnboardingController_instances = new WeakSet(), _SeedlessOnboardingController_submitGlobalPassword =
692
693
  /**
693
694
  * @description Fetch the keyring encryption key corresponding to the targetAuthPubKey.
694
695
  *
@@ -697,9 +698,10 @@ _SeedlessOnboardingController_vaultEncryptor = new WeakMap(), _SeedlessOnboardin
697
698
  * @param params.globalPassword - The latest global password.
698
699
  * @returns A promise that resolves to the keyring encryption key corresponding to the current authPubKey in state.
699
700
  */
700
- async function _SeedlessOnboardingController_recoverKeyringEncryptionKey({ targetAuthPubKey, globalPassword, }) {
701
+ async function _SeedlessOnboardingController_submitGlobalPassword({ targetAuthPubKey, globalPassword, }) {
701
702
  const { encKey: latestPwEncKey, authKeyPair: latestPwAuthKeyPair } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_recoverEncKey).call(this, globalPassword);
702
703
  try {
704
+ // Recover vault encryption key.
703
705
  const res = await this.toprfClient.recoverPassword({
704
706
  targetPwPubKey: targetAuthPubKey,
705
707
  curEncKey: latestPwEncKey,
@@ -707,8 +709,12 @@ async function _SeedlessOnboardingController_recoverKeyringEncryptionKey({ targe
707
709
  });
708
710
  const { password: recoveredEncryptionKeyBase64 } = res;
709
711
  const recoveredEncryptionKey = (0, utils_1.base64ToBytes)(recoveredEncryptionKeyBase64);
710
- const keyringEncryptionKey = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_loadKeyringEncryptionKey).call(this, recoveredEncryptionKey);
711
- return { keyringEncryptionKey };
712
+ const vaultKey = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_loadSeedlessEncryptionKey).call(this, recoveredEncryptionKey);
713
+ // Unlock the controller
714
+ await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey).call(this, undefined, vaultKey);
715
+ __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_setUnlocked).call(this);
716
+ // revoke and recyle refresh token after unlock to keep refresh token fresh, avoid malicious use of leaked refresh token
717
+ await this.revokeRefreshToken(globalPassword);
712
718
  }
713
719
  catch (error) {
714
720
  if (__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_isTokenExpiredError).call(this, error)) {
@@ -777,6 +783,20 @@ async function _SeedlessOnboardingController_loadKeyringEncryptionKey(encKey) {
777
783
  const aes = (0, webcrypto_1.managedNonce)(aes_1.gcm)(encKey);
778
784
  const password = aes.decrypt(encryptedPasswordBytes);
779
785
  return (0, utils_2.bytesToUtf8)(password);
786
+ }, _SeedlessOnboardingController_loadSeedlessEncryptionKey =
787
+ /**
788
+ * Decrypt the seedless encryption key from state.
789
+ *
790
+ * @param encKey - The encryption key.
791
+ * @returns The seedless encryption key.
792
+ */
793
+ async function _SeedlessOnboardingController_loadSeedlessEncryptionKey(encKey) {
794
+ const { encryptedSeedlessEncryptionKey: encryptedKey } = this.state;
795
+ assertIsEncryptedSeedlessEncryptionKeySet(encryptedKey);
796
+ const encryptedPasswordBytes = (0, utils_1.base64ToBytes)(encryptedKey);
797
+ const aes = (0, webcrypto_1.managedNonce)(aes_1.gcm)(encKey);
798
+ const password = aes.decrypt(encryptedPasswordBytes);
799
+ return (0, utils_2.bytesToUtf8)(password);
780
800
  }, _SeedlessOnboardingController_recoverAuthPubKey = function _SeedlessOnboardingController_recoverAuthPubKey() {
781
801
  __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsSRPBackedUpUser).call(this, this.state);
782
802
  const { authPubKey } = this.state;
@@ -892,6 +912,7 @@ async function _SeedlessOnboardingController_encryptAndStoreSecretData(params) {
892
912
  * This method ensures thread-safety by using a mutex lock when accessing the vault.
893
913
  *
894
914
  * @param password - The optional password to unlock the vault.
915
+ * @param encryptionKey - The optional encryption key to unlock the vault.
895
916
  * @returns A promise that resolves to an object containing:
896
917
  * - nodeAuthTokens: Authentication tokens to communicate with the TOPRF service
897
918
  * - toprfEncryptionKey: The decrypted TOPRF encryption key
@@ -902,14 +923,15 @@ async function _SeedlessOnboardingController_encryptAndStoreSecretData(params) {
902
923
  * - The password is incorrect (from encryptor.decrypt)
903
924
  * - The decrypted vault data is malformed
904
925
  */
905
- async function _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey(password) {
926
+ async function _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey(password, encryptionKey) {
906
927
  return __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withVaultLock).call(this, async () => {
907
- const { vault: encryptedVault, vaultEncryptionKey, vaultEncryptionSalt, } = this.state;
928
+ let { vaultEncryptionKey } = this.state;
929
+ const { vault: encryptedVault, vaultEncryptionSalt } = this.state;
908
930
  if (!encryptedVault) {
909
931
  throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.VaultError);
910
932
  }
911
- if (!vaultEncryptionKey && !password) {
912
- throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.MissingCredentials);
933
+ if (encryptionKey) {
934
+ vaultEncryptionKey = encryptionKey;
913
935
  }
914
936
  let decryptedVaultData;
915
937
  const updatedState = {};
@@ -924,13 +946,12 @@ async function _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey(passw
924
946
  updatedState.vaultEncryptionSalt = result.salt;
925
947
  }
926
948
  else {
949
+ assertIsVaultEncryptionKeyDefined(vaultEncryptionKey);
927
950
  const parsedEncryptedVault = JSON.parse(encryptedVault);
928
- if (vaultEncryptionSalt !== parsedEncryptedVault.salt) {
951
+ if (vaultEncryptionSalt &&
952
+ vaultEncryptionSalt !== parsedEncryptedVault.salt) {
929
953
  throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.ExpiredCredentials);
930
954
  }
931
- if (typeof vaultEncryptionKey !== 'string') {
932
- throw new TypeError(constants_1.SeedlessOnboardingControllerErrorMessage.WrongPasswordType);
933
- }
934
955
  const key = await __classPrivateFieldGet(this, _SeedlessOnboardingController_vaultEncryptor, "f").importKey(vaultEncryptionKey);
935
956
  decryptedVaultData = await __classPrivateFieldGet(this, _SeedlessOnboardingController_vaultEncryptor, "f").decryptWithKey(key, parsedEncryptedVault);
936
957
  updatedState.vaultEncryptionKey = vaultEncryptionKey;
@@ -1034,6 +1055,7 @@ async function _SeedlessOnboardingController_createNewVaultWithAuthData({ passwo
1034
1055
  await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_updateVault).call(this, {
1035
1056
  password,
1036
1057
  serializedVaultData,
1058
+ encKey: rawToprfEncryptionKey,
1037
1059
  });
1038
1060
  }, _SeedlessOnboardingController_updateVault =
1039
1061
  /**
@@ -1042,19 +1064,24 @@ async function _SeedlessOnboardingController_createNewVaultWithAuthData({ passwo
1042
1064
  * @param params - The parameters for updating the vault.
1043
1065
  * @param params.password - The password to encrypt the vault.
1044
1066
  * @param params.serializedVaultData - The serialized authentication data to update the vault with.
1067
+ * @param params.encKey - The global backup encryption key.
1045
1068
  * @returns A promise that resolves to the updated vault.
1046
1069
  */
1047
- async function _SeedlessOnboardingController_updateVault({ password, serializedVaultData, }) {
1070
+ async function _SeedlessOnboardingController_updateVault({ password, serializedVaultData, encKey, }) {
1048
1071
  await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_withVaultLock).call(this, async () => {
1049
1072
  assertIsValidPassword(password);
1050
1073
  // Note that vault encryption using the password is a very costly operation as it involves deriving the encryption key
1051
1074
  // from the password using an intentionally slow key derivation function.
1052
1075
  // We should make sure that we only call it very intentionally.
1053
1076
  const { vault, exportedKeyString } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_vaultEncryptor, "f").encryptWithDetail(password, serializedVaultData);
1077
+ // Encrypt vault key.
1078
+ const aes = (0, webcrypto_1.managedNonce)(aes_1.gcm)(encKey);
1079
+ const encryptedKey = aes.encrypt((0, utils_2.utf8ToBytes)(exportedKeyString));
1054
1080
  this.update((state) => {
1055
1081
  state.vault = vault;
1056
1082
  state.vaultEncryptionKey = exportedKeyString;
1057
1083
  state.vaultEncryptionSalt = JSON.parse(vault).salt;
1084
+ state.encryptedSeedlessEncryptionKey = (0, utils_1.bytesToBase64)(encryptedKey);
1058
1085
  });
1059
1086
  });
1060
1087
  }, _SeedlessOnboardingController_withControllerLock =
@@ -1273,4 +1300,26 @@ function assertIsEncryptedKeyringEncryptionKeySet(encryptedKeyringEncryptionKey)
1273
1300
  throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.EncryptedKeyringEncryptionKeyNotSet);
1274
1301
  }
1275
1302
  }
1303
+ /**
1304
+ * Assert that the provided encrypted seedless encryption key is a valid non-empty string.
1305
+ *
1306
+ * @param encryptedSeedlessEncryptionKey - The encrypted seedless encryption key to check.
1307
+ * @throws If the encrypted seedless encryption key is not a valid string.
1308
+ */
1309
+ function assertIsEncryptedSeedlessEncryptionKeySet(encryptedSeedlessEncryptionKey) {
1310
+ if (!encryptedSeedlessEncryptionKey) {
1311
+ throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.EncryptedSeedlessEncryptionKeyNotSet);
1312
+ }
1313
+ }
1314
+ /**
1315
+ * Assert that the provided vault encryption key is a valid non-empty string.
1316
+ *
1317
+ * @param vaultEncryptionKey - The vault encryption key to check.
1318
+ * @throws If the vault encryption key is not a valid string.
1319
+ */
1320
+ function assertIsVaultEncryptionKeyDefined(vaultEncryptionKey) {
1321
+ if (!vaultEncryptionKey) {
1322
+ throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.VaultEncryptionKeyUndefined);
1323
+ }
1324
+ }
1276
1325
  //# sourceMappingURL=SeedlessOnboardingController.cjs.map