@salesforce/core 3.19.1 → 3.19.2

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
@@ -2,6 +2,12 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [3.19.2](https://github.com/forcedotcom/sfdx-core/compare/v3.19.1...v3.19.2) (2022-06-02)
6
+
7
+ ### Bug Fixes
8
+
9
+ - loosen audience url determination ([#588](https://github.com/forcedotcom/sfdx-core/issues/588)) ([a58ab89](https://github.com/forcedotcom/sfdx-core/commit/a58ab89e2ada34fbdb6d8c72d88966a5281db60b))
10
+
5
11
  ### [3.19.1](https://github.com/forcedotcom/sfdx-core/compare/v3.19.0...v3.19.1) (2022-05-27)
6
12
 
7
13
  ### Bug Fixes
@@ -289,7 +289,8 @@ export declare class AuthInfo extends AsyncOptionalCreatable<AuthInfo.Options> {
289
289
  private loadDecryptedAuthFromConfig;
290
290
  private isTokenOptions;
291
291
  private refreshFn;
292
- private buildJwtConfig;
292
+ private authJwt;
293
+ private tryJwtAuth;
293
294
  private buildRefreshTokenConfig;
294
295
  /**
295
296
  * Performs an authCode exchange but the Oauth2 feature of jsforce is extended to include a code_challenge
@@ -38,6 +38,7 @@ const messages = messages_1.Messages.load('@salesforce/core', 'core', [
38
38
  'jwtAuthError',
39
39
  'authCodeUsernameRetrievalError',
40
40
  'authCodeExchangeError',
41
+ 'missingClientId',
41
42
  ]);
42
43
  // Extend OAuth2 to add JWT Bearer Token Flow support.
43
44
  class JwtOAuth2 extends jsforce_1.OAuth2 {
@@ -607,7 +608,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
607
608
  options.privateKey = (0, path_1.resolve)(options.privateKeyFile);
608
609
  }
609
610
  if (options.privateKey) {
610
- authConfig = await this.buildJwtConfig(options);
611
+ authConfig = await this.authJwt(options);
611
612
  }
612
613
  else if (!options.authCode && options.refreshToken) {
613
614
  // refresh token flow (from sfdxUrl or OAuth refreshFn)
@@ -667,27 +668,32 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
667
668
  }
668
669
  }
669
670
  // Build OAuth config for a JWT auth flow
670
- async buildJwtConfig(options) {
671
+ async authJwt(options) {
672
+ if (!options.clientId) {
673
+ throw messages.createError('missingClientId');
674
+ }
671
675
  const privateKeyContents = await fs.promises.readFile((0, ts_types_1.ensure)(options.privateKey), 'utf8');
672
676
  const { loginUrl = sfdcUrl_1.SfdcUrl.PRODUCTION } = options;
673
677
  const url = new sfdcUrl_1.SfdcUrl(loginUrl);
674
678
  const createdOrgInstance = (0, ts_types_1.getString)(options, 'createdOrgInstance', '').trim().toLowerCase();
675
679
  const audienceUrl = await url.getJwtAudienceUrl(createdOrgInstance);
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 });
685
680
  let authFieldsBuilder;
686
- try {
687
- authFieldsBuilder = (0, ts_types_1.ensureJsonMap)(await oauth2.jwtAuthorize(jwtToken));
681
+ const authErrors = [];
682
+ // given that we can no longer depend on instance names or URls to determine audience, let's try them all
683
+ const loginAndAudienceUrls = (0, sfdcUrl_1.getLoginAudienceCombos)(audienceUrl, loginUrl);
684
+ for (const [login, audience] of loginAndAudienceUrls) {
685
+ try {
686
+ authFieldsBuilder = await this.tryJwtAuth(options.clientId, login, audience, privateKeyContents);
687
+ break;
688
+ }
689
+ catch (err) {
690
+ const error = err;
691
+ const message = error.message.includes('audience') ? `${error.message}-${login}:${audience}` : error.message;
692
+ authErrors.push(message);
693
+ }
688
694
  }
689
- catch (err) {
690
- throw messages.createError('jwtAuthError', [err.message]);
695
+ if (!authFieldsBuilder) {
696
+ throw messages.createError('jwtAuthError', [authErrors.join('\n')]);
691
697
  }
692
698
  const authFields = {
693
699
  accessToken: (0, ts_types_1.asString)(authFieldsBuilder.access_token),
@@ -709,6 +715,18 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
709
715
  }
710
716
  return authFields;
711
717
  }
718
+ async tryJwtAuth(clientId, loginUrl, audienceUrl, privateKeyContents) {
719
+ const jwtToken = jwt.sign({
720
+ iss: clientId,
721
+ sub: this.getUsername(),
722
+ aud: audienceUrl,
723
+ exp: Date.now() + 300,
724
+ }, privateKeyContents, {
725
+ algorithm: 'RS256',
726
+ });
727
+ const oauth2 = new JwtOAuth2({ loginUrl });
728
+ return (0, ts_types_1.ensureJsonMap)(await oauth2.jwtAuthorize(jwtToken));
729
+ }
712
730
  // Build OAuth config for a refresh token auth flow
713
731
  async buildRefreshTokenConfig(options) {
714
732
  // Ideally, this would be removed at some point in the distant future when all auth files
@@ -24,7 +24,6 @@ const scratchOrgCache_1 = require("./scratchOrgCache");
24
24
  const scratchOrgErrorCodes_1 = require("./scratchOrgErrorCodes");
25
25
  messages_1.Messages.importMessagesDirectory(__dirname);
26
26
  const messages = messages_1.Messages.load('@salesforce/core', 'scratchOrgCreate', [
27
- 'SourceStatusResetFailureError',
28
27
  'DurationDaysValidationMaxError',
29
28
  'DurationDaysValidationMinError',
30
29
  'RetryNotIntError',
@@ -1,5 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { URL } from 'url';
3
+ export declare function getLoginAudienceCombos(audienceUrl: string, loginUrl: string): [string, string][];
3
4
  export declare class SfdcUrl extends URL {
4
5
  /**
5
6
  * Salesforce URLs
@@ -27,7 +28,7 @@ export declare class SfdcUrl extends URL {
27
28
  /**
28
29
  * Tests whether this url is an internal Salesforce domain
29
30
  *
30
- * @returns {boolean} true if this is a internal domain
31
+ * @returns {boolean} true if this is an internal domain
31
32
  */
32
33
  isInternalUrl(): boolean;
33
34
  /**
@@ -57,6 +58,7 @@ export declare class SfdcUrl extends URL {
57
58
  /**
58
59
  * Tests whether this url is a sandbox url
59
60
  *
61
+ * @Deprecated - identification of a sandbox instance by URL alone is not deterministic
60
62
  * @param createdOrgInstance The Salesforce instance the org was created on. e.g. `cs42`
61
63
  * @returns {boolean}
62
64
  */
@@ -67,12 +69,4 @@ export declare class SfdcUrl extends URL {
67
69
  * @returns {boolean} true if this domain is a lightning domain
68
70
  */
69
71
  isLightningDomain(): boolean;
70
- /**
71
- * Tests whether this url is a sandbox url
72
- * otherwise tries to resolve dns cnames and then look if any is sandbox url
73
- *
74
- * @param createdOrgInstance The Salesforce instance the org was created on. e.g. `cs42`
75
- * @returns {Promise<boolean>} true if this domain resolves to sanbox url
76
- */
77
- private resolvesToSandbox;
78
72
  }
@@ -6,13 +6,37 @@
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
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.SfdcUrl = void 0;
9
+ exports.SfdcUrl = exports.getLoginAudienceCombos = void 0;
10
10
  const url_1 = require("url");
11
11
  const kit_1 = require("@salesforce/kit");
12
12
  const ts_types_1 = require("@salesforce/ts-types");
13
13
  const myDomainResolver_1 = require("../status/myDomainResolver");
14
14
  const logger_1 = require("../logger");
15
15
  const lifecycleEvents_1 = require("../lifecycleEvents");
16
+ function getLoginAudienceCombos(audienceUrl, loginUrl) {
17
+ const filtered = [
18
+ [loginUrl, loginUrl],
19
+ [SfdcUrl.SANDBOX, SfdcUrl.SANDBOX],
20
+ [SfdcUrl.PRODUCTION, SfdcUrl.PRODUCTION],
21
+ [audienceUrl, audienceUrl],
22
+ [audienceUrl, SfdcUrl.PRODUCTION],
23
+ [audienceUrl, SfdcUrl.SANDBOX],
24
+ [loginUrl, audienceUrl],
25
+ [loginUrl, SfdcUrl.PRODUCTION],
26
+ [loginUrl, SfdcUrl.SANDBOX],
27
+ [SfdcUrl.PRODUCTION, audienceUrl],
28
+ [SfdcUrl.SANDBOX, audienceUrl],
29
+ ].filter(([login, audience]) => !((login === SfdcUrl.PRODUCTION && audience === SfdcUrl.SANDBOX) ||
30
+ (login === SfdcUrl.SANDBOX && audience === SfdcUrl.PRODUCTION)));
31
+ const reduced = filtered.reduce((acc, [login, audience]) => {
32
+ const l = new url_1.URL(login);
33
+ const a = new url_1.URL(audience);
34
+ acc.set(`${l.origin}:${a.origin}`, [login, audience]);
35
+ return acc;
36
+ }, new Map());
37
+ return [...reduced.values()];
38
+ }
39
+ exports.getLoginAudienceCombos = getLoginAudienceCombos;
16
40
  class SfdcUrl extends url_1.URL {
17
41
  constructor(input, base) {
18
42
  super(input.toString(), base);
@@ -45,13 +69,6 @@ class SfdcUrl extends url_1.URL {
45
69
  this.logger.debug(`Audience URL overridden by env var SFDX_AUDIENCE_URL=${envVarVal}`);
46
70
  return envVarVal;
47
71
  }
48
- if (this.isInternalUrl()) {
49
- // This is for internal developers when just doing authorize
50
- return this.origin;
51
- }
52
- if (await this.resolvesToSandbox(createdOrgInstance)) {
53
- return SfdcUrl.SANDBOX;
54
- }
55
72
  if ((createdOrgInstance && /^gs1/gi.test(createdOrgInstance)) || /(gs1.my.salesforce.com)/gi.test(this.origin)) {
56
73
  return 'https://gs1.salesforce.com';
57
74
  }
@@ -81,7 +98,7 @@ class SfdcUrl extends url_1.URL {
81
98
  /**
82
99
  * Tests whether this url is an internal Salesforce domain
83
100
  *
84
- * @returns {boolean} true if this is a internal domain
101
+ * @returns {boolean} true if this is an internal domain
85
102
  */
86
103
  isInternalUrl() {
87
104
  const INTERNAL_URL_PARTS = [
@@ -157,9 +174,11 @@ class SfdcUrl extends url_1.URL {
157
174
  /**
158
175
  * Tests whether this url is a sandbox url
159
176
  *
177
+ * @Deprecated - identification of a sandbox instance by URL alone is not deterministic
160
178
  * @param createdOrgInstance The Salesforce instance the org was created on. e.g. `cs42`
161
179
  * @returns {boolean}
162
180
  */
181
+ // TODO: how to get rid of this?
163
182
  isSandboxUrl(createdOrgInstance) {
164
183
  return ((createdOrgInstance && /^cs|s$/gi.test(createdOrgInstance)) ||
165
184
  this.origin.endsWith('sandbox.my.salesforce.mil') ||
@@ -177,25 +196,7 @@ class SfdcUrl extends url_1.URL {
177
196
  * @returns {boolean} true if this domain is a lightning domain
178
197
  */
179
198
  isLightningDomain() {
180
- return /\.lightning\.force\.com/.test(this.origin);
181
- }
182
- /**
183
- * Tests whether this url is a sandbox url
184
- * otherwise tries to resolve dns cnames and then look if any is sandbox url
185
- *
186
- * @param createdOrgInstance The Salesforce instance the org was created on. e.g. `cs42`
187
- * @returns {Promise<boolean>} true if this domain resolves to sanbox url
188
- */
189
- async resolvesToSandbox(createdOrgInstance) {
190
- if (this.isSandboxUrl(createdOrgInstance)) {
191
- return true;
192
- }
193
- const myDomainResolver = await myDomainResolver_1.MyDomainResolver.create({ url: this });
194
- const cnames = await myDomainResolver.getCnames();
195
- return cnames.some((cname) => {
196
- const url = new SfdcUrl(`https://${cname}`);
197
- return url.isSandboxUrl();
198
- });
199
+ return /\.lightning\.force\.com/.test(this.origin) || /\.lightning\.crmforce\.mil/.test(this.origin);
199
200
  }
200
201
  }
201
202
  exports.SfdcUrl = SfdcUrl;
package/messages/core.md CHANGED
@@ -24,6 +24,12 @@ Due to: %s
24
24
 
25
25
  Error authenticating with JWT config due to: %s
26
26
 
27
+ # jwtAuthErrors
28
+
29
+ Error authenticating with JWT.
30
+ Errors encountered:
31
+ %s
32
+
27
33
  # refreshTokenAuthError
28
34
 
29
35
  Error authenticating with the refresh token due to: %s
@@ -55,3 +61,7 @@ Setting aliases must be in the format <key>=<value> but found: [%s].
55
61
 
56
62
  All JSON input must have heads down camelcase keys. E.g., `{ sfdcLoginUrl: "https://login.salesforce.com" }`
57
63
  Found "%s" at %s
64
+
65
+ # missingClientId
66
+
67
+ Client ID is required for JWT authentication.
@@ -2,10 +2,6 @@
2
2
 
3
3
  Org snapshots don’t support one or more options you specified: %s.
4
4
 
5
- # SourceStatusResetFailureError
6
-
7
- Successfully created org with ID: %s and name: %s. Unfortunately, source tracking isn’t working as expected. If you run force:source:status, the results may be incorrect. Try again by creating another scratch org.
8
-
9
5
  # DurationDaysValidationMinError
10
6
 
11
7
  Expected 'durationDays' greater than or equal to %s but received %s.
@@ -9,3 +9,7 @@ You cannot use 'settings' and `'orgPreferences' in your scratch definition file,
9
9
  # DeprecatedPrefFormat
10
10
 
11
11
  We've deprecated OrgPreferences. Update the scratch org definition file to replace OrgPreferences with their corresponding settings.
12
+
13
+ # SourceStatusResetFailureError
14
+
15
+ Successfully created org with ID: %s and name: %s. Unfortunately, source tracking isn’t working as expected. If you run force:source:status, the results may be incorrect. Try again by creating another scratch org.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/core",
3
- "version": "3.19.1",
3
+ "version": "3.19.2",
4
4
  "description": "Core libraries to interact with SFDX projects, orgs, and APIs.",
5
5
  "main": "lib/exported",
6
6
  "types": "lib/exported.d.ts",
@@ -68,14 +68,14 @@
68
68
  "@typescript-eslint/parser": "4.33.0",
69
69
  "chai": "^4.3.4",
70
70
  "commitizen": "^3.1.2",
71
- "eslint": "^6.8.0",
71
+ "eslint": "^7.27.0",
72
72
  "eslint-config-prettier": "^6.15.0",
73
73
  "eslint-config-salesforce": "^0.1.6",
74
74
  "eslint-config-salesforce-license": "^0.1.6",
75
75
  "eslint-config-salesforce-typescript": "^0.2.8",
76
- "eslint-plugin-header": "^3.1.1",
76
+ "eslint-plugin-header": "3.0.0",
77
77
  "eslint-plugin-import": "^2.25.4",
78
- "eslint-plugin-jsdoc": "^27.1.2",
78
+ "eslint-plugin-jsdoc": "^35.1.2",
79
79
  "eslint-plugin-prettier": "^3.1.3",
80
80
  "husky": "^7.0.4",
81
81
  "mocha": "^9.1.3",