@metamask-previews/seedless-onboarding-controller 2.0.1-preview-32f9004a → 2.1.0-preview-522a9fc

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,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.1.0]
11
+
12
+ ### Added
13
+
14
+ - Added `access_token` and `metadata_access_token` in seedless controller state. ([#6060](https://github.com/MetaMask/core/pull/6060))
15
+ - `access_token` can be used by profile sync pairing and for other apis access after wallet is unlocked.
16
+ - `metadata_access_token` is used to give access for web3auth metadata apis.
17
+
10
18
  ## [2.0.1]
11
19
 
12
20
  ### Fixed
@@ -78,7 +86,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
78
86
  - `checkIsPasswordOutdated`: Check if the password is current device is outdated, i.e. user changed password in another device.
79
87
  - `clearState`: Reset the state of the controller to the defaults.
80
88
 
81
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@2.0.1...HEAD
89
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@2.1.0...HEAD
90
+ [2.1.0]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@2.0.1...@metamask/seedless-onboarding-controller@2.1.0
82
91
  [2.0.1]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@2.0.0...@metamask/seedless-onboarding-controller@2.0.1
83
92
  [2.0.0]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@1.0.0...@metamask/seedless-onboarding-controller@2.0.0
84
93
  [1.0.0]: https://github.com/MetaMask/core/releases/tag/@metamask/seedless-onboarding-controller@1.0.0
@@ -26,6 +26,7 @@ const constants_1 = require("./constants.cjs");
26
26
  const errors_1 = require("./errors.cjs");
27
27
  const logger_1 = require("./logger.cjs");
28
28
  const SecretMetadata_1 = require("./SecretMetadata.cjs");
29
+ const utils_3 = require("./utils.cjs");
29
30
  const log = (0, logger_1.createModuleLogger)(logger_1.projectLogger, constants_1.controllerName);
30
31
  /**
31
32
  * Get the default state for the Seedless Onboarding Controller.
@@ -102,6 +103,17 @@ const seedlessOnboardingMetadata = {
102
103
  persist: false,
103
104
  anonymous: true,
104
105
  },
106
+ // stays in vault
107
+ accessToken: {
108
+ persist: false,
109
+ anonymous: true,
110
+ },
111
+ // stays outside of vault as this token is accessed by the metadata service
112
+ // before the vault is created or unlocked.
113
+ metadataAccessToken: {
114
+ persist: true,
115
+ anonymous: true,
116
+ },
105
117
  encryptedSeedlessEncryptionKey: {
106
118
  persist: true,
107
119
  anonymous: true,
@@ -150,6 +162,7 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
150
162
  this.toprfClient = new toprf_secure_backup_1.ToprfSecureBackup({
151
163
  network,
152
164
  keyDeriver: toprfKeyDeriver,
165
+ fetchMetadataAccessCreds: this.fetchMetadataAccessCreds.bind(this),
153
166
  });
154
167
  __classPrivateFieldSet(this, _SeedlessOnboardingController_refreshJWTToken, refreshJWTToken, "f");
155
168
  __classPrivateFieldSet(this, _SeedlessOnboardingController_revokeRefreshToken, revokeRefreshToken, "f");
@@ -162,6 +175,24 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
162
175
  __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_setUnlocked).call(this);
163
176
  });
164
177
  }
178
+ async fetchMetadataAccessCreds() {
179
+ const { metadataAccessToken } = this.state;
180
+ if (!metadataAccessToken) {
181
+ throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.InvalidMetadataAccessToken);
182
+ }
183
+ // Check if token is expired and refresh if needed
184
+ const decodedToken = (0, utils_3.decodeJWTToken)(metadataAccessToken);
185
+ if (decodedToken.exp < Math.floor(Date.now() / 1000)) {
186
+ // Token is expired, refresh it
187
+ await this.refreshAuthTokens();
188
+ // Get the new token after refresh
189
+ const { metadataAccessToken: newMetadataAccessToken } = this.state;
190
+ return {
191
+ metadataAccessToken: newMetadataAccessToken,
192
+ };
193
+ }
194
+ return { metadataAccessToken };
195
+ }
165
196
  /**
166
197
  * Authenticate OAuth user using the seedless onboarding flow
167
198
  * and determine if the user is already registered or not.
@@ -175,13 +206,15 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
175
206
  * @param params.socialLoginEmail - The user email from Social login.
176
207
  * @param params.refreshToken - refresh token for refreshing expired nodeAuthTokens.
177
208
  * @param params.revokeToken - revoke token for revoking refresh token and get new refresh token and new revoke token.
209
+ * @param params.accessToken - Access token for pairing with profile sync auth service and to access other services.
210
+ * @param params.metadataAccessToken - Metadata access token for accessing the metadata service before the vault is created or unlocked.
178
211
  * @param params.skipLock - Optional flag to skip acquiring the controller lock. (to prevent deadlock in case the caller already acquired the lock)
179
212
  * @returns A promise that resolves to the authentication result.
180
213
  */
181
214
  async authenticate(params) {
182
215
  const doAuthenticateWithNodes = async () => {
183
216
  try {
184
- const { idTokens, authConnectionId, groupedAuthConnectionId, userId, authConnection, socialLoginEmail, refreshToken, revokeToken, } = params;
217
+ const { idTokens, authConnectionId, groupedAuthConnectionId, userId, authConnection, socialLoginEmail, refreshToken, revokeToken, accessToken, metadataAccessToken, } = params;
185
218
  const authenticationResult = await this.toprfClient.authenticate({
186
219
  authConnectionId,
187
220
  userId,
@@ -203,6 +236,12 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
203
236
  // Temporarily store revoke token in state for later vault creation
204
237
  state.revokeToken = revokeToken;
205
238
  }
239
+ if (accessToken) {
240
+ state.accessToken = accessToken;
241
+ }
242
+ if (metadataAccessToken) {
243
+ state.metadataAccessToken = metadataAccessToken;
244
+ }
206
245
  });
207
246
  return authenticationResult;
208
247
  }
@@ -461,6 +500,7 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
461
500
  delete state.vaultEncryptionKey;
462
501
  delete state.vaultEncryptionSalt;
463
502
  delete state.revokeToken;
503
+ delete state.accessToken;
464
504
  });
465
505
  __classPrivateFieldSet(this, _SeedlessOnboardingController_isUnlocked, false, "f");
466
506
  }
@@ -582,14 +622,14 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
582
622
  return await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_loadKeyringEncryptionKey).call(this, encKey);
583
623
  }
584
624
  /**
585
- * Refresh expired nodeAuthTokens using the stored refresh token.
625
+ * Refresh expired nodeAuthTokens, accessToken, and metadataAccessToken using the stored refresh token.
586
626
  *
587
627
  * This method retrieves the refresh token from the vault and uses it to obtain
588
628
  * new nodeAuthTokens when the current ones have expired.
589
629
  *
590
630
  * @returns A promise that resolves to the new nodeAuthTokens.
591
631
  */
592
- async refreshNodeAuthTokens() {
632
+ async refreshAuthTokens() {
593
633
  __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsAuthenticatedUser).call(this, this.state);
594
634
  const { refreshToken } = this.state;
595
635
  try {
@@ -597,10 +637,12 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
597
637
  connection: this.state.authConnection,
598
638
  refreshToken,
599
639
  });
600
- const { idTokens } = res;
640
+ const { idTokens, accessToken, metadataAccessToken } = res;
601
641
  // re-authenticate with the new id tokens to set new node auth tokens
602
642
  await this.authenticate({
603
643
  idTokens,
644
+ accessToken,
645
+ metadataAccessToken,
604
646
  authConnection: this.state.authConnection,
605
647
  authConnectionId: this.state.authConnectionId,
606
648
  groupedAuthConnectionId: this.state.groupedAuthConnectionId,
@@ -652,18 +694,35 @@ class SeedlessOnboardingController extends base_controller_1.BaseController {
652
694
  // all auth tokens should be expired at the same time so we can check the first one
653
695
  const firstAuthToken = nodeAuthTokens[0]?.authToken;
654
696
  // node auth token is base64 encoded json object
655
- const decodedToken = this.decodeNodeAuthToken(firstAuthToken);
697
+ const decodedToken = (0, utils_3.decodeNodeAuthToken)(firstAuthToken);
656
698
  // check if the token is expired
657
699
  return decodedToken.exp < Date.now() / 1000;
658
700
  }
659
- /**
660
- * Decode the node auth token from base64 to json object.
661
- *
662
- * @param token - The node auth token to decode.
663
- * @returns The decoded node auth token.
664
- */
665
- decodeNodeAuthToken(token) {
666
- return JSON.parse((0, utils_2.bytesToUtf8)((0, utils_1.base64ToBytes)(token)));
701
+ checkMetadataAccessTokenExpired() {
702
+ try {
703
+ __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsAuthenticatedUser).call(this, this.state);
704
+ const { metadataAccessToken } = this.state;
705
+ // assertIsAuthenticatedUser will throw if metadataAccessToken is missing
706
+ const decodedToken = (0, utils_3.decodeJWTToken)(metadataAccessToken);
707
+ return decodedToken.exp < Math.floor(Date.now() / 1000);
708
+ }
709
+ catch {
710
+ return true; // Consider unauthenticated user as having expired tokens
711
+ }
712
+ }
713
+ checkAccessTokenExpired() {
714
+ try {
715
+ __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_assertIsAuthenticatedUser).call(this, this.state);
716
+ const { accessToken } = this.state;
717
+ if (!accessToken) {
718
+ return true; // Consider missing token as expired
719
+ }
720
+ const decodedToken = (0, utils_3.decodeJWTToken)(accessToken);
721
+ return decodedToken.exp < Math.floor(Date.now() / 1000);
722
+ }
723
+ catch {
724
+ return true; // Consider unauthenticated user as having expired tokens
725
+ }
667
726
  }
668
727
  }
669
728
  exports.SeedlessOnboardingController = SeedlessOnboardingController;
@@ -919,7 +978,6 @@ async function _SeedlessOnboardingController_encryptAndStoreSecretData(params) {
919
978
  * @param password - The optional password to unlock the vault.
920
979
  * @param encryptionKey - The optional encryption key to unlock the vault.
921
980
  * @returns A promise that resolves to an object containing:
922
- * - nodeAuthTokens: Authentication tokens to communicate with the TOPRF service
923
981
  * - toprfEncryptionKey: The decrypted TOPRF encryption key
924
982
  * - toprfAuthKeyPair: The decrypted TOPRF authentication key pair
925
983
  * @throws {Error} If:
@@ -962,20 +1020,19 @@ async function _SeedlessOnboardingController_unlockVaultAndGetBackupEncKey(passw
962
1020
  updatedState.vaultEncryptionKey = vaultEncryptionKey;
963
1021
  updatedState.vaultEncryptionSalt = vaultEncryptionSalt;
964
1022
  }
965
- const { nodeAuthTokens, toprfEncryptionKey, toprfPwEncryptionKey, toprfAuthKeyPair, revokeToken, } = __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_parseVaultData).call(this, decryptedVaultData);
966
- // update the state with the restored nodeAuthTokens
1023
+ const { toprfEncryptionKey, toprfPwEncryptionKey, toprfAuthKeyPair, revokeToken, accessToken, } = __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_parseVaultData).call(this, decryptedVaultData);
967
1024
  this.update((state) => {
968
- state.nodeAuthTokens = nodeAuthTokens;
969
1025
  state.vaultEncryptionKey = updatedState.vaultEncryptionKey;
970
1026
  state.vaultEncryptionSalt = updatedState.vaultEncryptionSalt;
971
1027
  state.revokeToken = revokeToken;
1028
+ state.accessToken = accessToken;
972
1029
  });
973
1030
  return {
974
- nodeAuthTokens,
975
1031
  toprfEncryptionKey,
976
1032
  toprfPwEncryptionKey,
977
1033
  toprfAuthKeyPair,
978
1034
  revokeToken,
1035
+ accessToken,
979
1036
  };
980
1037
  });
981
1038
  }, _SeedlessOnboardingController_withPersistedSecretMetadataBackupsState =
@@ -1051,14 +1108,17 @@ async function _SeedlessOnboardingController_createNewVaultWithAuthData({ passwo
1051
1108
  if (!this.state.revokeToken) {
1052
1109
  throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.InvalidRevokeToken);
1053
1110
  }
1111
+ if (!this.state.accessToken) {
1112
+ throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.InvalidAccessToken);
1113
+ }
1054
1114
  __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_setUnlocked).call(this);
1055
1115
  const { toprfEncryptionKey, toprfPwEncryptionKey, toprfAuthKeyPair } = __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_serializeKeyData).call(this, rawToprfEncryptionKey, rawToprfPwEncryptionKey, rawToprfAuthKeyPair);
1056
1116
  const serializedVaultData = JSON.stringify({
1057
- authTokens: this.state.nodeAuthTokens,
1058
1117
  toprfEncryptionKey,
1059
1118
  toprfPwEncryptionKey,
1060
1119
  toprfAuthKeyPair,
1061
1120
  revokeToken: this.state.revokeToken,
1121
+ accessToken: this.state.accessToken,
1062
1122
  });
1063
1123
  await __classPrivateFieldGet(this, _SeedlessOnboardingController_instances, "m", _SeedlessOnboardingController_updateVault).call(this, {
1064
1124
  password,
@@ -1157,11 +1217,11 @@ async function _SeedlessOnboardingController_withVaultLock(callback) {
1157
1217
  pk: (0, utils_1.base64ToBytes)(parsedToprfAuthKeyPair.pk),
1158
1218
  };
1159
1219
  return {
1160
- nodeAuthTokens: parsedVaultData.authTokens,
1161
1220
  toprfEncryptionKey: rawToprfEncryptionKey,
1162
1221
  toprfPwEncryptionKey: rawToprfPwEncryptionKey,
1163
1222
  toprfAuthKeyPair: rawToprfAuthKeyPair,
1164
1223
  revokeToken: parsedVaultData.revokeToken,
1224
+ accessToken: parsedVaultData.accessToken,
1165
1225
  };
1166
1226
  }, _SeedlessOnboardingController_assertIsUnlocked = function _SeedlessOnboardingController_assertIsUnlocked() {
1167
1227
  if (!__classPrivateFieldGet(this, _SeedlessOnboardingController_isUnlocked, "f")) {
@@ -1186,6 +1246,10 @@ async function _SeedlessOnboardingController_withVaultLock(callback) {
1186
1246
  if (!('refreshToken' in value) || typeof value.refreshToken !== 'string') {
1187
1247
  throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.InvalidRefreshToken);
1188
1248
  }
1249
+ if (!('metadataAccessToken' in value) ||
1250
+ typeof value.metadataAccessToken !== 'string') {
1251
+ throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.InvalidMetadataAccessToken);
1252
+ }
1189
1253
  }, _SeedlessOnboardingController_assertIsSRPBackedUpUser = function _SeedlessOnboardingController_assertIsSRPBackedUpUser(value) {
1190
1254
  if (!this.state.authPubKey) {
1191
1255
  throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.SRPNotBackedUpError);
@@ -1212,8 +1276,6 @@ async function _SeedlessOnboardingController_assertPasswordInSync(options) {
1212
1276
  // value is not valid vault data if any of the following conditions are true:
1213
1277
  if (!value || // value is not defined
1214
1278
  typeof value !== 'object' || // value is not an object
1215
- !('authTokens' in value) || // authTokens is not defined
1216
- typeof value.authTokens !== 'object' || // authTokens is not an object
1217
1279
  !('toprfEncryptionKey' in value) || // toprfEncryptionKey is not defined
1218
1280
  typeof value.toprfEncryptionKey !== 'string' || // toprfEncryptionKey is not a string
1219
1281
  !('toprfPwEncryptionKey' in value) || // toprfPwEncryptionKey is not defined
@@ -1221,7 +1283,9 @@ async function _SeedlessOnboardingController_assertPasswordInSync(options) {
1221
1283
  !('toprfAuthKeyPair' in value) || // toprfAuthKeyPair is not defined
1222
1284
  typeof value.toprfAuthKeyPair !== 'string' || // toprfAuthKeyPair is not a string
1223
1285
  !('revokeToken' in value) || // revokeToken is not defined
1224
- typeof value.revokeToken !== 'string' // revokeToken is not a string
1286
+ typeof value.revokeToken !== 'string' || // revokeToken is not a string
1287
+ !('accessToken' in value) || // accessToken is not defined
1288
+ typeof value.accessToken !== 'string' // accessToken is not a string
1225
1289
  ) {
1226
1290
  throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.VaultDataError);
1227
1291
  }
@@ -1248,9 +1312,13 @@ async function _SeedlessOnboardingController_executeWithTokenRefresh(operation,
1248
1312
  try {
1249
1313
  // proactively check for expired tokens and refresh them if needed
1250
1314
  const isNodeAuthTokenExpired = this.checkNodeAuthTokenExpired();
1251
- if (isNodeAuthTokenExpired) {
1315
+ const isMetadataAccessTokenExpired = this.checkMetadataAccessTokenExpired();
1316
+ const isAccessTokenExpired = this.checkAccessTokenExpired();
1317
+ if (isNodeAuthTokenExpired ||
1318
+ isMetadataAccessTokenExpired ||
1319
+ isAccessTokenExpired) {
1252
1320
  log(`JWT token expired during ${operationName}, attempting to refresh tokens`, 'node auth token exp check');
1253
- await this.refreshNodeAuthTokens();
1321
+ await this.refreshAuthTokens();
1254
1322
  }
1255
1323
  return await operation();
1256
1324
  }
@@ -1260,7 +1328,7 @@ async function _SeedlessOnboardingController_executeWithTokenRefresh(operation,
1260
1328
  log(`Token expired during ${operationName}, attempting to refresh tokens`, error);
1261
1329
  try {
1262
1330
  // Refresh the tokens
1263
- await this.refreshNodeAuthTokens();
1331
+ await this.refreshAuthTokens();
1264
1332
  // Retry the operation with fresh tokens
1265
1333
  return await operation();
1266
1334
  }