@salesforce/core 4.0.0 → 4.1.0
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/LICENSE.txt +1 -1
- package/README.md +93 -44
- package/lib/config/authInfoConfig.d.ts +19 -0
- package/lib/config/authInfoConfig.js +35 -0
- package/lib/config/config.d.ts +87 -22
- package/lib/config/config.js +117 -65
- package/lib/config/configAggregator.d.ts +41 -35
- package/lib/config/configAggregator.js +102 -73
- package/lib/config/configFile.d.ts +2 -2
- package/lib/config/configFile.js +38 -29
- package/lib/config/configStore.d.ts +9 -9
- package/lib/config/configStore.js +17 -15
- package/lib/config/envVars.d.ts +15 -9
- package/lib/config/envVars.js +71 -47
- package/lib/config/orgUsersConfig.js +2 -0
- package/lib/config/sandboxOrgConfig.js +2 -0
- package/lib/config/sandboxProcessCache.d.ts +16 -0
- package/lib/config/sandboxProcessCache.js +38 -0
- package/lib/config/tokensConfig.d.ts +10 -0
- package/lib/config/tokensConfig.js +29 -0
- package/lib/config/ttlConfig.d.ts +34 -0
- package/lib/config/ttlConfig.js +50 -0
- package/lib/crypto/crypto.js +15 -22
- package/lib/crypto/keyChain.js +2 -3
- package/lib/crypto/keyChainImpl.d.ts +5 -3
- package/lib/crypto/keyChainImpl.js +58 -61
- package/lib/crypto/secureBuffer.d.ts +1 -1
- package/lib/deviceOauthService.d.ts +3 -3
- package/lib/deviceOauthService.js +27 -25
- package/lib/exported.d.ts +15 -12
- package/lib/exported.js +28 -16
- package/lib/global.d.ts +11 -3
- package/lib/global.js +39 -12
- package/lib/lifecycleEvents.d.ts +1 -1
- package/lib/lifecycleEvents.js +3 -0
- package/lib/logger.d.ts +19 -9
- package/lib/logger.js +112 -86
- package/lib/messages.d.ts +53 -36
- package/lib/messages.js +81 -91
- package/lib/org/authInfo.d.ts +56 -20
- package/lib/org/authInfo.js +232 -131
- package/lib/org/authRemover.d.ts +8 -7
- package/lib/org/authRemover.js +32 -28
- package/lib/org/connection.d.ts +13 -37
- package/lib/org/connection.js +78 -124
- package/lib/org/index.js +5 -1
- package/lib/org/org.d.ts +151 -48
- package/lib/org/org.js +468 -225
- package/lib/org/orgConfigProperties.d.ts +64 -3
- package/lib/org/orgConfigProperties.js +96 -4
- package/lib/org/permissionSetAssignment.js +4 -13
- package/lib/org/scratchOrgCache.d.ts +20 -0
- package/lib/org/scratchOrgCache.js +33 -0
- package/lib/org/scratchOrgCreate.d.ts +28 -17
- package/lib/org/scratchOrgCreate.js +125 -53
- package/lib/org/scratchOrgErrorCodes.d.ts +9 -3
- package/lib/org/scratchOrgErrorCodes.js +34 -17
- package/lib/org/scratchOrgFeatureDeprecation.js +1 -6
- package/lib/org/scratchOrgInfoApi.d.ts +21 -47
- package/lib/org/scratchOrgInfoApi.js +129 -63
- package/lib/org/scratchOrgInfoGenerator.d.ts +6 -5
- package/lib/org/scratchOrgInfoGenerator.js +76 -62
- package/lib/org/scratchOrgLifecycleEvents.d.ts +10 -0
- package/lib/org/scratchOrgLifecycleEvents.js +41 -0
- package/lib/org/scratchOrgSettingsGenerator.d.ts +44 -21
- package/lib/org/scratchOrgSettingsGenerator.js +165 -98
- package/lib/org/scratchOrgTypes.d.ts +43 -0
- package/lib/org/scratchOrgTypes.js +9 -0
- package/lib/org/user.d.ts +1 -1
- package/lib/org/user.js +25 -34
- package/lib/schema/printer.d.ts +6 -0
- package/lib/schema/printer.js +34 -31
- package/lib/schema/validator.d.ts +12 -10
- package/lib/schema/validator.js +56 -76
- package/lib/{sfdxError.d.ts → sfError.d.ts} +12 -20
- package/lib/{sfdxError.js → sfError.js} +40 -30
- package/lib/{sfdxProject.d.ts → sfProject.d.ts} +75 -35
- package/lib/sfProject.js +651 -0
- package/lib/stateAggregator/accessors/aliasAccessor.d.ts +129 -0
- package/lib/stateAggregator/accessors/aliasAccessor.js +263 -0
- package/lib/stateAggregator/accessors/orgAccessor.d.ts +101 -0
- package/lib/stateAggregator/accessors/orgAccessor.js +240 -0
- package/lib/stateAggregator/accessors/sandboxAccessor.d.ts +8 -0
- package/lib/stateAggregator/accessors/sandboxAccessor.js +28 -0
- package/lib/stateAggregator/accessors/tokenAccessor.d.ts +63 -0
- package/lib/stateAggregator/accessors/tokenAccessor.js +80 -0
- package/lib/stateAggregator/index.d.ts +4 -0
- package/lib/stateAggregator/index.js +27 -0
- package/lib/stateAggregator/stateAggregator.d.ts +25 -0
- package/lib/stateAggregator/stateAggregator.js +46 -0
- package/lib/status/myDomainResolver.d.ts +1 -1
- package/lib/status/myDomainResolver.js +4 -4
- package/lib/status/pollingClient.js +4 -4
- package/lib/status/streamingClient.d.ts +2 -2
- package/lib/status/streamingClient.js +58 -63
- package/lib/status/types.d.ts +2 -2
- package/lib/testSetup.d.ts +204 -75
- package/lib/testSetup.js +468 -164
- package/lib/util/cache.d.ts +2 -2
- package/lib/util/cache.js +6 -6
- package/lib/util/checkLightningDomain.js +3 -4
- package/lib/util/directoryWriter.d.ts +12 -0
- package/lib/util/directoryWriter.js +54 -0
- package/lib/util/getJwtAudienceUrl.js +1 -1
- package/lib/util/internal.d.ts +28 -2
- package/lib/util/internal.js +65 -8
- package/lib/util/jsonXmlTools.js +2 -4
- package/lib/util/mapKeys.d.ts +9 -9
- package/lib/util/mapKeys.js +13 -9
- package/lib/util/sfdc.d.ts +51 -51
- package/lib/util/sfdc.js +74 -79
- package/lib/util/sfdcUrl.d.ts +5 -19
- package/lib/util/sfdcUrl.js +40 -49
- package/lib/util/structuredWriter.d.ts +9 -0
- package/lib/util/structuredWriter.js +3 -0
- package/lib/util/zipWriter.d.ts +8 -6
- package/lib/util/zipWriter.js +13 -13
- package/lib/webOAuthServer.d.ts +20 -6
- package/lib/webOAuthServer.js +102 -56
- package/messageTransformer/messageTransformer.ts +93 -0
- package/messages/auth.md +9 -1
- package/messages/config.md +42 -6
- package/messages/connection.md +8 -0
- package/messages/core.md +10 -0
- package/messages/envVars.md +37 -3
- package/messages/org.md +21 -1
- package/messages/scratchOrgCreate.md +2 -6
- package/messages/scratchOrgErrorCodes.md +17 -1
- package/messages/scratchOrgInfoApi.md +9 -0
- package/messages/scratchOrgInfoGenerator.md +9 -1
- package/package.json +123 -46
- package/CHANGELOG.md +0 -1244
- package/lib/config/keychainConfig.d.ts +0 -19
- package/lib/config/keychainConfig.js +0 -43
- package/lib/globalInfo/accessors/aliasAccessor.d.ts +0 -83
- package/lib/globalInfo/accessors/aliasAccessor.js +0 -130
- package/lib/globalInfo/accessors/orgAccessor.d.ts +0 -13
- package/lib/globalInfo/accessors/orgAccessor.js +0 -45
- package/lib/globalInfo/accessors/tokenAccessor.d.ts +0 -13
- package/lib/globalInfo/accessors/tokenAccessor.js +0 -35
- package/lib/globalInfo/globalInfoConfig.d.ts +0 -36
- package/lib/globalInfo/globalInfoConfig.js +0 -105
- package/lib/globalInfo/index.d.ts +0 -6
- package/lib/globalInfo/index.js +0 -29
- package/lib/globalInfo/sfdxDataHandler.d.ts +0 -43
- package/lib/globalInfo/sfdxDataHandler.js +0 -217
- package/lib/globalInfo/types.d.ts +0 -39
- package/lib/globalInfo/types.js +0 -10
- package/lib/sfdxProject.js +0 -557
- package/lib/util/fs.d.ts +0 -201
- package/lib/util/fs.js +0 -378
package/lib/org/authInfo.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AuthInfo = exports.DEFAULT_CONNECTED_APP_INFO = void 0;
|
|
4
2
|
/*
|
|
5
3
|
* Copyright (c) 2020, salesforce.com, inc.
|
|
6
4
|
* All rights reserved.
|
|
7
5
|
* Licensed under the BSD 3-Clause license.
|
|
8
6
|
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
9
7
|
*/
|
|
8
|
+
/* eslint-disable class-methods-use-this */
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.AuthInfo = exports.DEFAULT_CONNECTED_APP_INFO = void 0;
|
|
10
11
|
const crypto_1 = require("crypto");
|
|
11
12
|
const path_1 = require("path");
|
|
12
13
|
const os = require("os");
|
|
14
|
+
const fs = require("fs");
|
|
13
15
|
const kit_1 = require("@salesforce/kit");
|
|
14
16
|
const ts_types_1 = require("@salesforce/ts-types");
|
|
15
17
|
const jsforce_1 = require("jsforce");
|
|
@@ -18,40 +20,15 @@ const jwt = require("jsonwebtoken");
|
|
|
18
20
|
const config_1 = require("../config/config");
|
|
19
21
|
const configAggregator_1 = require("../config/configAggregator");
|
|
20
22
|
const logger_1 = require("../logger");
|
|
21
|
-
const
|
|
22
|
-
const fs_1 = require("../util/fs");
|
|
23
|
+
const sfError_1 = require("../sfError");
|
|
23
24
|
const sfdc_1 = require("../util/sfdc");
|
|
24
|
-
const
|
|
25
|
+
const stateAggregator_1 = require("../stateAggregator");
|
|
25
26
|
const messages_1 = require("../messages");
|
|
26
27
|
const sfdcUrl_1 = require("../util/sfdcUrl");
|
|
27
28
|
const connection_1 = require("./connection");
|
|
28
29
|
const orgConfigProperties_1 = require("./orgConfigProperties");
|
|
29
|
-
|
|
30
|
-
const messages = messages_1.Messages
|
|
31
|
-
'authInfoCreationError',
|
|
32
|
-
'authInfoOverwriteError',
|
|
33
|
-
'namedOrgNotFound',
|
|
34
|
-
'orgDataNotAvailableError',
|
|
35
|
-
'orgDataNotAvailableError.actions',
|
|
36
|
-
'refreshTokenAuthError',
|
|
37
|
-
'jwtAuthError',
|
|
38
|
-
'authCodeUsernameRetrievalError',
|
|
39
|
-
'authCodeExchangeError',
|
|
40
|
-
]);
|
|
41
|
-
// Extend OAuth2 to add JWT Bearer Token Flow support.
|
|
42
|
-
class JwtOAuth2 extends jsforce_1.OAuth2 {
|
|
43
|
-
constructor(options) {
|
|
44
|
-
super(options);
|
|
45
|
-
}
|
|
46
|
-
jwtAuthorize(innerToken) {
|
|
47
|
-
// @ts-ignore
|
|
48
|
-
return super._postParams({
|
|
49
|
-
// eslint-disable-next-line camelcase
|
|
50
|
-
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
|
51
|
-
assertion: innerToken,
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
}
|
|
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."]]));
|
|
55
32
|
// parses the id field returned from jsForce oauth2 methods to get
|
|
56
33
|
// user ID and org ID.
|
|
57
34
|
function parseIdUrl(idUrl) {
|
|
@@ -66,15 +43,7 @@ function parseIdUrl(idUrl) {
|
|
|
66
43
|
}
|
|
67
44
|
exports.DEFAULT_CONNECTED_APP_INFO = {
|
|
68
45
|
clientId: 'PlatformCLI',
|
|
69
|
-
|
|
70
|
-
// creates new AuthInfos. Currently that is the auth:* commands which
|
|
71
|
-
// aren't owned by this core library. These values need to be here
|
|
72
|
-
// for any old auth files where the id and secret aren't stored.
|
|
73
|
-
//
|
|
74
|
-
// Ideally, this would be removed at some point in the distant future
|
|
75
|
-
// when all auth files now have the clientId stored in it.
|
|
76
|
-
legacyClientId: 'SalesforceDevelopmentExperience',
|
|
77
|
-
legacyClientSecret: '1384510088588713504',
|
|
46
|
+
clientSecret: '',
|
|
78
47
|
};
|
|
79
48
|
/**
|
|
80
49
|
* Handles persistence and fetching of user authentication information using
|
|
@@ -121,7 +90,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
121
90
|
super(options);
|
|
122
91
|
// Possibly overridden in create
|
|
123
92
|
this.usingAccessToken = false;
|
|
124
|
-
this.options = options
|
|
93
|
+
this.options = options ?? {};
|
|
125
94
|
}
|
|
126
95
|
/**
|
|
127
96
|
* Returns the default instance url
|
|
@@ -129,8 +98,8 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
129
98
|
* @returns {string}
|
|
130
99
|
*/
|
|
131
100
|
static getDefaultInstanceUrl() {
|
|
132
|
-
const configuredInstanceUrl = configAggregator_1.ConfigAggregator.getValue(
|
|
133
|
-
return configuredInstanceUrl
|
|
101
|
+
const configuredInstanceUrl = configAggregator_1.ConfigAggregator.getValue(orgConfigProperties_1.OrgConfigProperties.ORG_INSTANCE_URL)?.value;
|
|
102
|
+
return configuredInstanceUrl ?? sfdcUrl_1.SfdcUrl.PRODUCTION;
|
|
134
103
|
}
|
|
135
104
|
/**
|
|
136
105
|
* Get a list of all authorizations based on auth files stored in the global directory.
|
|
@@ -142,20 +111,21 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
142
111
|
* @returns {Promise<OrgAuthorization[]>}
|
|
143
112
|
*/
|
|
144
113
|
static async listAllAuthorizations(orgAuthFilter = (orgAuth) => !!orgAuth) {
|
|
145
|
-
|
|
146
|
-
const globalInfo = await globalInfo_1.GlobalInfo.getInstance();
|
|
114
|
+
const stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
|
|
147
115
|
const config = (await configAggregator_1.ConfigAggregator.create()).getConfigInfo();
|
|
148
|
-
const orgs =
|
|
116
|
+
const orgs = await stateAggregator.orgs.readAll();
|
|
149
117
|
const final = [];
|
|
150
118
|
for (const org of orgs) {
|
|
151
119
|
const username = (0, ts_types_1.ensureString)(org.username);
|
|
152
|
-
const aliases =
|
|
120
|
+
const aliases = stateAggregator.aliases.getAll(username) ?? undefined;
|
|
153
121
|
// Get a list of configuration values that are set to either the username or one
|
|
154
122
|
// of the aliases
|
|
155
123
|
const configs = config
|
|
156
124
|
.filter((c) => aliases.includes(c.value) || c.value === username)
|
|
157
125
|
.map((c) => c.key);
|
|
158
126
|
try {
|
|
127
|
+
// prevent ConfigFile collision bug
|
|
128
|
+
// eslint-disable-next-line no-await-in-loop
|
|
159
129
|
const authInfo = await AuthInfo.create({ username });
|
|
160
130
|
const { orgId, instanceUrl, devHubUsername, expirationDate, isDevHub } = authInfo.getFields();
|
|
161
131
|
final.push({
|
|
@@ -164,7 +134,9 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
164
134
|
username,
|
|
165
135
|
instanceUrl,
|
|
166
136
|
isScratchOrg: Boolean(devHubUsername),
|
|
167
|
-
isDevHub: isDevHub
|
|
137
|
+
isDevHub: isDevHub ?? false,
|
|
138
|
+
// eslint-disable-next-line no-await-in-loop
|
|
139
|
+
isSandbox: await stateAggregator.sandboxes.hasFile(orgId),
|
|
168
140
|
orgId: orgId,
|
|
169
141
|
accessToken: authInfo.getConnectionOptions().accessToken,
|
|
170
142
|
oauthMethod: authInfo.isJwt() ? 'jwt' : authInfo.isOauth() ? 'web' : 'token',
|
|
@@ -194,7 +166,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
194
166
|
*/
|
|
195
167
|
static async hasAuthentications() {
|
|
196
168
|
try {
|
|
197
|
-
const auths = (await
|
|
169
|
+
const auths = await (await stateAggregator_1.StateAggregator.getInstance()).orgs.list();
|
|
198
170
|
return !(0, kit_1.isEmpty)(auths);
|
|
199
171
|
}
|
|
200
172
|
catch (err) {
|
|
@@ -213,14 +185,14 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
213
185
|
static getAuthorizationUrl(options, oauth2) {
|
|
214
186
|
// Always use a verifier for enhanced security
|
|
215
187
|
options.useVerifier = true;
|
|
216
|
-
const oauth2Verifier = oauth2
|
|
188
|
+
const oauth2Verifier = oauth2 ?? new jsforce_1.OAuth2(options);
|
|
217
189
|
// The state parameter allows the redirectUri callback listener to ignore request
|
|
218
190
|
// that don't contain the state value.
|
|
219
191
|
const params = {
|
|
220
192
|
state: (0, crypto_1.randomBytes)(Math.ceil(6)).toString('hex'),
|
|
221
193
|
prompt: 'login',
|
|
222
194
|
// Default connected app is 'refresh_token api web'
|
|
223
|
-
scope: options.scope
|
|
195
|
+
scope: options.scope ?? kit_1.env.getString('SFDX_AUTH_SCOPES', 'refresh_token api web'),
|
|
224
196
|
};
|
|
225
197
|
return oauth2Verifier.getAuthorizationUrl(params);
|
|
226
198
|
}
|
|
@@ -236,7 +208,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
236
208
|
static parseSfdxAuthUrl(sfdxAuthUrl) {
|
|
237
209
|
const match = sfdxAuthUrl.match(/^force:\/\/([a-zA-Z0-9._-]+):([a-zA-Z0-9._-]*):([a-zA-Z0-9._-]+={0,2})@([a-zA-Z0-9._-]+)/);
|
|
238
210
|
if (!match) {
|
|
239
|
-
throw new
|
|
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');
|
|
240
212
|
}
|
|
241
213
|
const [, clientId, clientSecret, refreshToken, loginUrl] = match;
|
|
242
214
|
return {
|
|
@@ -246,6 +218,64 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
246
218
|
loginUrl: `https://${loginUrl}`,
|
|
247
219
|
};
|
|
248
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
|
+
}
|
|
249
279
|
/**
|
|
250
280
|
* Get the username.
|
|
251
281
|
*/
|
|
@@ -287,11 +317,11 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
287
317
|
async save(authData) {
|
|
288
318
|
this.update(authData);
|
|
289
319
|
const username = (0, ts_types_1.ensure)(this.getUsername());
|
|
290
|
-
if (sfdc_1.
|
|
320
|
+
if ((0, sfdc_1.matchesAccessToken)(username)) {
|
|
291
321
|
this.logger.debug('Username is an accesstoken. Skip saving authinfo to disk.');
|
|
292
322
|
return this;
|
|
293
323
|
}
|
|
294
|
-
await this.
|
|
324
|
+
await this.stateAggregator.orgs.write(username);
|
|
295
325
|
this.logger.info(`Saved auth info for username: ${username}`);
|
|
296
326
|
return this;
|
|
297
327
|
}
|
|
@@ -302,13 +332,10 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
302
332
|
* @param authData Authorization fields to update.
|
|
303
333
|
*/
|
|
304
334
|
update(authData) {
|
|
305
|
-
// todo move into configstore
|
|
306
335
|
if (authData && (0, ts_types_1.isPlainObject)(authData)) {
|
|
307
|
-
this.username = authData.username
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
this.globalInfo.orgs.set(this.getUsername(), mergedFields);
|
|
311
|
-
this.logger.info(`Updated auth info for username: ${this.getUsername()}`);
|
|
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}`);
|
|
312
339
|
}
|
|
313
340
|
return this;
|
|
314
341
|
}
|
|
@@ -341,7 +368,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
341
368
|
// Decrypt a user provided client secret or use the default.
|
|
342
369
|
opts = {
|
|
343
370
|
oauth2: {
|
|
344
|
-
loginUrl: instanceUrl
|
|
371
|
+
loginUrl: instanceUrl ?? sfdcUrl_1.SfdcUrl.PRODUCTION,
|
|
345
372
|
clientId: this.getClientId(),
|
|
346
373
|
redirectUri: this.getRedirectUri(),
|
|
347
374
|
},
|
|
@@ -354,8 +381,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
354
381
|
return opts;
|
|
355
382
|
}
|
|
356
383
|
getClientId() {
|
|
357
|
-
|
|
358
|
-
return ((_a = this.getFields()) === null || _a === void 0 ? void 0 : _a.clientId) || exports.DEFAULT_CONNECTED_APP_INFO.legacyClientId;
|
|
384
|
+
return this.getFields()?.clientId ?? exports.DEFAULT_CONNECTED_APP_INFO.clientId;
|
|
359
385
|
}
|
|
360
386
|
getRedirectUri() {
|
|
361
387
|
return 'http://localhost:1717/OauthRedirect';
|
|
@@ -366,7 +392,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
366
392
|
* @param decrypt Decrypt the fields.
|
|
367
393
|
*/
|
|
368
394
|
getFields(decrypt) {
|
|
369
|
-
return this.
|
|
395
|
+
return this.stateAggregator.orgs.get(this.username, decrypt) ?? {};
|
|
370
396
|
}
|
|
371
397
|
/**
|
|
372
398
|
* Get the org front door (used for web based oauth flows)
|
|
@@ -393,11 +419,37 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
393
419
|
const instanceUrl = (0, ts_types_1.ensure)(decryptedFields.instanceUrl, 'undefined instanceUrl').replace(/^https?:\/\//, '');
|
|
394
420
|
let sfdxAuthUrl = 'force://';
|
|
395
421
|
if (decryptedFields.clientId) {
|
|
396
|
-
sfdxAuthUrl += `${decryptedFields.clientId}:${decryptedFields.clientSecret
|
|
422
|
+
sfdxAuthUrl += `${decryptedFields.clientId}:${decryptedFields.clientSecret ?? ''}:`;
|
|
397
423
|
}
|
|
398
424
|
sfdxAuthUrl += `${(0, ts_types_1.ensure)(decryptedFields.refreshToken, 'undefined refreshToken')}@${instanceUrl}`;
|
|
399
425
|
return sfdxAuthUrl;
|
|
400
426
|
}
|
|
427
|
+
/**
|
|
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
|
+
}
|
|
401
453
|
/**
|
|
402
454
|
* Set the target-env (default) or the target-dev-hub to the alias if
|
|
403
455
|
* it exists otherwise to the username. Method will try to set the local
|
|
@@ -415,8 +467,8 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
415
467
|
config = await config_1.Config.create({ isGlobal: true });
|
|
416
468
|
}
|
|
417
469
|
const username = (0, ts_types_1.ensureString)(this.getUsername());
|
|
418
|
-
const alias = this.
|
|
419
|
-
const value = alias
|
|
470
|
+
const alias = this.stateAggregator.aliases.get(username);
|
|
471
|
+
const value = alias ?? username;
|
|
420
472
|
if (options.org) {
|
|
421
473
|
config.set(orgConfigProperties_1.OrgConfigProperties.TARGET_ORG, value);
|
|
422
474
|
}
|
|
@@ -431,16 +483,16 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
431
483
|
* @param alias alias to set
|
|
432
484
|
*/
|
|
433
485
|
async setAlias(alias) {
|
|
434
|
-
this.
|
|
486
|
+
this.stateAggregator.aliases.set(alias, this.getUsername());
|
|
487
|
+
await this.stateAggregator.aliases.write();
|
|
435
488
|
}
|
|
436
489
|
/**
|
|
437
490
|
* Initializes an instance of the AuthInfo class.
|
|
438
491
|
*/
|
|
439
492
|
async init() {
|
|
440
|
-
|
|
441
|
-
this.globalInfo = await globalInfo_1.GlobalInfo.getInstance();
|
|
493
|
+
this.stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
|
|
442
494
|
const username = this.options.username;
|
|
443
|
-
const authOptions = this.options.oauth2Options
|
|
495
|
+
const authOptions = this.options.oauth2Options ?? this.options.accessTokenOptions;
|
|
444
496
|
// Must specify either username and/or options
|
|
445
497
|
if (!username && !authOptions) {
|
|
446
498
|
throw messages.createError('authInfoCreationError');
|
|
@@ -448,21 +500,21 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
448
500
|
// If a username AND oauth options, ensure an authorization for the username doesn't
|
|
449
501
|
// already exist. Throw if it does so we don't overwrite the authorization.
|
|
450
502
|
if (username && authOptions) {
|
|
451
|
-
|
|
452
|
-
if (authExists) {
|
|
503
|
+
if (await this.stateAggregator.orgs.hasFile(username)) {
|
|
453
504
|
throw messages.createError('authInfoOverwriteError');
|
|
454
505
|
}
|
|
455
506
|
}
|
|
456
|
-
const oauthUsername = username
|
|
507
|
+
const oauthUsername = username ?? authOptions?.username;
|
|
457
508
|
if (oauthUsername) {
|
|
458
509
|
this.username = oauthUsername;
|
|
510
|
+
await this.stateAggregator.orgs.read(oauthUsername, false, false);
|
|
459
511
|
} // Else it will be set in initAuthOptions below.
|
|
460
512
|
// If the username is an access token, use that for auth and don't persist
|
|
461
|
-
if ((0, ts_types_1.isString)(oauthUsername) && sfdc_1.
|
|
513
|
+
if ((0, ts_types_1.isString)(oauthUsername) && (0, sfdc_1.matchesAccessToken)(oauthUsername)) {
|
|
462
514
|
// Need to initAuthOptions the logger and authInfoCrypto since we don't call init()
|
|
463
515
|
this.logger = await logger_1.Logger.child('AuthInfo');
|
|
464
516
|
const aggregator = await configAggregator_1.ConfigAggregator.create();
|
|
465
|
-
const instanceUrl = this.getInstanceUrl(
|
|
517
|
+
const instanceUrl = this.getInstanceUrl(aggregator, authOptions);
|
|
466
518
|
this.update({
|
|
467
519
|
accessToken: oauthUsername,
|
|
468
520
|
instanceUrl,
|
|
@@ -472,16 +524,16 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
472
524
|
this.usingAccessToken = true;
|
|
473
525
|
}
|
|
474
526
|
// If a username with NO oauth options, ensure authorization already exist.
|
|
475
|
-
else if (username && !authOptions && !this.
|
|
527
|
+
else if (username && !authOptions && !(await this.stateAggregator.orgs.exists(username))) {
|
|
476
528
|
throw messages.createError('namedOrgNotFound', [username]);
|
|
477
529
|
}
|
|
478
530
|
else {
|
|
479
531
|
await this.initAuthOptions(authOptions);
|
|
480
532
|
}
|
|
481
533
|
}
|
|
482
|
-
getInstanceUrl(
|
|
483
|
-
const instanceUrl =
|
|
484
|
-
return instanceUrl
|
|
534
|
+
getInstanceUrl(aggregator, options) {
|
|
535
|
+
const instanceUrl = options?.instanceUrl ?? aggregator.getPropertyValue(orgConfigProperties_1.OrgConfigProperties.ORG_INSTANCE_URL);
|
|
536
|
+
return instanceUrl ?? sfdcUrl_1.SfdcUrl.PRODUCTION;
|
|
485
537
|
}
|
|
486
538
|
/**
|
|
487
539
|
* Initialize this AuthInfo instance with the specified options. If options are not provided, initialize it from cache
|
|
@@ -489,7 +541,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
489
541
|
*
|
|
490
542
|
* @param options Options to be used for creating an OAuth2 instance.
|
|
491
543
|
*
|
|
492
|
-
* **Throws** *{@link
|
|
544
|
+
* **Throws** *{@link SfError}{ name: 'NamedOrgNotFoundError' }* Org information does not exist.
|
|
493
545
|
* @returns {Promise<AuthInfo>}
|
|
494
546
|
*/
|
|
495
547
|
async initAuthOptions(options) {
|
|
@@ -501,7 +553,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
501
553
|
if (this.isTokenOptions(options)) {
|
|
502
554
|
authConfig = options;
|
|
503
555
|
const userInfo = await this.retrieveUserInfo((0, ts_types_1.ensureString)(options.instanceUrl), (0, ts_types_1.ensureString)(options.accessToken));
|
|
504
|
-
this.update({ username: userInfo
|
|
556
|
+
this.update({ username: userInfo?.username, orgId: userInfo?.organizationId });
|
|
505
557
|
}
|
|
506
558
|
else {
|
|
507
559
|
if (this.options.parentUsername) {
|
|
@@ -524,30 +576,32 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
524
576
|
options.privateKey = (0, path_1.resolve)(options.privateKeyFile);
|
|
525
577
|
}
|
|
526
578
|
if (options.privateKey) {
|
|
527
|
-
authConfig = await this.
|
|
579
|
+
authConfig = await this.authJwt(options);
|
|
528
580
|
}
|
|
529
581
|
else if (!options.authCode && options.refreshToken) {
|
|
530
582
|
// refresh token flow (from sfdxUrl or OAuth refreshFn)
|
|
531
583
|
authConfig = await this.buildRefreshTokenConfig(options);
|
|
532
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
|
+
}
|
|
533
589
|
else {
|
|
534
|
-
|
|
535
|
-
// authcode exchange / web auth flow
|
|
536
|
-
authConfig = await this.exchangeToken(options, this.options.oauth2);
|
|
537
|
-
}
|
|
538
|
-
else {
|
|
539
|
-
authConfig = await this.exchangeToken(options);
|
|
540
|
-
}
|
|
590
|
+
authConfig = await this.exchangeToken(options);
|
|
541
591
|
}
|
|
542
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);
|
|
543
596
|
// Update the auth fields WITH encryption
|
|
544
597
|
this.update(authConfig);
|
|
545
598
|
}
|
|
546
599
|
return this;
|
|
547
600
|
}
|
|
601
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
548
602
|
async loadDecryptedAuthFromConfig(username) {
|
|
549
603
|
// Fetch from the persisted auth file
|
|
550
|
-
const authInfo = this.
|
|
604
|
+
const authInfo = this.stateAggregator.orgs.get(username, true);
|
|
551
605
|
if (!authInfo) {
|
|
552
606
|
throw messages.createError('namedOrgNotFound', [username]);
|
|
553
607
|
}
|
|
@@ -565,7 +619,6 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
565
619
|
// A callback function for a connection to refresh an access token. This is used
|
|
566
620
|
// both for a JWT connection and an OAuth connection.
|
|
567
621
|
async refreshFn(conn, callback) {
|
|
568
|
-
var _a;
|
|
569
622
|
this.logger.info('Access token has expired. Updating...');
|
|
570
623
|
try {
|
|
571
624
|
const fields = this.getFields(true);
|
|
@@ -575,35 +628,48 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
575
628
|
}
|
|
576
629
|
catch (err) {
|
|
577
630
|
const error = err;
|
|
578
|
-
if (
|
|
631
|
+
if (error?.message?.includes('Data Not Available')) {
|
|
579
632
|
// Set cause to keep original stacktrace
|
|
580
633
|
return await callback(messages.createError('orgDataNotAvailableError', [this.getUsername()], [], error));
|
|
581
634
|
}
|
|
582
635
|
return await callback(error);
|
|
583
636
|
}
|
|
584
637
|
}
|
|
638
|
+
async readJwtKey(keyFile) {
|
|
639
|
+
return fs.promises.readFile(keyFile, 'utf8');
|
|
640
|
+
}
|
|
585
641
|
// Build OAuth config for a JWT auth flow
|
|
586
|
-
async
|
|
587
|
-
|
|
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));
|
|
588
647
|
const { loginUrl = sfdcUrl_1.SfdcUrl.PRODUCTION } = options;
|
|
589
648
|
const url = new sfdcUrl_1.SfdcUrl(loginUrl);
|
|
590
|
-
const createdOrgInstance = (
|
|
649
|
+
const createdOrgInstance = (this.getFields().createdOrgInstance ?? '').trim().toLowerCase();
|
|
591
650
|
const audienceUrl = await url.getJwtAudienceUrl(createdOrgInstance);
|
|
592
|
-
const jwtToken = jwt.sign({
|
|
593
|
-
iss: options.clientId,
|
|
594
|
-
sub: this.getUsername(),
|
|
595
|
-
aud: audienceUrl,
|
|
596
|
-
exp: Date.now() + 300,
|
|
597
|
-
}, privateKeyContents, {
|
|
598
|
-
algorithm: 'RS256',
|
|
599
|
-
});
|
|
600
|
-
const oauth2 = new JwtOAuth2({ loginUrl: options.loginUrl });
|
|
601
651
|
let authFieldsBuilder;
|
|
602
|
-
|
|
603
|
-
|
|
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
|
+
}
|
|
604
669
|
}
|
|
605
|
-
|
|
606
|
-
|
|
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');
|
|
607
673
|
}
|
|
608
674
|
const authFields = {
|
|
609
675
|
accessToken: (0, ts_types_1.asString)(authFieldsBuilder.access_token),
|
|
@@ -620,18 +686,34 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
620
686
|
authFields.instanceUrl = instanceUrl;
|
|
621
687
|
}
|
|
622
688
|
catch (err) {
|
|
623
|
-
this.logger.debug(
|
|
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.`);
|
|
624
692
|
authFields.instanceUrl = options.loginUrl;
|
|
625
693
|
}
|
|
626
694
|
return authFields;
|
|
627
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
|
+
}
|
|
628
710
|
// Build OAuth config for a refresh token auth flow
|
|
629
711
|
async buildRefreshTokenConfig(options) {
|
|
630
712
|
// Ideally, this would be removed at some point in the distant future when all auth files
|
|
631
713
|
// now have the clientId stored in it.
|
|
632
714
|
if (!options.clientId) {
|
|
633
|
-
options.clientId = exports.DEFAULT_CONNECTED_APP_INFO.
|
|
634
|
-
options.clientSecret = exports.DEFAULT_CONNECTED_APP_INFO.
|
|
715
|
+
options.clientId = exports.DEFAULT_CONNECTED_APP_INFO.clientId;
|
|
716
|
+
options.clientSecret = exports.DEFAULT_CONNECTED_APP_INFO.clientSecret;
|
|
635
717
|
}
|
|
636
718
|
if (!options.redirectUri) {
|
|
637
719
|
options.redirectUri = this.getRedirectUri();
|
|
@@ -644,22 +726,20 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
644
726
|
catch (err) {
|
|
645
727
|
throw messages.createError('refreshTokenAuthError', [err.message]);
|
|
646
728
|
}
|
|
729
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
647
730
|
// @ts-ignore
|
|
648
731
|
const { orgId } = parseIdUrl(authFieldsBuilder.id);
|
|
649
732
|
let username = this.getUsername();
|
|
650
733
|
if (!username) {
|
|
651
|
-
// @ts-ignore
|
|
652
734
|
const userInfo = await this.retrieveUserInfo(authFieldsBuilder.instance_url, authFieldsBuilder.access_token);
|
|
653
|
-
username = (0, ts_types_1.ensureString)(userInfo
|
|
735
|
+
username = (0, ts_types_1.ensureString)(userInfo?.username);
|
|
654
736
|
}
|
|
655
737
|
return {
|
|
656
738
|
orgId,
|
|
657
739
|
username,
|
|
658
740
|
accessToken: authFieldsBuilder.access_token,
|
|
659
|
-
// @ts-ignore TODO: need better typings for jsforce
|
|
660
741
|
instanceUrl: authFieldsBuilder.instance_url,
|
|
661
|
-
|
|
662
|
-
loginUrl: options.loginUrl || authFieldsBuilder.instance_url,
|
|
742
|
+
loginUrl: options.loginUrl ?? authFieldsBuilder.instance_url,
|
|
663
743
|
refreshToken: options.refreshToken,
|
|
664
744
|
clientId: options.clientId,
|
|
665
745
|
clientSecret: options.clientSecret,
|
|
@@ -687,24 +767,22 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
687
767
|
catch (err) {
|
|
688
768
|
throw messages.createError('authCodeExchangeError', [err.message]);
|
|
689
769
|
}
|
|
690
|
-
// @ts-ignore TODO: need better typings for jsforce
|
|
691
770
|
const { orgId } = parseIdUrl(authFields.id);
|
|
692
771
|
let username = this.getUsername();
|
|
693
772
|
// Only need to query for the username if it isn't known. For example, a new auth code exchange
|
|
694
773
|
// rather than refreshing a token on an existing connection.
|
|
695
774
|
if (!username) {
|
|
775
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
696
776
|
// @ts-ignore
|
|
697
777
|
const userInfo = await this.retrieveUserInfo(authFields.instance_url, authFields.access_token);
|
|
698
|
-
username = userInfo
|
|
778
|
+
username = userInfo?.username;
|
|
699
779
|
}
|
|
700
780
|
return {
|
|
701
781
|
accessToken: authFields.access_token,
|
|
702
|
-
// @ts-ignore TODO: need better typings for jsforce
|
|
703
782
|
instanceUrl: authFields.instance_url,
|
|
704
783
|
orgId,
|
|
705
784
|
username,
|
|
706
|
-
|
|
707
|
-
loginUrl: options.loginUrl || authFields.instance_url,
|
|
785
|
+
loginUrl: options.loginUrl ?? authFields.instance_url,
|
|
708
786
|
refreshToken: authFields.refresh_token,
|
|
709
787
|
clientId: options.clientId,
|
|
710
788
|
clientSecret: options.clientSecret,
|
|
@@ -717,7 +795,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
717
795
|
const apiVersion = 'v51.0'; // hardcoding to v51.0 just for this call is okay.
|
|
718
796
|
const instance = (0, ts_types_1.ensure)(instanceUrl);
|
|
719
797
|
const baseUrl = new sfdcUrl_1.SfdcUrl(instance);
|
|
720
|
-
const userInfoUrl = `${baseUrl}services/oauth2/userinfo`;
|
|
798
|
+
const userInfoUrl = `${baseUrl.toString()}services/oauth2/userinfo`;
|
|
721
799
|
const headers = Object.assign({ Authorization: `Bearer ${accessToken}` }, connection_1.SFDX_HTTP_HEADERS);
|
|
722
800
|
try {
|
|
723
801
|
this.logger.info(`Sending request for Username after successful auth code exchange to URL: ${userInfoUrl}`);
|
|
@@ -727,7 +805,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
727
805
|
}
|
|
728
806
|
else {
|
|
729
807
|
const userInfoJson = (0, kit_1.parseJsonMap)(response.body);
|
|
730
|
-
const url = `${baseUrl}
|
|
808
|
+
const url = `${baseUrl.toString()}services/data/${apiVersion}/sobjects/User/${userInfoJson.user_id}`;
|
|
731
809
|
this.logger.info(`Sending request for User SObject after successful auth code exchange to URL: ${url}`);
|
|
732
810
|
response = await new transport_1.default().httpRequest({ url, method: 'GET', headers });
|
|
733
811
|
if (response.statusCode >= 400) {
|
|
@@ -751,24 +829,47 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
751
829
|
* @private
|
|
752
830
|
*/
|
|
753
831
|
throwUserGetException(response) {
|
|
754
|
-
var _a;
|
|
755
832
|
let errorMsg = '';
|
|
756
|
-
const bodyAsString =
|
|
833
|
+
const bodyAsString = response.body ?? JSON.stringify({ message: 'UNKNOWN', errorCode: 'UNKNOWN' });
|
|
757
834
|
try {
|
|
758
835
|
const body = (0, kit_1.parseJson)(bodyAsString);
|
|
759
836
|
if ((0, ts_types_1.isArray)(body)) {
|
|
760
|
-
errorMsg = body
|
|
761
|
-
.map((line) => { var _a; return (_a = (0, ts_types_1.getString)(line, 'message')) !== null && _a !== void 0 ? _a : (0, ts_types_1.getString)(line, 'errorCode', 'UNKNOWN'); })
|
|
762
|
-
.join(os.EOL);
|
|
837
|
+
errorMsg = body.map((line) => line.message ?? line.errorCode ?? 'UNKNOWN').join(os.EOL);
|
|
763
838
|
}
|
|
764
839
|
else {
|
|
765
|
-
errorMsg =
|
|
840
|
+
errorMsg = body.message ?? body.errorCode ?? 'UNKNOWN';
|
|
766
841
|
}
|
|
767
842
|
}
|
|
768
843
|
catch (err) {
|
|
769
844
|
errorMsg = `${bodyAsString}`;
|
|
770
845
|
}
|
|
771
|
-
throw new
|
|
846
|
+
throw new sfError_1.SfError(errorMsg);
|
|
847
|
+
}
|
|
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
|
+
}
|
|
772
873
|
}
|
|
773
874
|
}
|
|
774
875
|
exports.AuthInfo = AuthInfo;
|