@metamask-previews/seedless-onboarding-controller 7.1.0-preview-69f51f81 → 7.1.0-preview-40468f94
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 +6 -0
- package/dist/SeedlessOnboardingController.cjs +62 -47
- package/dist/SeedlessOnboardingController.cjs.map +1 -1
- package/dist/SeedlessOnboardingController.d.cts.map +1 -1
- package/dist/SeedlessOnboardingController.d.mts.map +1 -1
- package/dist/SeedlessOnboardingController.mjs +62 -47
- package/dist/SeedlessOnboardingController.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
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.
|
|
12
14
|
- Added new public method, `getAccessToken`. ([#7800](https://github.com/MetaMask/core/pull/7800))
|
|
13
15
|
- Clients can use this method to get `accessToken` from the controller, instead of directly accessing from the state.
|
|
14
16
|
- This method also adds refresh token mechanism when `accessToken` is expired, hence preventing expired token usage in the clients.
|
|
@@ -20,6 +22,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
20
22
|
- Bump `@metamask/keyring-controller` from `^25.0.0` to `^25.1.0` ([#7713](https://github.com/MetaMask/core/pull/7713))
|
|
21
23
|
- Upgrade `@metamask/utils` from `^11.8.1` to `^11.9.0` ([#7511](https://github.com/MetaMask/core/pull/7511))
|
|
22
24
|
|
|
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
|
+
|
|
23
29
|
## [7.1.0]
|
|
24
30
|
|
|
25
31
|
### Added
|
|
@@ -975,22 +975,39 @@ _SeedlessOnboardingController_vaultEncryptor = new WeakMap(), _SeedlessOnboardin
|
|
|
975
975
|
* corresponding to the current authPubKey in state.
|
|
976
976
|
*/
|
|
977
977
|
async function _SeedlessOnboardingController_submitGlobalPassword({ targetAuthPubKey, globalPassword, maxKeyChainLength, }) {
|
|
978
|
-
const { pwEncKey:
|
|
978
|
+
const { pwEncKey: globalPwEncKey, authKeyPair: globalAuthKeyPair } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_recoverEncKey).call(this, globalPassword);
|
|
979
979
|
try {
|
|
980
980
|
// Recover vault encryption key.
|
|
981
981
|
const res = await this.toprfClient.recoverPwEncKey({
|
|
982
982
|
targetAuthPubKey,
|
|
983
|
-
curPwEncKey,
|
|
984
|
-
curAuthKeyPair,
|
|
983
|
+
curPwEncKey: globalPwEncKey,
|
|
984
|
+
curAuthKeyPair: globalAuthKeyPair,
|
|
985
985
|
maxPwChainLength: maxKeyChainLength,
|
|
986
986
|
});
|
|
987
987
|
const { pwEncKey } = res;
|
|
988
988
|
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;
|
|
989
992
|
// Unlock the controller
|
|
990
|
-
await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetVaultData).call(this, {
|
|
993
|
+
const decryptedVaultData = await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_unlockVaultAndGetVaultData).call(this, {
|
|
991
994
|
encryptionKey: vaultKey,
|
|
992
995
|
});
|
|
993
996
|
__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);
|
|
994
1011
|
}
|
|
995
1012
|
catch (error) {
|
|
996
1013
|
if (__classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_isAuthTokenError).call(this, error)) {
|
|
@@ -1420,24 +1437,14 @@ async function _SeedlessOnboardingController_updateVault({ password, vaultData,
|
|
|
1420
1437
|
// from the password using an intentionally slow key derivation function.
|
|
1421
1438
|
// We should make sure that we only call it very intentionally.
|
|
1422
1439
|
const { vault, exportedKeyString } = await __classPrivateFieldGet(this, _SeedlessOnboardingController_vaultEncryptor, "f").encryptWithDetail(password, serializedVaultData);
|
|
1423
|
-
const
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
vaultEncryptionSalt: JSON.parse(vault).salt,
|
|
1427
|
-
};
|
|
1428
|
-
// Encrypt vault key.
|
|
1429
|
-
if (pwEncKey) {
|
|
1430
|
-
const aes = (0, webcrypto_1.managedNonce)(aes_1.gcm)(pwEncKey);
|
|
1431
|
-
const encryptedKey = aes.encrypt((0, utils_2.utf8ToBytes)(exportedKeyString));
|
|
1432
|
-
updatedState.encryptedSeedlessEncryptionKey =
|
|
1433
|
-
(0, utils_1.bytesToBase64)(encryptedKey);
|
|
1434
|
-
}
|
|
1440
|
+
const aes = (0, webcrypto_1.managedNonce)(aes_1.gcm)(pwEncKey);
|
|
1441
|
+
const encryptedKey = aes.encrypt((0, utils_2.utf8ToBytes)(exportedKeyString));
|
|
1442
|
+
const encryptedSeedlessEncryptionKey = (0, utils_1.bytesToBase64)(encryptedKey);
|
|
1435
1443
|
this.update((state) => {
|
|
1436
|
-
state.vault =
|
|
1437
|
-
state.vaultEncryptionKey =
|
|
1438
|
-
state.vaultEncryptionSalt =
|
|
1439
|
-
state.encryptedSeedlessEncryptionKey =
|
|
1440
|
-
updatedState.encryptedSeedlessEncryptionKey;
|
|
1444
|
+
state.vault = vault;
|
|
1445
|
+
state.vaultEncryptionKey = exportedKeyString;
|
|
1446
|
+
state.vaultEncryptionSalt = JSON.parse(vault).salt;
|
|
1447
|
+
state.encryptedSeedlessEncryptionKey = encryptedSeedlessEncryptionKey;
|
|
1441
1448
|
});
|
|
1442
1449
|
});
|
|
1443
1450
|
}, _SeedlessOnboardingController_getAccessTokenAndRevokeToken =
|
|
@@ -1623,35 +1630,43 @@ async function _SeedlessOnboardingController_executeWithTokenRefresh(operation,
|
|
|
1623
1630
|
}
|
|
1624
1631
|
}
|
|
1625
1632
|
}, _SeedlessOnboardingController_updateVaultAfterAuthTokenRefresh = async function _SeedlessOnboardingController_updateVaultAfterAuthTokenRefresh(accessToken) {
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
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;
|
|
1629
1659
|
this.update((state) => {
|
|
1660
|
+
state.vault = JSON.stringify(updatedEncVault);
|
|
1661
|
+
state.vaultEncryptionSalt = vaultEncryptionSalt;
|
|
1630
1662
|
state.accessToken = accessToken;
|
|
1663
|
+
state.vaultEncryptionKey = vaultEncryptionKey;
|
|
1631
1664
|
});
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
!__classPrivateFieldGet(this, _SeedlessOnboardingController_cachedDecryptedVaultData, "f")) {
|
|
1638
|
-
throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.MissingCredentials);
|
|
1639
|
-
}
|
|
1640
|
-
// update the cached decrypted vault data with the new access token
|
|
1641
|
-
__classPrivateFieldGet(this, _SeedlessOnboardingController_cachedDecryptedVaultData, "f").accessToken = accessToken;
|
|
1642
|
-
const serializedVaultData = (0, utils_3.serializeVaultData)(__classPrivateFieldGet(this, _SeedlessOnboardingController_cachedDecryptedVaultData, "f"));
|
|
1643
|
-
const encryptionKey = await __classPrivateFieldGet(this, _SeedlessOnboardingController_vaultEncryptor, "f").importKey(vaultEncryptionKey);
|
|
1644
|
-
const updatedEncVault = await __classPrivateFieldGet(this, _SeedlessOnboardingController_vaultEncryptor, "f").encryptWithKey(encryptionKey, serializedVaultData);
|
|
1645
|
-
// NOTE: Referenced from keyring-controller!
|
|
1646
|
-
// We need to include the salt used to derive
|
|
1647
|
-
// the encryption key, to be able to derive it
|
|
1648
|
-
// from password again.
|
|
1649
|
-
updatedEncVault.salt = vaultEncryptionSalt;
|
|
1650
|
-
this.update((state) => {
|
|
1651
|
-
state.vault = JSON.stringify(updatedEncVault);
|
|
1652
|
-
state.vaultEncryptionSalt = vaultEncryptionSalt;
|
|
1653
|
-
state.accessToken = accessToken;
|
|
1654
|
-
state.vaultEncryptionKey = vaultEncryptionKey;
|
|
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");
|
|
1655
1670
|
});
|
|
1656
1671
|
}, _SeedlessOnboardingController_checkTokensExpired = function _SeedlessOnboardingController_checkTokensExpired() {
|
|
1657
1672
|
// proactively check for expired tokens and refresh them if needed
|