@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.
- package/dist/SeedlessOnboardingController.cjs +69 -20
- package/dist/SeedlessOnboardingController.cjs.map +1 -1
- package/dist/SeedlessOnboardingController.d.cts +2 -4
- package/dist/SeedlessOnboardingController.d.cts.map +1 -1
- package/dist/SeedlessOnboardingController.d.mts +2 -4
- package/dist/SeedlessOnboardingController.d.mts.map +1 -1
- package/dist/SeedlessOnboardingController.mjs +69 -20
- package/dist/SeedlessOnboardingController.mjs.map +1 -1
- package/dist/constants.cjs +2 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +3 -1
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.mts +3 -1
- package/dist/constants.d.mts.map +1 -1
- package/dist/constants.mjs +2 -0
- package/dist/constants.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +4 -0
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +4 -0
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +1 -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,
|
|
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
|
|
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
|
-
|
|
528
|
+
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_submitGlobalPassword).call(this, {
|
|
525
529
|
targetAuthPubKey: currentDeviceAuthPubKey,
|
|
526
530
|
globalPassword,
|
|
527
531
|
});
|
|
528
|
-
|
|
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(),
|
|
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
|
|
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
|
|
711
|
-
|
|
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
|
-
|
|
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 (
|
|
912
|
-
|
|
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
|
|
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
|