@salesforce/core 8.12.0 → 8.12.1-dev.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.
@@ -7,6 +7,15 @@ import { Org } from './org';
7
7
  * Fields for authorization, org, and local information.
8
8
  */
9
9
  export type AuthFields = {
10
+ apps?: {
11
+ [key: string]: {
12
+ clientId: string;
13
+ clientSecret?: string;
14
+ accessToken: string;
15
+ refreshToken: string;
16
+ oauthFlow: 'web';
17
+ };
18
+ };
10
19
  accessToken?: string;
11
20
  alias?: string;
12
21
  authCode?: string;
@@ -234,8 +243,10 @@ export declare class AuthInfo extends AsyncOptionalCreatable<AuthInfo.Options> {
234
243
  update(authData?: AuthFields): AuthInfo;
235
244
  /**
236
245
  * Get the auth fields (decrypted) needed to make a connection.
246
+ *
247
+ * @param app Name of the CA/ECA associated with the user.
237
248
  */
238
- getConnectionOptions(): ConnectionOptions;
249
+ getConnectionOptions(app?: string): ConnectionOptions;
239
250
  getClientId(): string;
240
251
  getRedirectUri(): string;
241
252
  /**
@@ -343,6 +354,13 @@ export declare namespace AuthInfo {
343
354
  * OAuth options.
344
355
  */
345
356
  oauth2Options?: JwtOAuth2Config;
357
+ apps?: Array<{
358
+ name: string;
359
+ accessToken: string;
360
+ refreshToken: string;
361
+ clientId: string;
362
+ clientSecret?: string;
363
+ }>;
346
364
  /**
347
365
  * Options for the access token auth.
348
366
  */
@@ -445,38 +445,85 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
445
445
  }
446
446
  /**
447
447
  * Get the auth fields (decrypted) needed to make a connection.
448
+ *
449
+ * @param app Name of the CA/ECA associated with the user.
448
450
  */
449
- getConnectionOptions() {
451
+ getConnectionOptions(app) {
450
452
  const decryptedCopy = this.getFields(true);
451
453
  const { accessToken, instanceUrl, loginUrl } = decryptedCopy;
452
- if (this.isAccessTokenFlow()) {
453
- this.logger.info('Returning fields for a connection using access token.');
454
- // Just auth with the accessToken
455
- return { accessToken, instanceUrl, loginUrl };
456
- }
457
- if (this.isJwt()) {
458
- this.logger.info('Returning fields for a connection using JWT config.');
454
+ // return main app auth fields
455
+ if (!app) {
456
+ if (this.isAccessTokenFlow()) {
457
+ this.logger.info('Returning fields for a connection using access token.');
458
+ // Just auth with the accessToken
459
+ return { accessToken, instanceUrl, loginUrl };
460
+ }
461
+ if (this.isJwt()) {
462
+ this.logger.info('Returning fields for a connection using JWT config.');
463
+ return {
464
+ accessToken,
465
+ instanceUrl,
466
+ refreshFn: this.refreshFn.bind(this),
467
+ };
468
+ }
469
+ // @TODO: figure out loginUrl and redirectUri (probably get from config class)
470
+ //
471
+ // redirectUri: org.config.getOauthCallbackUrl()
472
+ // loginUrl: this.fields.instanceUrl || this.config.getAppConfig().sfdcLoginUrl
473
+ this.logger.info('Returning fields for a connection using OAuth config.');
474
+ // Decrypt a user provided client secret or use the default.
459
475
  return {
476
+ oauth2: {
477
+ loginUrl: instanceUrl ?? sfdcUrl_1.SfdcUrl.PRODUCTION,
478
+ clientId: this.getClientId(),
479
+ redirectUri: this.getRedirectUri(),
480
+ },
460
481
  accessToken,
461
482
  instanceUrl,
462
483
  refreshFn: this.refreshFn.bind(this),
463
484
  };
464
485
  }
465
- // @TODO: figure out loginUrl and redirectUri (probably get from config class)
466
- //
467
- // redirectUri: org.config.getOauthCallbackUrl()
468
- // loginUrl: this.fields.instanceUrl || this.config.getAppConfig().sfdcLoginUrl
469
- this.logger.info('Returning fields for a connection using OAuth config.');
470
- // Decrypt a user provided client secret or use the default.
486
+ if (!decryptedCopy.apps) {
487
+ throw new sfError_1.SfError(`${this.username} does not have any apps linked yet.`);
488
+ }
489
+ if (!(app in decryptedCopy.apps)) {
490
+ throw new sfError_1.SfError(`${this.username} does not have a "${app}" app linked yet.`);
491
+ }
492
+ const decryptedApp = decryptedCopy.apps[app];
471
493
  return {
472
494
  oauth2: {
473
495
  loginUrl: instanceUrl ?? sfdcUrl_1.SfdcUrl.PRODUCTION,
474
- clientId: this.getClientId(),
496
+ clientId: decryptedApp.clientId,
475
497
  redirectUri: this.getRedirectUri(),
476
498
  },
477
- accessToken,
499
+ accessToken: decryptedApp.accessToken,
478
500
  instanceUrl,
479
- refreshFn: this.refreshFn.bind(this),
501
+ // Specific refreshFn for AuthInfo's apps.
502
+ //
503
+ // Each app stores the oauth flow used for its initial auth, here we ensure each refresh returns
504
+ // a token, update the auth file with it and send it back to jsforce's through the callback.
505
+ refreshFn: async (_conn, callback) => {
506
+ // This only handles refresh for web flow.
507
+ // When more flows are supported for apps, check the `app.oauthFlow` field to set the appropiate refresh helper.
508
+ const authFields = await this.buildRefreshTokenConfig({
509
+ clientId: decryptedApp.clientId,
510
+ clientSecret: decryptedApp.clientSecret,
511
+ refreshToken: decryptedApp.refreshToken,
512
+ loginUrl: instanceUrl,
513
+ });
514
+ await this.save({
515
+ apps: {
516
+ [app]: {
517
+ accessToken: (0, ts_types_1.ensureString)(authFields.accessToken),
518
+ clientId: decryptedApp.clientId,
519
+ clientSecret: decryptedApp.clientSecret,
520
+ refreshToken: decryptedApp.refreshToken,
521
+ oauthFlow: 'web',
522
+ },
523
+ },
524
+ });
525
+ await callback(null, authFields.accessToken);
526
+ },
480
527
  };
481
528
  }
482
529
  getClientId() {
@@ -184,5 +184,9 @@ export declare namespace Connection {
184
184
  * Additional connection parameters.
185
185
  */
186
186
  connectionOptions?: ConnectionConfig<S>;
187
+ /**
188
+ * App to use for auth info credentials.
189
+ */
190
+ app?: string;
187
191
  };
188
192
  }
@@ -86,7 +86,7 @@ class Connection extends jsforce_node_1.Connection {
86
86
  callOptions: {
87
87
  client: clientId,
88
88
  },
89
- ...options.authInfo.getConnectionOptions(),
89
+ ...options.authInfo.getConnectionOptions(options.app),
90
90
  // this assertion is questionable, but has existed before core7
91
91
  };
92
92
  const conn = new this({ ...options, connectionOptions });
@@ -26,6 +26,8 @@ export declare class WebOAuthServer extends AsyncCreatable<WebOAuthServer.Option
26
26
  private oauth2;
27
27
  private oauthConfig;
28
28
  private oauthError;
29
+ private app?;
30
+ private username?;
29
31
  constructor(options: WebOAuthServer.Options);
30
32
  /**
31
33
  * Returns the configured oauthLocalPort or the WebOAuthServer.DEFAULT_PORT
@@ -80,7 +82,31 @@ export declare class WebOAuthServer extends AsyncCreatable<WebOAuthServer.Option
80
82
  }
81
83
  export declare namespace WebOAuthServer {
82
84
  type Options = {
83
- oauthConfig: JwtOAuth2Config;
85
+ oauthConfig: JwtOAuth2Config & {
86
+ /**
87
+ * OAuth scopes to be requested for the access token.
88
+ *
89
+ * This should be a string with each scope separated by spaces:
90
+ * "refresh_token sfap_api chatbot_api web api"
91
+ *
92
+ * If not specified, all scopes assigned to the connected app are requested.
93
+ */
94
+ scope?: string;
95
+ };
96
+ } | {
97
+ oauthConfig: JwtOAuth2Config & {
98
+ /**
99
+ * OAuth scopes to be requested for the access token.
100
+ *
101
+ * This should be a string with each scope separated by spaces:
102
+ * "refresh_token sfap_api chatbot_api web api"
103
+ *
104
+ * If not specified, all scopes assigned to the connected app are requested.
105
+ */
106
+ scope?: string;
107
+ };
108
+ app: string;
109
+ username: string;
84
110
  };
85
111
  type Request = http.IncomingMessage & {
86
112
  query: {
@@ -73,9 +73,15 @@ class WebOAuthServer extends kit_1.AsyncCreatable {
73
73
  oauth2;
74
74
  oauthConfig;
75
75
  oauthError = new Error('Oauth Error');
76
+ app;
77
+ username;
76
78
  constructor(options) {
77
79
  super(options);
78
80
  this.oauthConfig = options.oauthConfig;
81
+ if ('app' in options) {
82
+ this.app = options.app;
83
+ this.username = options.username;
84
+ }
79
85
  }
80
86
  /**
81
87
  * Returns the configured oauthLocalPort or the WebOAuthServer.DEFAULT_PORT
@@ -113,14 +119,43 @@ class WebOAuthServer extends kit_1.AsyncCreatable {
113
119
  this.executeOauthRequest()
114
120
  .then(async (response) => {
115
121
  try {
116
- const authInfo = await authInfo_1.AuthInfo.create({
117
- oauth2Options: this.oauthConfig,
118
- oauth2: this.oauth2,
119
- });
120
- await authInfo.save();
121
- await this.webServer.handleSuccess(response);
122
- response.end();
123
- resolve(authInfo);
122
+ // Link app to an existing auth file.
123
+ if (this.app) {
124
+ const authInfo = await authInfo_1.AuthInfo.create({
125
+ oauth2Options: this.oauthConfig,
126
+ oauth2: this.oauth2,
127
+ });
128
+ const authFields = authInfo.getFields(true);
129
+ // get user authInfo and save app creds in `apps`
130
+ const userAuthInfo = await authInfo_1.AuthInfo.create({
131
+ username: this.username,
132
+ });
133
+ await userAuthInfo.save({
134
+ apps: {
135
+ [this.app]: {
136
+ clientId: (0, ts_types_1.ensureString)(authFields.clientId),
137
+ clientSecret: authFields.clientSecret,
138
+ accessToken: (0, ts_types_1.ensureString)(authFields.accessToken),
139
+ refreshToken: (0, ts_types_1.ensureString)(authFields.refreshToken),
140
+ oauthFlow: 'web',
141
+ },
142
+ },
143
+ });
144
+ await this.webServer.handleSuccess(response);
145
+ response.end();
146
+ resolve(authInfo);
147
+ }
148
+ else {
149
+ // new auth, create new file.
150
+ const authInfo = await authInfo_1.AuthInfo.create({
151
+ oauth2Options: this.oauthConfig,
152
+ oauth2: this.oauth2,
153
+ });
154
+ await authInfo.save();
155
+ await this.webServer.handleSuccess(response);
156
+ response.end();
157
+ resolve(authInfo);
158
+ }
124
159
  }
125
160
  catch (err) {
126
161
  this.oauthError = err;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/core",
3
- "version": "8.12.0",
3
+ "version": "8.12.1-dev.0",
4
4
  "description": "Core libraries to interact with SFDX projects, orgs, and APIs.",
5
5
  "main": "lib/index",
6
6
  "types": "lib/index.d.ts",