@salesforce/core 4.0.0-v3.0 → 4.0.1

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.
Files changed (160) hide show
  1. package/LICENSE.txt +1 -1
  2. package/README.md +93 -44
  3. package/lib/config/aliasesConfig.d.ts +12 -0
  4. package/lib/config/aliasesConfig.js +28 -0
  5. package/lib/config/authInfoConfig.d.ts +19 -0
  6. package/lib/config/authInfoConfig.js +35 -0
  7. package/lib/config/config.d.ts +181 -48
  8. package/lib/config/config.js +382 -159
  9. package/lib/config/configAggregator.d.ts +59 -42
  10. package/lib/config/configAggregator.js +135 -82
  11. package/lib/config/configFile.d.ts +2 -2
  12. package/lib/config/configFile.js +40 -31
  13. package/lib/config/configGroup.d.ts +9 -9
  14. package/lib/config/configGroup.js +13 -11
  15. package/lib/config/configStore.d.ts +9 -9
  16. package/lib/config/configStore.js +29 -26
  17. package/lib/config/envVars.d.ts +102 -0
  18. package/lib/config/envVars.js +457 -0
  19. package/lib/config/orgUsersConfig.d.ts +8 -0
  20. package/lib/config/orgUsersConfig.js +12 -0
  21. package/lib/config/sandboxOrgConfig.d.ts +8 -0
  22. package/lib/config/sandboxOrgConfig.js +12 -0
  23. package/lib/config/sandboxProcessCache.d.ts +16 -0
  24. package/lib/config/sandboxProcessCache.js +38 -0
  25. package/lib/config/tokensConfig.d.ts +10 -0
  26. package/lib/config/tokensConfig.js +29 -0
  27. package/lib/config/ttlConfig.d.ts +34 -0
  28. package/lib/config/ttlConfig.js +50 -0
  29. package/lib/crypto/crypto.js +29 -16
  30. package/lib/crypto/keyChain.js +4 -4
  31. package/lib/crypto/keyChainImpl.d.ts +5 -3
  32. package/lib/crypto/keyChainImpl.js +65 -66
  33. package/lib/crypto/secureBuffer.d.ts +1 -1
  34. package/lib/crypto/secureBuffer.js +1 -1
  35. package/lib/deviceOauthService.d.ts +5 -5
  36. package/lib/deviceOauthService.js +37 -33
  37. package/lib/exported.d.ts +22 -15
  38. package/lib/exported.js +49 -25
  39. package/lib/global.d.ts +17 -3
  40. package/lib/global.js +46 -13
  41. package/lib/lifecycleEvents.d.ts +39 -2
  42. package/lib/lifecycleEvents.js +77 -3
  43. package/lib/logger.d.ts +21 -11
  44. package/lib/logger.js +121 -105
  45. package/lib/messages.d.ts +53 -36
  46. package/lib/messages.js +89 -97
  47. package/lib/org/authInfo.d.ts +84 -72
  48. package/lib/org/authInfo.js +326 -320
  49. package/lib/org/authRemover.d.ts +16 -23
  50. package/lib/org/authRemover.js +62 -60
  51. package/lib/org/connection.d.ts +33 -59
  52. package/lib/org/connection.js +129 -190
  53. package/lib/org/index.js +6 -2
  54. package/lib/org/org.d.ts +263 -38
  55. package/lib/org/org.js +734 -149
  56. package/lib/org/orgConfigProperties.d.ts +69 -0
  57. package/lib/org/orgConfigProperties.js +121 -0
  58. package/lib/org/permissionSetAssignment.js +6 -15
  59. package/lib/org/scratchOrgCache.d.ts +20 -0
  60. package/lib/org/scratchOrgCache.js +33 -0
  61. package/lib/org/scratchOrgCreate.d.ts +54 -0
  62. package/lib/org/scratchOrgCreate.js +214 -0
  63. package/lib/org/scratchOrgErrorCodes.d.ts +10 -0
  64. package/lib/org/scratchOrgErrorCodes.js +79 -0
  65. package/lib/org/scratchOrgFeatureDeprecation.d.ts +26 -0
  66. package/lib/org/scratchOrgFeatureDeprecation.js +105 -0
  67. package/lib/org/scratchOrgInfoApi.d.ts +68 -0
  68. package/lib/org/scratchOrgInfoApi.js +416 -0
  69. package/lib/org/scratchOrgInfoGenerator.d.ts +64 -0
  70. package/lib/org/scratchOrgInfoGenerator.js +237 -0
  71. package/lib/org/scratchOrgLifecycleEvents.d.ts +10 -0
  72. package/lib/org/scratchOrgLifecycleEvents.js +41 -0
  73. package/lib/org/scratchOrgSettingsGenerator.d.ts +79 -0
  74. package/lib/org/scratchOrgSettingsGenerator.js +277 -0
  75. package/lib/org/scratchOrgTypes.d.ts +43 -0
  76. package/lib/{status/client.js → org/scratchOrgTypes.js} +1 -1
  77. package/lib/org/user.d.ts +7 -2
  78. package/lib/org/user.js +78 -54
  79. package/lib/schema/printer.d.ts +6 -0
  80. package/lib/schema/printer.js +49 -46
  81. package/lib/schema/validator.d.ts +12 -10
  82. package/lib/schema/validator.js +56 -76
  83. package/lib/{sfdxError.d.ts → sfError.d.ts} +12 -15
  84. package/lib/{sfdxError.js → sfError.js} +42 -24
  85. package/lib/{sfdxProject.d.ts → sfProject.d.ts} +75 -35
  86. package/lib/sfProject.js +651 -0
  87. package/lib/stateAggregator/accessors/aliasAccessor.d.ts +98 -0
  88. package/lib/stateAggregator/accessors/aliasAccessor.js +146 -0
  89. package/lib/stateAggregator/accessors/orgAccessor.d.ts +101 -0
  90. package/lib/stateAggregator/accessors/orgAccessor.js +240 -0
  91. package/lib/stateAggregator/accessors/sandboxAccessor.d.ts +8 -0
  92. package/lib/stateAggregator/accessors/sandboxAccessor.js +28 -0
  93. package/lib/stateAggregator/accessors/tokenAccessor.d.ts +63 -0
  94. package/lib/stateAggregator/accessors/tokenAccessor.js +80 -0
  95. package/lib/stateAggregator/index.d.ts +4 -0
  96. package/lib/stateAggregator/index.js +27 -0
  97. package/lib/stateAggregator/stateAggregator.d.ts +25 -0
  98. package/lib/stateAggregator/stateAggregator.js +46 -0
  99. package/lib/status/myDomainResolver.d.ts +1 -1
  100. package/lib/status/myDomainResolver.js +10 -10
  101. package/lib/status/pollingClient.d.ts +2 -6
  102. package/lib/status/pollingClient.js +38 -64
  103. package/lib/status/streamingClient.d.ts +5 -80
  104. package/lib/status/streamingClient.js +74 -94
  105. package/lib/status/types.d.ts +89 -0
  106. package/lib/status/types.js +18 -0
  107. package/lib/testSetup.d.ts +212 -79
  108. package/lib/testSetup.js +478 -182
  109. package/lib/util/cache.d.ts +11 -0
  110. package/lib/util/cache.js +70 -0
  111. package/lib/util/checkLightningDomain.d.ts +1 -0
  112. package/lib/util/checkLightningDomain.js +29 -0
  113. package/lib/util/directoryWriter.d.ts +12 -0
  114. package/lib/util/directoryWriter.js +54 -0
  115. package/lib/util/getJwtAudienceUrl.d.ts +4 -0
  116. package/lib/util/getJwtAudienceUrl.js +19 -0
  117. package/lib/util/internal.d.ts +28 -2
  118. package/lib/util/internal.js +65 -8
  119. package/lib/util/jsonXmlTools.d.ts +14 -0
  120. package/lib/util/jsonXmlTools.js +39 -0
  121. package/lib/util/mapKeys.d.ts +14 -0
  122. package/lib/util/mapKeys.js +52 -0
  123. package/lib/util/sfdc.d.ts +51 -63
  124. package/lib/util/sfdc.js +75 -127
  125. package/lib/util/sfdcUrl.d.ts +64 -0
  126. package/lib/util/sfdcUrl.js +197 -0
  127. package/lib/util/structuredWriter.d.ts +9 -0
  128. package/lib/util/structuredWriter.js +3 -0
  129. package/lib/util/zipWriter.d.ts +16 -0
  130. package/lib/util/zipWriter.js +68 -0
  131. package/lib/webOAuthServer.d.ts +20 -7
  132. package/lib/webOAuthServer.js +107 -60
  133. package/messageTransformer/messageTransformer.ts +93 -0
  134. package/messages/auth.md +11 -3
  135. package/messages/config.md +94 -6
  136. package/messages/connection.md +8 -0
  137. package/messages/core.json +3 -3
  138. package/messages/core.md +11 -1
  139. package/messages/envVars.md +313 -0
  140. package/messages/org.md +64 -0
  141. package/messages/scratchOrgCreate.md +23 -0
  142. package/messages/scratchOrgErrorCodes.md +115 -0
  143. package/messages/scratchOrgFeatureDeprecation.md +11 -0
  144. package/messages/scratchOrgInfoApi.md +20 -0
  145. package/messages/scratchOrgInfoGenerator.md +27 -0
  146. package/messages/user.md +12 -0
  147. package/package.json +138 -66
  148. package/CHANGELOG.md +0 -699
  149. package/lib/config/aliases.d.ts +0 -56
  150. package/lib/config/aliases.js +0 -96
  151. package/lib/config/globalInfoConfig.d.ts +0 -74
  152. package/lib/config/globalInfoConfig.js +0 -144
  153. package/lib/config/keychainConfig.d.ts +0 -19
  154. package/lib/config/keychainConfig.js +0 -43
  155. package/lib/config/sfdxDataHandler.d.ts +0 -36
  156. package/lib/config/sfdxDataHandler.js +0 -165
  157. package/lib/sfdxProject.js +0 -546
  158. package/lib/status/client.d.ts +0 -15
  159. package/lib/util/fs.d.ts +0 -198
  160. package/lib/util/fs.js +0 -374
@@ -5,160 +5,30 @@
5
5
  * Licensed under the BSD 3-Clause license.
6
6
  * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
7
7
  */
8
+ /* eslint-disable class-methods-use-this */
8
9
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.AuthInfo = exports.DEFAULT_CONNECTED_APP_INFO = exports.getJwtAudienceUrl = exports.SfdcUrl = exports.OAuth2WithVerifier = void 0;
10
+ exports.AuthInfo = exports.DEFAULT_CONNECTED_APP_INFO = void 0;
10
11
  const crypto_1 = require("crypto");
11
- const url_1 = require("url");
12
- const dns = require("dns");
13
12
  const path_1 = require("path");
14
- const url_2 = require("url");
15
13
  const os = require("os");
14
+ const fs = require("fs");
16
15
  const kit_1 = require("@salesforce/kit");
17
16
  const ts_types_1 = require("@salesforce/ts-types");
18
17
  const jsforce_1 = require("jsforce");
19
- // No typings directly available for jsforce/lib/transport
20
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
21
- // @ts-ignore
22
- const Transport = require("jsforce/lib/transport");
18
+ const transport_1 = require("jsforce/lib/transport");
23
19
  const jwt = require("jsonwebtoken");
24
- const aliases_1 = require("../config/aliases");
25
20
  const config_1 = require("../config/config");
26
21
  const configAggregator_1 = require("../config/configAggregator");
27
22
  const logger_1 = require("../logger");
28
- const sfdxError_1 = require("../sfdxError");
29
- const fs_1 = require("../util/fs");
23
+ const sfError_1 = require("../sfError");
30
24
  const sfdc_1 = require("../util/sfdc");
31
- const myDomainResolver_1 = require("../status/myDomainResolver");
32
- const globalInfoConfig_1 = require("../config/globalInfoConfig");
25
+ const stateAggregator_1 = require("../stateAggregator");
33
26
  const messages_1 = require("../messages");
27
+ const sfdcUrl_1 = require("../util/sfdcUrl");
34
28
  const connection_1 = require("./connection");
35
- messages_1.Messages.importMessagesDirectory(__dirname);
36
- const messages = messages_1.Messages.load('@salesforce/core', 'core', [
37
- 'authInfoCreationError',
38
- 'authInfoOverwriteError',
39
- 'namedOrgNotFound',
40
- 'orgDataNotAvailableError',
41
- 'orgDataNotAvailableError.actions',
42
- 'refreshTokenAuthError',
43
- 'jwtAuthError',
44
- 'authCodeUsernameRetrievalError',
45
- 'authCodeExchangeError',
46
- ]);
47
- // Extend OAuth2 to add JWT Bearer Token Flow support.
48
- class JwtOAuth2 extends jsforce_1.OAuth2 {
49
- constructor(options) {
50
- super(options);
51
- }
52
- jwtAuthorize(innerToken, callback) {
53
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
54
- // @ts-ignore
55
- return super._postParams({
56
- grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
57
- assertion: innerToken,
58
- }, callback);
59
- }
60
- }
61
- /**
62
- * Extend OAuth2 to add code verifier support for the auth code (web auth) flow
63
- * const oauth2 = new OAuth2WithVerifier({ loginUrl, clientSecret, clientId, redirectUri });
64
- *
65
- * const authUrl = oauth2.getAuthorizationUrl({
66
- * state: 'foo',
67
- * prompt: 'login',
68
- * scope: 'api web'
69
- * });
70
- * console.log(authUrl);
71
- * const authCode = await retrieveCode();
72
- * const authInfo = await AuthInfo.create({ oauth2Options: { clientId, clientSecret, loginUrl, authCode }, oauth2});
73
- * console.log(`access token: ${authInfo.getFields(true).accessToken}`);
74
- */
75
- class OAuth2WithVerifier extends jsforce_1.OAuth2 {
76
- constructor(options) {
77
- super(options);
78
- // Set a code verifier string for OAuth authorization
79
- this.codeVerifier = base64UrlEscape(crypto_1.randomBytes(Math.ceil(128)).toString('base64'));
80
- }
81
- /**
82
- * Overrides jsforce.OAuth2.getAuthorizationUrl. Get Salesforce OAuth2 authorization page
83
- * URL to redirect user agent, adding a verification code for added security.
84
- *
85
- * @param params
86
- */
87
- getAuthorizationUrl(params) {
88
- // code verifier must be a base 64 url encoded hash of 128 bytes of random data. Our random data is also
89
- // base 64 url encoded. See Connection.create();
90
- const codeChallenge = base64UrlEscape(crypto_1.createHash('sha256').update(this.codeVerifier).digest('base64'));
91
- kit_1.set(params, 'code_challenge', codeChallenge);
92
- return super.getAuthorizationUrl(params);
93
- }
94
- async requestToken(code, callback) {
95
- return super.requestToken(code, callback);
96
- }
97
- /**
98
- * Overrides jsforce.OAuth2._postParams because jsforce's oauth impl doesn't support
99
- * coder_verifier and code_challenge. This enables the server to disallow trading a one-time auth code
100
- * for an access/refresh token when the verifier and challenge are out of alignment.
101
- *
102
- * See https://github.com/jsforce/jsforce/issues/665
103
- */
104
- async _postParams(params, callback) {
105
- kit_1.set(params, 'code_verifier', this.codeVerifier);
106
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
107
- // @ts-ignore TODO: need better typings for jsforce
108
- return super._postParams(params, callback);
109
- }
110
- }
111
- exports.OAuth2WithVerifier = OAuth2WithVerifier;
112
- /**
113
- * Salesforce URLs.
114
- */
115
- var SfdcUrl;
116
- (function (SfdcUrl) {
117
- SfdcUrl["SANDBOX"] = "https://test.salesforce.com";
118
- SfdcUrl["PRODUCTION"] = "https://login.salesforce.com";
119
- })(SfdcUrl = exports.SfdcUrl || (exports.SfdcUrl = {}));
120
- function isSandboxUrl(options) {
121
- var _a;
122
- const createdOrgInstance = ts_types_1.getString(options, 'createdOrgInstance', '').trim().toLowerCase();
123
- const loginUrl = (_a = options.loginUrl) !== null && _a !== void 0 ? _a : '';
124
- return (/^cs|s$/gi.test(createdOrgInstance) ||
125
- /sandbox\.my\.salesforce\.com/gi.test(loginUrl) || // enhanced domains >= 230
126
- /(cs[0-9]+(\.my|)\.salesforce\.com)/gi.test(loginUrl) || // my domains on CS instance OR CS instance without my domain
127
- /([a-z]{3}[0-9]+s\.sfdc-.+\.salesforce\.com)/gi.test(loginUrl) || // falcon sandbox ex: usa2s.sfdc-whatever.salesforce.com
128
- /([a-z]{3}[0-9]+s\.sfdc-.+\.force\.com)/gi.test(loginUrl) || // falcon sandbox ex: usa2s.sfdc-whatever.salesforce.com
129
- url_2.parse(loginUrl).hostname === 'test.salesforce.com');
130
- }
131
- async function resolvesToSandbox(options) {
132
- if (isSandboxUrl(options)) {
133
- return true;
134
- }
135
- let cnames = [];
136
- if (options.loginUrl) {
137
- const myDomainResolver = await myDomainResolver_1.MyDomainResolver.create({ url: new url_1.URL(options.loginUrl) });
138
- cnames = await myDomainResolver.getCnames();
139
- }
140
- return cnames.some((cname) => isSandboxUrl(Object.assign(Object.assign({}, options), { loginUrl: cname })));
141
- }
142
- async function getJwtAudienceUrl(options) {
143
- var _a;
144
- // environment variable is used as an override
145
- if (process.env.SFDX_AUDIENCE_URL) {
146
- return process.env.SFDX_AUDIENCE_URL;
147
- }
148
- if (options.loginUrl && sfdc_1.sfdc.isInternalUrl(options.loginUrl)) {
149
- // This is for internal developers when just doing authorize;
150
- return options.loginUrl;
151
- }
152
- if (await resolvesToSandbox(options)) {
153
- return SfdcUrl.SANDBOX;
154
- }
155
- const createdOrgInstance = ts_types_1.getString(options, 'createdOrgInstance', '').trim().toLowerCase();
156
- if (/^gs1/gi.test(createdOrgInstance) || /(gs1.my.salesforce.com)/gi.test((_a = options.loginUrl) !== null && _a !== void 0 ? _a : '')) {
157
- return 'https://gs1.salesforce.com';
158
- }
159
- return SfdcUrl.PRODUCTION;
160
- }
161
- exports.getJwtAudienceUrl = getJwtAudienceUrl;
29
+ const orgConfigProperties_1 = require("./orgConfigProperties");
30
+ const org_1 = require("./org");
31
+ const messages = new messages_1.Messages('@salesforce/core', 'core', new Map([["authInfoCreationError", "Must pass a username and/or OAuth options when creating an AuthInfo instance."], ["authInfoOverwriteError", "Cannot create an AuthInfo instance that will overwrite existing auth data."], ["authInfoOverwriteError.actions", ["Create the AuthInfo instance using existing auth data by just passing the username. E.g., `AuthInfo.create({ username: 'my@user.org' });`."]], ["authCodeExchangeError", "Error authenticating with auth code due to: %s"], ["authCodeUsernameRetrievalError", "Could not retrieve the username after successful auth code exchange.\n\nDue to: %s"], ["jwtAuthError", "Error authenticating with JWT config due to: %s"], ["jwtAuthErrors", "Error authenticating with JWT.\nErrors encountered:\n%s"], ["refreshTokenAuthError", "Error authenticating with the refresh token due to: %s"], ["orgDataNotAvailableError", "An attempt to refresh the authentication token failed with a 'Data Not Found Error'. The org identified by username %s does not appear to exist. Likely cause is that the org was deleted by another user or has expired."], ["orgDataNotAvailableError.actions", ["Run `sfdx force:org:list --clean` to remove stale org authentications.", "Use `sfdx force:config:set` to update the defaultusername.", "Use `sfdx force:org:create` to create a new org.", "Use `sfdx auth` to authenticate an existing org."]], ["namedOrgNotFound", "No authorization information found for %s."], ["noAliasesFound", "Nothing to set."], ["invalidFormat", "Setting aliases must be in the format <key>=<value> but found: [%s]."], ["invalidJsonCasing", "All JSON input must have heads down camelcase keys. E.g., `{ sfdcLoginUrl: \"https://login.salesforce.com\" }`\nFound \"%s\" at %s"], ["missingClientId", "Client ID is required for JWT authentication."]]));
162
32
  // parses the id field returned from jsForce oauth2 methods to get
163
33
  // user ID and org ID.
164
34
  function parseIdUrl(idUrl) {
@@ -173,23 +43,8 @@ function parseIdUrl(idUrl) {
173
43
  }
174
44
  exports.DEFAULT_CONNECTED_APP_INFO = {
175
45
  clientId: 'PlatformCLI',
176
- // Legacy. The connected app info is owned by the thing that
177
- // creates new AuthInfos. Currently that is the auth:* commands which
178
- // aren't owned by this core library. These values need to be here
179
- // for any old auth files where the id and secret aren't stored.
180
- //
181
- // Ideally, this would be removed at some point in the distant future
182
- // when all auth files now have the clientId stored in it.
183
- legacyClientId: 'SalesforceDevelopmentExperience',
184
- legacyClientSecret: '1384510088588713504',
46
+ clientSecret: '',
185
47
  };
186
- // Makes a nodejs base64 encoded string compatible with rfc4648 alternative encoding for urls.
187
- // @param base64Encoded a nodejs base64 encoded string
188
- function base64UrlEscape(base64Encoded) {
189
- // builtin node js base 64 encoding is not 64 url compatible.
190
- // See https://toolsn.ietf.org/html/rfc4648#section-5
191
- return base64Encoded.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
192
- }
193
48
  /**
194
49
  * Handles persistence and fetching of user authentication information using
195
50
  * JWT, OAuth, or refresh tokens. Sets up the refresh flows that jsForce will
@@ -235,7 +90,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
235
90
  super(options);
236
91
  // Possibly overridden in create
237
92
  this.usingAccessToken = false;
238
- this.options = options || {};
93
+ this.options = options ?? {};
239
94
  }
240
95
  /**
241
96
  * Returns the default instance url
@@ -243,63 +98,83 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
243
98
  * @returns {string}
244
99
  */
245
100
  static getDefaultInstanceUrl() {
246
- const configuredInstanceUrl = configAggregator_1.ConfigAggregator.getValue('instanceUrl').value;
247
- return configuredInstanceUrl || 'https://login.salesforce.com';
101
+ const configuredInstanceUrl = configAggregator_1.ConfigAggregator.getValue(orgConfigProperties_1.OrgConfigProperties.ORG_INSTANCE_URL)?.value;
102
+ return configuredInstanceUrl ?? sfdcUrl_1.SfdcUrl.PRODUCTION;
248
103
  }
249
104
  /**
250
105
  * Get a list of all authorizations based on auth files stored in the global directory.
106
+ * One can supply a filter (see @param orgAuthFilter) and calling this function without
107
+ * a filter will return all authorizations.
251
108
  *
252
- * @returns {Promise<SfOrg[]>}
109
+ * @param orgAuthFilter A predicate function that returns true for those org authorizations that are to be retained.
110
+ *
111
+ * @returns {Promise<OrgAuthorization[]>}
253
112
  */
254
- static async listAllAuthorizations() {
255
- const globalInfo = await globalInfoConfig_1.GlobalInfo.getInstance();
256
- const auths = Object.values(globalInfo.getOrgs());
257
- const aliases = await aliases_1.Aliases.create(aliases_1.Aliases.getDefaultOptions());
113
+ static async listAllAuthorizations(orgAuthFilter = (orgAuth) => !!orgAuth) {
114
+ const stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
115
+ const config = (await configAggregator_1.ConfigAggregator.create()).getConfigInfo();
116
+ const orgs = await stateAggregator.orgs.readAll();
258
117
  const final = [];
259
- for (const auth of auths) {
260
- const username = ts_types_1.ensureString(auth.username);
261
- const [alias] = aliases.getKeysByValue(username);
118
+ for (const org of orgs) {
119
+ const username = (0, ts_types_1.ensureString)(org.username);
120
+ const aliases = stateAggregator.aliases.getAll(username) ?? undefined;
121
+ // Get a list of configuration values that are set to either the username or one
122
+ // of the aliases
123
+ const configs = config
124
+ .filter((c) => aliases.includes(c.value) || c.value === username)
125
+ .map((c) => c.key);
262
126
  try {
127
+ // prevent ConfigFile collision bug
128
+ // eslint-disable-next-line no-await-in-loop
263
129
  const authInfo = await AuthInfo.create({ username });
264
- const { orgId, instanceUrl } = authInfo.getFields();
130
+ const { orgId, instanceUrl, devHubUsername, expirationDate, isDevHub } = authInfo.getFields();
265
131
  final.push({
266
- alias,
132
+ aliases,
133
+ configs,
267
134
  username,
268
- orgId,
269
135
  instanceUrl,
136
+ isScratchOrg: Boolean(devHubUsername),
137
+ isDevHub: isDevHub ?? false,
138
+ // eslint-disable-next-line no-await-in-loop
139
+ isSandbox: await stateAggregator.sandboxes.hasFile(orgId),
140
+ orgId: orgId,
270
141
  accessToken: authInfo.getConnectionOptions().accessToken,
271
142
  oauthMethod: authInfo.isJwt() ? 'jwt' : authInfo.isOauth() ? 'web' : 'token',
272
- timestamp: auth.timestamp,
143
+ isExpired: Boolean(devHubUsername) && expirationDate
144
+ ? new Date((0, ts_types_1.ensureString)(expirationDate)).getTime() < new Date().getTime()
145
+ : 'unknown',
273
146
  });
274
147
  }
275
148
  catch (err) {
276
149
  final.push({
277
- alias,
150
+ aliases,
151
+ configs,
278
152
  username,
279
- orgId: auth.orgId,
280
- instanceUrl: auth.instanceUrl,
153
+ orgId: org.orgId,
154
+ instanceUrl: org.instanceUrl,
281
155
  accessToken: undefined,
282
156
  oauthMethod: 'unknown',
283
157
  error: err.message,
284
- timestamp: auth.timestamp,
158
+ isExpired: 'unknown',
285
159
  });
286
160
  }
287
161
  }
288
- return final;
162
+ return final.filter(orgAuthFilter);
289
163
  }
290
164
  /**
291
165
  * Returns true if one or more authentications are persisted.
292
166
  */
293
167
  static async hasAuthentications() {
294
168
  try {
295
- const auths = (await globalInfoConfig_1.GlobalInfo.getInstance()).getOrgs();
296
- return !kit_1.isEmpty(auths);
169
+ const auths = await (await stateAggregator_1.StateAggregator.getInstance()).orgs.list();
170
+ return !(0, kit_1.isEmpty)(auths);
297
171
  }
298
172
  catch (err) {
299
- if (err.name === 'OrgDataNotAvailableError' || err.code === 'ENOENT') {
173
+ const error = err;
174
+ if (error.name === 'OrgDataNotAvailableError' || error.code === 'ENOENT') {
300
175
  return false;
301
176
  }
302
- throw err;
177
+ throw error;
303
178
  }
304
179
  }
305
180
  /**
@@ -308,14 +183,16 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
308
183
  * @param options The options to generate the URL.
309
184
  */
310
185
  static getAuthorizationUrl(options, oauth2) {
311
- const oauth2Verifier = oauth2 || new OAuth2WithVerifier(options);
186
+ // Always use a verifier for enhanced security
187
+ options.useVerifier = true;
188
+ const oauth2Verifier = oauth2 ?? new jsforce_1.OAuth2(options);
312
189
  // The state parameter allows the redirectUri callback listener to ignore request
313
190
  // that don't contain the state value.
314
191
  const params = {
315
- state: crypto_1.randomBytes(Math.ceil(6)).toString('hex'),
192
+ state: (0, crypto_1.randomBytes)(Math.ceil(6)).toString('hex'),
316
193
  prompt: 'login',
317
194
  // Default connected app is 'refresh_token api web'
318
- scope: options.scope || kit_1.env.getString('SFDX_AUTH_SCOPES', 'refresh_token api web'),
195
+ scope: options.scope ?? kit_1.env.getString('SFDX_AUTH_SCOPES', 'refresh_token api web'),
319
196
  };
320
197
  return oauth2Verifier.getAuthorizationUrl(params);
321
198
  }
@@ -331,7 +208,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
331
208
  static parseSfdxAuthUrl(sfdxAuthUrl) {
332
209
  const match = sfdxAuthUrl.match(/^force:\/\/([a-zA-Z0-9._-]+):([a-zA-Z0-9._-]*):([a-zA-Z0-9._-]+={0,2})@([a-zA-Z0-9._-]+)/);
333
210
  if (!match) {
334
- throw new sfdxError_1.SfdxError('Invalid sfdx auth url. Must be in the format `force://<clientId>:<clientSecret>:<refreshToken>@<loginUrl>`. The instanceUrl must not have the protocol set.', 'INVALID_SFDX_AUTH_URL');
211
+ throw new sfError_1.SfError('Invalid SFDX auth URL. Must be in the format "force://<clientId>:<clientSecret>:<refreshToken>@<instanceUrl>". Note that the SFDX auth URL uses the "force" protocol, and not "http" or "https". Also note that the "instanceUrl" inside the SFDX auth URL doesn\'t include the protocol ("https://").', 'INVALID_SFDX_AUTH_URL');
335
212
  }
336
213
  const [, clientId, clientSecret, refreshToken, loginUrl] = match;
337
214
  return {
@@ -341,6 +218,64 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
341
218
  loginUrl: `https://${loginUrl}`,
342
219
  };
343
220
  }
221
+ /**
222
+ * Given a set of decrypted fields and an authInfo, determine if the org belongs to an available
223
+ * dev hub.
224
+ *
225
+ * @param fields
226
+ * @param orgAuthInfo
227
+ */
228
+ static async identifyPossibleScratchOrgs(fields, orgAuthInfo) {
229
+ // fields property is passed in because the consumers of this method have performed the decrypt.
230
+ // This is so we don't have to call authInfo.getFields(true) and decrypt again OR accidentally save an
231
+ // authInfo before it is necessary.
232
+ const logger = await logger_1.Logger.child('Common', { tag: 'identifyPossibleScratchOrgs' });
233
+ // return if we already know the hub org we know it is a devhub or prod-like or no orgId present
234
+ if (fields.isDevHub || fields.devHubUsername || !fields.orgId)
235
+ return;
236
+ logger.debug('getting devHubs');
237
+ // TODO: return if url is not sandbox-like to avoid constantly asking about production orgs
238
+ // TODO: someday we make this easier by asking the org if it is a scratch org
239
+ const hubAuthInfos = await AuthInfo.getDevHubAuthInfos();
240
+ logger.debug(`found ${hubAuthInfos.length} DevHubs`);
241
+ if (hubAuthInfos.length === 0)
242
+ return;
243
+ // ask all those orgs if they know this orgId
244
+ await Promise.all(hubAuthInfos.map(async (hubAuthInfo) => {
245
+ try {
246
+ const soi = await AuthInfo.queryScratchOrg(hubAuthInfo.username, fields.orgId);
247
+ // if any return a result
248
+ logger.debug(`found orgId ${fields.orgId} in devhub ${hubAuthInfo.username}`);
249
+ try {
250
+ await orgAuthInfo.save({
251
+ ...fields,
252
+ devHubUsername: hubAuthInfo.username,
253
+ expirationDate: soi.ExpirationDate,
254
+ isScratch: true,
255
+ });
256
+ logger.debug(`set ${hubAuthInfo.username} as devhub and expirationDate ${soi.ExpirationDate} for scratch org ${orgAuthInfo.getUsername()}`);
257
+ }
258
+ catch (error) {
259
+ logger.debug(`error updating auth file for ${orgAuthInfo.getUsername()}`, error);
260
+ }
261
+ }
262
+ catch (error) {
263
+ logger.error(`Error connecting to devhub ${hubAuthInfo.username}`, error);
264
+ }
265
+ }));
266
+ }
267
+ /**
268
+ * Find all dev hubs available in the local environment.
269
+ */
270
+ static async getDevHubAuthInfos() {
271
+ return AuthInfo.listAllAuthorizations((possibleHub) => possibleHub?.isDevHub ?? false);
272
+ }
273
+ static async queryScratchOrg(devHubUsername, scratchOrgId) {
274
+ const devHubOrg = await org_1.Org.create({ aliasOrUsername: devHubUsername });
275
+ const conn = devHubOrg.getConnection();
276
+ const data = await conn.singleRecordQuery(`select Id, ExpirationDate from ScratchOrgInfo where ScratchOrg = '${(0, sfdc_1.trimTo15)(scratchOrgId)}'`);
277
+ return data;
278
+ }
344
279
  /**
345
280
  * Get the username.
346
281
  */
@@ -381,12 +316,12 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
381
316
  */
382
317
  async save(authData) {
383
318
  this.update(authData);
384
- const username = ts_types_1.ensure(this.getUsername());
385
- if (sfdc_1.sfdc.matchesAccessToken(username)) {
319
+ const username = (0, ts_types_1.ensure)(this.getUsername());
320
+ if ((0, sfdc_1.matchesAccessToken)(username)) {
386
321
  this.logger.debug('Username is an accesstoken. Skip saving authinfo to disk.');
387
322
  return this;
388
323
  }
389
- await this.globalInfo.write();
324
+ await this.stateAggregator.orgs.write(username);
390
325
  this.logger.info(`Saved auth info for username: ${username}`);
391
326
  return this;
392
327
  }
@@ -397,13 +332,10 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
397
332
  * @param authData Authorization fields to update.
398
333
  */
399
334
  update(authData) {
400
- // todo move into configstore
401
- if (authData && ts_types_1.isPlainObject(authData)) {
402
- this.username = authData.username || this.username;
403
- const existingFields = this.globalInfo.getOrg(this.getUsername());
404
- const mergedFields = Object.assign({}, existingFields || {}, authData);
405
- this.globalInfo.setOrg(this.getUsername(), mergedFields);
406
- this.logger.info(`Updated auth info for username: ${this.getUsername()}`);
335
+ if (authData && (0, ts_types_1.isPlainObject)(authData)) {
336
+ this.username = authData.username ?? this.username;
337
+ this.stateAggregator.orgs.update(this.username, authData);
338
+ this.logger.info(`Updated auth info for username: ${this.username}`);
407
339
  }
408
340
  return this;
409
341
  }
@@ -436,9 +368,9 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
436
368
  // Decrypt a user provided client secret or use the default.
437
369
  opts = {
438
370
  oauth2: {
439
- loginUrl: instanceUrl || 'https://login.salesforce.com',
440
- clientId: decryptedCopy.clientId || exports.DEFAULT_CONNECTED_APP_INFO.legacyClientId,
441
- redirectUri: 'http://localhost:1717/OauthRedirect',
371
+ loginUrl: instanceUrl ?? sfdcUrl_1.SfdcUrl.PRODUCTION,
372
+ clientId: this.getClientId(),
373
+ redirectUri: this.getRedirectUri(),
442
374
  },
443
375
  accessToken,
444
376
  instanceUrl,
@@ -448,21 +380,27 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
448
380
  // decrypt the fields
449
381
  return opts;
450
382
  }
383
+ getClientId() {
384
+ return this.getFields()?.clientId ?? exports.DEFAULT_CONNECTED_APP_INFO.clientId;
385
+ }
386
+ getRedirectUri() {
387
+ return 'http://localhost:1717/OauthRedirect';
388
+ }
451
389
  /**
452
390
  * Get the authorization fields.
453
391
  *
454
392
  * @param decrypt Decrypt the fields.
455
393
  */
456
394
  getFields(decrypt) {
457
- return this.globalInfo.getOrg(this.username, decrypt);
395
+ return this.stateAggregator.orgs.get(this.username, decrypt) ?? {};
458
396
  }
459
397
  /**
460
398
  * Get the org front door (used for web based oauth flows)
461
399
  */
462
400
  getOrgFrontDoorUrl() {
463
401
  const authFields = this.getFields(true);
464
- const base = ts_types_1.ensureString(authFields.instanceUrl).replace(/\/+$/, '');
465
- const accessToken = ts_types_1.ensureString(authFields.accessToken);
402
+ const base = (0, ts_types_1.ensureString)(authFields.instanceUrl).replace(/\/+$/, '');
403
+ const accessToken = (0, ts_types_1.ensureString)(authFields.accessToken);
466
404
  return `${base}/secur/frontdoor.jsp?sid=${accessToken}`;
467
405
  }
468
406
  /**
@@ -478,38 +416,64 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
478
416
  */
479
417
  getSfdxAuthUrl() {
480
418
  const decryptedFields = this.getFields(true);
481
- const instanceUrl = ts_types_1.ensure(decryptedFields.instanceUrl, 'undefined instanceUrl').replace(/^https?:\/\//, '');
419
+ const instanceUrl = (0, ts_types_1.ensure)(decryptedFields.instanceUrl, 'undefined instanceUrl').replace(/^https?:\/\//, '');
482
420
  let sfdxAuthUrl = 'force://';
483
421
  if (decryptedFields.clientId) {
484
- sfdxAuthUrl += `${decryptedFields.clientId}:${decryptedFields.clientSecret || ''}:`;
422
+ sfdxAuthUrl += `${decryptedFields.clientId}:${decryptedFields.clientSecret ?? ''}:`;
485
423
  }
486
- sfdxAuthUrl += `${ts_types_1.ensure(decryptedFields.refreshToken, 'undefined refreshToken')}@${instanceUrl}`;
424
+ sfdxAuthUrl += `${(0, ts_types_1.ensure)(decryptedFields.refreshToken, 'undefined refreshToken')}@${instanceUrl}`;
487
425
  return sfdxAuthUrl;
488
426
  }
489
427
  /**
490
- * Set the defaultusername or the defaultdevhubusername to the alias if
428
+ * Convenience function to handle typical side effects encountered when dealing with an AuthInfo.
429
+ * Given the values supplied in parameter sideEffects, this function will set auth alias, default auth
430
+ * and default dev hub.
431
+ *
432
+ * @param sideEffects - instance of AuthSideEffects
433
+ */
434
+ async handleAliasAndDefaultSettings(sideEffects) {
435
+ if (sideEffects.alias ||
436
+ sideEffects.setDefault ||
437
+ sideEffects.setDefaultDevHub ||
438
+ typeof sideEffects.setTracksSource === 'boolean') {
439
+ if (sideEffects.alias)
440
+ await this.setAlias(sideEffects.alias);
441
+ if (sideEffects.setDefault)
442
+ await this.setAsDefault({ org: true });
443
+ if (sideEffects.setDefaultDevHub)
444
+ await this.setAsDefault({ devHub: true });
445
+ if (typeof sideEffects.setTracksSource === 'boolean') {
446
+ await this.save({ tracksSource: sideEffects.setTracksSource });
447
+ }
448
+ else {
449
+ await this.save();
450
+ }
451
+ }
452
+ }
453
+ /**
454
+ * Set the target-env (default) or the target-dev-hub to the alias if
491
455
  * it exists otherwise to the username. Method will try to set the local
492
456
  * config first but will default to global config if that fails.
493
457
  *
494
458
  * @param options
495
459
  */
496
- async setAsDefault(options) {
460
+ async setAsDefault(options = { org: true }) {
497
461
  let config;
498
462
  // if we fail to create the local config, default to the global config
499
463
  try {
500
464
  config = await config_1.Config.create({ isGlobal: false });
501
465
  }
502
- catch (_a) {
466
+ catch {
503
467
  config = await config_1.Config.create({ isGlobal: true });
504
468
  }
505
- const username = ts_types_1.ensureString(this.getUsername());
506
- const aliases = await aliases_1.Aliases.create(aliases_1.Aliases.getDefaultOptions());
507
- const value = aliases.getKeysByValue(username)[0] || username;
508
- if (options.defaultUsername) {
509
- config.set(config_1.Config.DEFAULT_USERNAME, value);
469
+ const username = (0, ts_types_1.ensureString)(this.getUsername());
470
+ const alias = this.stateAggregator.aliases.get(username);
471
+ const value = alias ?? username;
472
+ if (options.org) {
473
+ config.set(orgConfigProperties_1.OrgConfigProperties.TARGET_ORG, value);
510
474
  }
511
- if (options.defaultDevhubUsername) {
512
- config.set(config_1.Config.DEFAULT_DEV_HUB_USERNAME, value);
475
+ if (options.devHub) {
476
+ config.set(orgConfigProperties_1.OrgConfigProperties.TARGET_DEV_HUB, value);
513
477
  }
514
478
  await config.write();
515
479
  }
@@ -519,17 +483,16 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
519
483
  * @param alias alias to set
520
484
  */
521
485
  async setAlias(alias) {
522
- const username = this.getUsername();
523
- await aliases_1.Aliases.parseAndUpdate([`${alias}=${username}`]);
486
+ this.stateAggregator.aliases.set(alias, this.getUsername());
487
+ await this.stateAggregator.aliases.write();
524
488
  }
525
489
  /**
526
490
  * Initializes an instance of the AuthInfo class.
527
491
  */
528
492
  async init() {
529
- // We have to set the global instance here because we need synchronous access to it later
530
- this.globalInfo = await globalInfoConfig_1.GlobalInfo.getInstance();
493
+ this.stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
531
494
  const username = this.options.username;
532
- const authOptions = this.options.oauth2Options || this.options.accessTokenOptions;
495
+ const authOptions = this.options.oauth2Options ?? this.options.accessTokenOptions;
533
496
  // Must specify either username and/or options
534
497
  if (!username && !authOptions) {
535
498
  throw messages.createError('authInfoCreationError');
@@ -537,21 +500,21 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
537
500
  // If a username AND oauth options, ensure an authorization for the username doesn't
538
501
  // already exist. Throw if it does so we don't overwrite the authorization.
539
502
  if (username && authOptions) {
540
- const authExists = this.globalInfo.hasOrg(username);
541
- if (authExists) {
503
+ if (await this.stateAggregator.orgs.hasFile(username)) {
542
504
  throw messages.createError('authInfoOverwriteError');
543
505
  }
544
506
  }
545
- const oauthUsername = username || ts_types_1.getString(authOptions, 'username');
507
+ const oauthUsername = username ?? authOptions?.username;
546
508
  if (oauthUsername) {
547
509
  this.username = oauthUsername;
510
+ await this.stateAggregator.orgs.read(oauthUsername, false, false);
548
511
  } // Else it will be set in initAuthOptions below.
549
512
  // If the username is an access token, use that for auth and don't persist
550
- if (ts_types_1.isString(oauthUsername) && sfdc_1.sfdc.matchesAccessToken(oauthUsername)) {
513
+ if ((0, ts_types_1.isString)(oauthUsername) && (0, sfdc_1.matchesAccessToken)(oauthUsername)) {
551
514
  // Need to initAuthOptions the logger and authInfoCrypto since we don't call init()
552
515
  this.logger = await logger_1.Logger.child('AuthInfo');
553
516
  const aggregator = await configAggregator_1.ConfigAggregator.create();
554
- const instanceUrl = this.getInstanceUrl(authOptions, aggregator);
517
+ const instanceUrl = this.getInstanceUrl(aggregator, authOptions);
555
518
  this.update({
556
519
  accessToken: oauthUsername,
557
520
  instanceUrl,
@@ -561,16 +524,16 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
561
524
  this.usingAccessToken = true;
562
525
  }
563
526
  // If a username with NO oauth options, ensure authorization already exist.
564
- else if (username && !authOptions && !this.globalInfo.hasOrg(username)) {
527
+ else if (username && !authOptions && !(await this.stateAggregator.orgs.exists(username))) {
565
528
  throw messages.createError('namedOrgNotFound', [username]);
566
529
  }
567
530
  else {
568
531
  await this.initAuthOptions(authOptions);
569
532
  }
570
533
  }
571
- getInstanceUrl(options, aggregator) {
572
- const instanceUrl = ts_types_1.getString(options, 'instanceUrl') || aggregator.getPropertyValue('instanceUrl');
573
- return instanceUrl || SfdcUrl.PRODUCTION;
534
+ getInstanceUrl(aggregator, options) {
535
+ const instanceUrl = options?.instanceUrl ?? aggregator.getPropertyValue(orgConfigProperties_1.OrgConfigProperties.ORG_INSTANCE_URL);
536
+ return instanceUrl ?? sfdcUrl_1.SfdcUrl.PRODUCTION;
574
537
  }
575
538
  /**
576
539
  * Initialize this AuthInfo instance with the specified options. If options are not provided, initialize it from cache
@@ -578,7 +541,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
578
541
  *
579
542
  * @param options Options to be used for creating an OAuth2 instance.
580
543
  *
581
- * **Throws** *{@link SfdxError}{ name: 'NamedOrgNotFoundError' }* Org information does not exist.
544
+ * **Throws** *{@link SfError}{ name: 'NamedOrgNotFoundError' }* Org information does not exist.
582
545
  * @returns {Promise<AuthInfo>}
583
546
  */
584
547
  async initAuthOptions(options) {
@@ -586,11 +549,11 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
586
549
  // If options were passed, use those before checking cache and reading an auth file.
587
550
  let authConfig;
588
551
  if (options) {
589
- options = kit_1.cloneJson(options);
552
+ options = (0, kit_1.cloneJson)(options);
590
553
  if (this.isTokenOptions(options)) {
591
554
  authConfig = options;
592
- const userInfo = await this.retrieveUserInfo(ts_types_1.ensureString(options.instanceUrl), ts_types_1.ensureString(options.accessToken));
593
- this.update({ username: userInfo === null || userInfo === void 0 ? void 0 : userInfo.username, orgId: userInfo === null || userInfo === void 0 ? void 0 : userInfo.organizationId });
555
+ const userInfo = await this.retrieveUserInfo((0, ts_types_1.ensureString)(options.instanceUrl), (0, ts_types_1.ensureString)(options.accessToken));
556
+ this.update({ username: userInfo?.username, orgId: userInfo?.organizationId });
594
557
  }
595
558
  else {
596
559
  if (this.options.parentUsername) {
@@ -603,48 +566,50 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
603
566
  // Grab whatever flow is defined
604
567
  Object.assign(options, {
605
568
  clientSecret: parentFields.clientSecret,
606
- privateKey: parentFields.privateKey ? path_1.resolve(parentFields.privateKey) : parentFields.privateKey,
569
+ privateKey: parentFields.privateKey ? (0, path_1.resolve)(parentFields.privateKey) : parentFields.privateKey,
607
570
  });
608
571
  }
609
572
  }
610
573
  // jwt flow
611
574
  // Support both sfdx and jsforce private key values
612
575
  if (!options.privateKey && options.privateKeyFile) {
613
- options.privateKey = path_1.resolve(options.privateKeyFile);
576
+ options.privateKey = (0, path_1.resolve)(options.privateKeyFile);
614
577
  }
615
578
  if (options.privateKey) {
616
- authConfig = await this.buildJwtConfig(options);
579
+ authConfig = await this.authJwt(options);
617
580
  }
618
581
  else if (!options.authCode && options.refreshToken) {
619
582
  // refresh token flow (from sfdxUrl or OAuth refreshFn)
620
583
  authConfig = await this.buildRefreshTokenConfig(options);
621
584
  }
585
+ else if (this.options.oauth2 instanceof jsforce_1.OAuth2) {
586
+ // authcode exchange / web auth flow
587
+ authConfig = await this.exchangeToken(options, this.options.oauth2);
588
+ }
622
589
  else {
623
- if (this.options.oauth2 instanceof OAuth2WithVerifier) {
624
- // authcode exchange / web auth flow
625
- authConfig = await this.exchangeToken(options, this.options.oauth2);
626
- }
627
- else {
628
- authConfig = await this.exchangeToken(options);
629
- }
590
+ authConfig = await this.exchangeToken(options);
630
591
  }
631
592
  }
593
+ authConfig.isDevHub = await this.determineIfDevHub((0, ts_types_1.ensureString)(authConfig.instanceUrl), (0, ts_types_1.ensureString)(authConfig.accessToken));
594
+ if (authConfig.username)
595
+ await this.stateAggregator.orgs.read(authConfig.username, false, false);
632
596
  // Update the auth fields WITH encryption
633
597
  this.update(authConfig);
634
598
  }
635
599
  return this;
636
600
  }
601
+ // eslint-disable-next-line @typescript-eslint/require-await
637
602
  async loadDecryptedAuthFromConfig(username) {
638
603
  // Fetch from the persisted auth file
639
- const authInfo = this.globalInfo.getOrg(username, true);
604
+ const authInfo = this.stateAggregator.orgs.get(username, true);
640
605
  if (!authInfo) {
641
606
  throw messages.createError('namedOrgNotFound', [username]);
642
607
  }
643
608
  return authInfo;
644
609
  }
645
610
  isTokenOptions(options) {
646
- // Although OAuth2Options does not contain refreshToken, privateKey, or privateKeyFile, a JS consumer could still pass those in
647
- // which WILL have an access token as well, but it should be considered an OAuth2Options at that point.
611
+ // Although OAuth2Config does not contain refreshToken, privateKey, or privateKeyFile, a JS consumer could still pass those in
612
+ // which WILL have an access token as well, but it should be considered an OAuth2Config at that point.
648
613
  return ('accessToken' in options &&
649
614
  !('refreshToken' in options) &&
650
615
  !('privateKey' in options) &&
@@ -662,89 +627,119 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
662
627
  return await callback(null, fields.accessToken);
663
628
  }
664
629
  catch (err) {
665
- if (err.message && err.message.includes('Data Not Available')) {
630
+ const error = err;
631
+ if (error?.message?.includes('Data Not Available')) {
666
632
  // Set cause to keep original stacktrace
667
- return await callback(messages.createError('orgDataNotAvailableError', [this.getUsername()], [], err));
633
+ return await callback(messages.createError('orgDataNotAvailableError', [this.getUsername()], [], error));
668
634
  }
669
- return await callback(err);
635
+ return await callback(error);
670
636
  }
671
637
  }
638
+ async readJwtKey(keyFile) {
639
+ return fs.promises.readFile(keyFile, 'utf8');
640
+ }
672
641
  // Build OAuth config for a JWT auth flow
673
- async buildJwtConfig(options) {
674
- const privateKeyContents = await fs_1.fs.readFile(ts_types_1.ensure(options.privateKey), 'utf8');
675
- const audienceUrl = await getJwtAudienceUrl(options);
676
- const jwtToken = jwt.sign({
677
- iss: options.clientId,
678
- sub: this.getUsername(),
679
- aud: audienceUrl,
680
- exp: Date.now() + 300,
681
- }, privateKeyContents, {
682
- algorithm: 'RS256',
683
- });
684
- const oauth2 = new JwtOAuth2({ loginUrl: options.loginUrl });
642
+ async authJwt(options) {
643
+ if (!options.clientId) {
644
+ throw messages.createError('missingClientId');
645
+ }
646
+ const privateKeyContents = await this.readJwtKey((0, ts_types_1.ensureString)(options.privateKey));
647
+ const { loginUrl = sfdcUrl_1.SfdcUrl.PRODUCTION } = options;
648
+ const url = new sfdcUrl_1.SfdcUrl(loginUrl);
649
+ const createdOrgInstance = (this.getFields().createdOrgInstance ?? '').trim().toLowerCase();
650
+ const audienceUrl = await url.getJwtAudienceUrl(createdOrgInstance);
685
651
  let authFieldsBuilder;
686
- try {
687
- authFieldsBuilder = ts_types_1.ensureJsonMap(await oauth2.jwtAuthorize(jwtToken));
652
+ const authErrors = [];
653
+ // given that we can no longer depend on instance names or URls to determine audience, let's try them all
654
+ const loginAndAudienceUrls = (0, sfdcUrl_1.getLoginAudienceCombos)(audienceUrl, loginUrl);
655
+ for (const [login, audience] of loginAndAudienceUrls) {
656
+ try {
657
+ // sequentially, in probabilistic order
658
+ // eslint-disable-next-line no-await-in-loop
659
+ authFieldsBuilder = await this.tryJwtAuth(options.clientId, login, audience, privateKeyContents);
660
+ break;
661
+ }
662
+ catch (err) {
663
+ const error = err;
664
+ const message = error.message.includes('audience')
665
+ ? `${error.message} [audience=${audience} login=${login}]`
666
+ : error.message;
667
+ authErrors.push(message);
668
+ }
688
669
  }
689
- catch (err) {
690
- throw messages.createError('jwtAuthError', [err.message]);
670
+ if (!authFieldsBuilder) {
671
+ // messages.createError expects names to end in `error` and this one says Errors so do it manually.
672
+ throw new sfError_1.SfError(messages.getMessage('jwtAuthErrors', [authErrors.join('\n')]), 'JwtAuthError');
691
673
  }
692
674
  const authFields = {
693
- accessToken: ts_types_1.asString(authFieldsBuilder.access_token),
694
- orgId: parseIdUrl(ts_types_1.ensureString(authFieldsBuilder.id)).orgId,
675
+ accessToken: (0, ts_types_1.asString)(authFieldsBuilder.access_token),
676
+ orgId: parseIdUrl((0, ts_types_1.ensureString)(authFieldsBuilder.id)).orgId,
695
677
  loginUrl: options.loginUrl,
696
678
  privateKey: options.privateKey,
697
679
  clientId: options.clientId,
698
680
  };
699
- const instanceUrl = ts_types_1.ensureString(authFieldsBuilder.instance_url);
700
- const parsedUrl = url_2.parse(instanceUrl);
681
+ const instanceUrl = (0, ts_types_1.ensureString)(authFieldsBuilder.instance_url);
682
+ const sfdcUrl = new sfdcUrl_1.SfdcUrl(instanceUrl);
701
683
  try {
702
684
  // Check if the url is resolvable. This can fail when my-domains have not been replicated.
703
- await this.lookup(ts_types_1.ensure(parsedUrl.hostname));
685
+ await sfdcUrl.lookup();
704
686
  authFields.instanceUrl = instanceUrl;
705
687
  }
706
688
  catch (err) {
707
- this.logger.debug(`Instance URL [${authFieldsBuilder.instance_url}] is not available. DNS lookup failed. Using loginUrl [${options.loginUrl}] instead. This may result in a "Destination URL not reset" error.`);
689
+ this.logger.debug(
690
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
691
+ `Instance URL [${authFieldsBuilder.instance_url}] is not available. DNS lookup failed. Using loginUrl [${options.loginUrl}] instead. This may result in a "Destination URL not reset" error.`);
708
692
  authFields.instanceUrl = options.loginUrl;
709
693
  }
710
694
  return authFields;
711
695
  }
696
+ async tryJwtAuth(clientId, loginUrl, audienceUrl, privateKeyContents) {
697
+ const jwtToken = jwt.sign({
698
+ iss: clientId,
699
+ sub: this.getUsername(),
700
+ aud: audienceUrl,
701
+ exp: Date.now() + 300,
702
+ }, privateKeyContents, {
703
+ algorithm: 'RS256',
704
+ });
705
+ const oauth2 = new jsforce_1.JwtOAuth2({ loginUrl });
706
+ // jsforce has it types as any
707
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
708
+ return (0, ts_types_1.ensureJsonMap)(await oauth2.jwtAuthorize(jwtToken));
709
+ }
712
710
  // Build OAuth config for a refresh token auth flow
713
711
  async buildRefreshTokenConfig(options) {
714
712
  // Ideally, this would be removed at some point in the distant future when all auth files
715
713
  // now have the clientId stored in it.
716
714
  if (!options.clientId) {
717
- options.clientId = exports.DEFAULT_CONNECTED_APP_INFO.legacyClientId;
718
- options.clientSecret = exports.DEFAULT_CONNECTED_APP_INFO.legacyClientSecret;
715
+ options.clientId = exports.DEFAULT_CONNECTED_APP_INFO.clientId;
716
+ options.clientSecret = exports.DEFAULT_CONNECTED_APP_INFO.clientSecret;
717
+ }
718
+ if (!options.redirectUri) {
719
+ options.redirectUri = this.getRedirectUri();
719
720
  }
720
721
  const oauth2 = new jsforce_1.OAuth2(options);
721
722
  let authFieldsBuilder;
722
723
  try {
723
- authFieldsBuilder = await oauth2.refreshToken(ts_types_1.ensure(options.refreshToken));
724
+ authFieldsBuilder = await oauth2.refreshToken((0, ts_types_1.ensure)(options.refreshToken));
724
725
  }
725
726
  catch (err) {
726
727
  throw messages.createError('refreshTokenAuthError', [err.message]);
727
728
  }
728
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
729
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
729
730
  // @ts-ignore
730
731
  const { orgId } = parseIdUrl(authFieldsBuilder.id);
731
732
  let username = this.getUsername();
732
733
  if (!username) {
733
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
734
- // @ts-ignore
735
734
  const userInfo = await this.retrieveUserInfo(authFieldsBuilder.instance_url, authFieldsBuilder.access_token);
736
- username = ts_types_1.ensureString(userInfo === null || userInfo === void 0 ? void 0 : userInfo.username);
735
+ username = (0, ts_types_1.ensureString)(userInfo?.username);
737
736
  }
738
737
  return {
739
738
  orgId,
740
739
  username,
741
740
  accessToken: authFieldsBuilder.access_token,
742
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
743
- // @ts-ignore TODO: need better typings for jsforce
744
741
  instanceUrl: authFieldsBuilder.instance_url,
745
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
746
- // @ts-ignore TODO: need better typings for jsforce
747
- loginUrl: options.loginUrl || authFieldsBuilder.instance_url,
742
+ loginUrl: options.loginUrl ?? authFieldsBuilder.instance_url,
748
743
  refreshToken: options.refreshToken,
749
744
  clientId: options.clientId,
750
745
  clientSecret: options.clientSecret,
@@ -757,37 +752,37 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
757
752
  * @param oauth2 The oauth2 extension that includes a code_challenge
758
753
  */
759
754
  async exchangeToken(options, oauth2 = new jsforce_1.OAuth2(options)) {
755
+ if (!oauth2.redirectUri) {
756
+ oauth2.redirectUri = this.getRedirectUri();
757
+ }
758
+ if (!oauth2.clientId) {
759
+ oauth2.clientId = this.getClientId();
760
+ }
760
761
  // Exchange the auth code for an access token and refresh token.
761
762
  let authFields;
762
763
  try {
763
764
  this.logger.info(`Exchanging auth code for access token using loginUrl: ${options.loginUrl}`);
764
- authFields = await oauth2.requestToken(ts_types_1.ensure(options.authCode));
765
+ authFields = await oauth2.requestToken((0, ts_types_1.ensure)(options.authCode));
765
766
  }
766
767
  catch (err) {
767
768
  throw messages.createError('authCodeExchangeError', [err.message]);
768
769
  }
769
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
770
- // @ts-ignore TODO: need better typings for jsforce
771
770
  const { orgId } = parseIdUrl(authFields.id);
772
771
  let username = this.getUsername();
773
772
  // Only need to query for the username if it isn't known. For example, a new auth code exchange
774
773
  // rather than refreshing a token on an existing connection.
775
774
  if (!username) {
776
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
775
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
777
776
  // @ts-ignore
778
777
  const userInfo = await this.retrieveUserInfo(authFields.instance_url, authFields.access_token);
779
- username = userInfo === null || userInfo === void 0 ? void 0 : userInfo.username;
778
+ username = userInfo?.username;
780
779
  }
781
780
  return {
782
781
  accessToken: authFields.access_token,
783
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
784
- // @ts-ignore TODO: need better typings for jsforce
785
782
  instanceUrl: authFields.instance_url,
786
783
  orgId,
787
784
  username,
788
- // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
789
- // @ts-ignore TODO: need better typings for jsforce
790
- loginUrl: options.loginUrl || authFields.instance_url,
785
+ loginUrl: options.loginUrl ?? authFields.instance_url,
791
786
  refreshToken: authFields.refresh_token,
792
787
  clientId: options.clientId,
793
788
  clientSecret: options.clientSecret,
@@ -798,26 +793,27 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
798
793
  // but we don't want to create circular dependencies or lots of snowflakes
799
794
  // within this file to support it.
800
795
  const apiVersion = 'v51.0'; // hardcoding to v51.0 just for this call is okay.
801
- const instance = ts_types_1.ensure(instanceUrl);
802
- const baseUrl = new url_1.URL(instance);
803
- const userInfoUrl = `${baseUrl}services/oauth2/userinfo`;
796
+ const instance = (0, ts_types_1.ensure)(instanceUrl);
797
+ const baseUrl = new sfdcUrl_1.SfdcUrl(instance);
798
+ const userInfoUrl = `${baseUrl.toString()}services/oauth2/userinfo`;
804
799
  const headers = Object.assign({ Authorization: `Bearer ${accessToken}` }, connection_1.SFDX_HTTP_HEADERS);
805
800
  try {
806
801
  this.logger.info(`Sending request for Username after successful auth code exchange to URL: ${userInfoUrl}`);
807
- let response = await new Transport().httpRequest({ url: userInfoUrl, headers });
802
+ let response = await new transport_1.default().httpRequest({ url: userInfoUrl, method: 'GET', headers });
808
803
  if (response.statusCode >= 400) {
809
804
  this.throwUserGetException(response);
810
805
  }
811
806
  else {
812
- const userInfoJson = kit_1.parseJsonMap(response.body);
813
- const url = `${baseUrl}/services/data/${apiVersion}/sobjects/User/${userInfoJson.user_id}`;
807
+ const userInfoJson = (0, kit_1.parseJsonMap)(response.body);
808
+ const url = `${baseUrl.toString()}services/data/${apiVersion}/sobjects/User/${userInfoJson.user_id}`;
814
809
  this.logger.info(`Sending request for User SObject after successful auth code exchange to URL: ${url}`);
815
- response = await new Transport().httpRequest({ url, headers });
810
+ response = await new transport_1.default().httpRequest({ url, method: 'GET', headers });
816
811
  if (response.statusCode >= 400) {
817
812
  this.throwUserGetException(response);
818
813
  }
819
814
  else {
820
- userInfoJson.preferred_username = kit_1.parseJsonMap(response.body).Username;
815
+ // eslint-disable-next-line camelcase
816
+ userInfoJson.preferred_username = (0, kit_1.parseJsonMap)(response.body).Username;
821
817
  }
822
818
  return { username: userInfoJson.preferred_username, organizationId: userInfoJson.organization_id };
823
819
  }
@@ -833,37 +829,47 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
833
829
  * @private
834
830
  */
835
831
  throwUserGetException(response) {
836
- var _a;
837
832
  let errorMsg = '';
838
- const bodyAsString = ts_types_1.getString(response, 'body', JSON.stringify({ message: 'UNKNOWN', errorCode: 'UNKNOWN' }));
833
+ const bodyAsString = response.body ?? JSON.stringify({ message: 'UNKNOWN', errorCode: 'UNKNOWN' });
839
834
  try {
840
- const body = kit_1.parseJson(bodyAsString);
841
- if (ts_types_1.isArray(body)) {
842
- errorMsg = body
843
- .map((line) => { var _a; return (_a = ts_types_1.getString(line, 'message')) !== null && _a !== void 0 ? _a : ts_types_1.getString(line, 'errorCode', 'UNKNOWN'); })
844
- .join(os.EOL);
835
+ const body = (0, kit_1.parseJson)(bodyAsString);
836
+ if ((0, ts_types_1.isArray)(body)) {
837
+ errorMsg = body.map((line) => line.message ?? line.errorCode ?? 'UNKNOWN').join(os.EOL);
845
838
  }
846
839
  else {
847
- errorMsg = (_a = ts_types_1.getString(body, 'message')) !== null && _a !== void 0 ? _a : ts_types_1.getString(body, 'errorCode', 'UNKNOWN');
840
+ errorMsg = body.message ?? body.errorCode ?? 'UNKNOWN';
848
841
  }
849
842
  }
850
843
  catch (err) {
851
844
  errorMsg = `${bodyAsString}`;
852
845
  }
853
- throw new sfdxError_1.SfdxError(errorMsg);
846
+ throw new sfError_1.SfError(errorMsg);
854
847
  }
855
- // See https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback
856
- async lookup(host) {
857
- return new Promise((resolve, reject) => {
858
- dns.lookup(host, (err, address, family) => {
859
- if (err) {
860
- reject(err);
861
- }
862
- else {
863
- resolve({ address, family });
864
- }
865
- });
866
- });
848
+ /**
849
+ * Returns `true` if the org is a Dev Hub.
850
+ *
851
+ * Check access to the ScratchOrgInfo object to determine if the org is a dev hub.
852
+ */
853
+ async determineIfDevHub(instanceUrl, accessToken) {
854
+ // Make a REST call for the ScratchOrgInfo obj directly. Normally this is done via a connection
855
+ // but we don't want to create circular dependencies or lots of snowflakes
856
+ // within this file to support it.
857
+ const apiVersion = 'v51.0'; // hardcoding to v51.0 just for this call is okay.
858
+ const instance = (0, ts_types_1.ensure)(instanceUrl);
859
+ const baseUrl = new sfdcUrl_1.SfdcUrl(instance);
860
+ const scratchOrgInfoUrl = `${baseUrl.toString()}/services/data/${apiVersion}/query?q=SELECT%20Id%20FROM%20ScratchOrgInfo%20limit%201`;
861
+ const headers = Object.assign({ Authorization: `Bearer ${accessToken}` }, connection_1.SFDX_HTTP_HEADERS);
862
+ try {
863
+ const res = await new transport_1.default().httpRequest({ url: scratchOrgInfoUrl, method: 'GET', headers });
864
+ if (res.statusCode >= 400) {
865
+ return false;
866
+ }
867
+ return true;
868
+ }
869
+ catch (err) {
870
+ /* Not a dev hub */
871
+ return false;
872
+ }
867
873
  }
868
874
  }
869
875
  exports.AuthInfo = AuthInfo;