Skip to content

Commit 48d3f61

Browse files
committed
feat: ability to use user-supplied auth
1 parent 7f0f11e commit 48d3f61

File tree

4 files changed

+77
-12
lines changed

4 files changed

+77
-12
lines changed

README.md

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,41 @@ See the [official Pub/Sub API repo](https://github.com/developerforce/pub-sub-ap
88

99
Install the client library with `npm install salesforce-pubsub-api-client`.
1010

11-
Create a `.env` file at the root of the project for configuration. You may use either of these authentication flows:
11+
Create a `.env` file at the root of the project for configuration.
1212

13-
- Username/password authentication
13+
Pick one of these authentication flows and fill the relevant configuration:
14+
15+
- User supplied authentication
16+
- Username/password authentication (recommended for tests)
1417
- OAuth 2.0 client credentials
15-
- OAuth 2.0 JWT Bearer
18+
- OAuth 2.0 JWT Bearer (recommended for production)
1619

17-
> **Warning**<br/>
18-
> Relying on a username/password authentication flow for production is not recommended. Consider switching to JWT auth or similar for extra security.
20+
### User supplied authentication
21+
22+
If you already have a Salesforce client in your app, you can reuse its authentication information. You'll only need this minimalistic configuration:
23+
24+
```properties
25+
SALESFORCE_AUTH_TYPE=user-supplied
26+
27+
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443
28+
```
29+
30+
When connecting to the Pub/Sub API, use the following method instead of the standard `connect()` method:
31+
32+
```js
33+
await client.connectWithAuth(
34+
accessToken,
35+
instanceUrl,
36+
organizationId,
37+
username
38+
);
39+
```
1940

2041
### Username/password flow
2142

43+
> **Warning**<br/>
44+
> Relying on a username/password authentication flow for production is not recommended. Consider switching to JWT auth for extra security.
45+
2246
```properties
2347
SALESFORCE_AUTH_TYPE=username-password
2448
SALESFORCE_LOGIN_URL=https://login.salesforce.com
@@ -40,7 +64,9 @@ SALESFORCE_CLIENT_SECRET=YOUR_CONNECTED_APP_CLIENT_SECRET
4064
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443
4165
```
4266

43-
### OAuth 2.0 JWT Bearer Flow
67+
### OAuth 2.0 JWT bearer flow
68+
69+
This is the most secure authentication option. Recommended for production use.
4470

4571
```properties
4672
SALESFORCE_AUTH_TYPE=oauth-jwt-bearer
@@ -56,7 +82,7 @@ PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443
5682

5783
Here's an example that will get you started quickly. It listens to a single account change event.
5884

59-
1. Activate account change events in **Salesforce Setup > Change Data Capture**.
85+
1. Activate Account change events in **Salesforce Setup > Change Data Capture**.
6086

6187
1. Create a `sample.js` file with this content:
6288

src/auth.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export default class SalesforceAuth {
109109
);
110110
}
111111
const { access_token, instance_url } = await loginResponse.json();
112-
// Get user info
112+
// Get org and user info
113113
const userInfoResponse = await fetch(
114114
`${Configuration.getSfLoginUrl()}/services/oauth2/userinfo`,
115115
{

src/client.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,16 @@ export default class PubSubApiClient {
5454
}
5555

5656
/**
57-
* Connects to the Pub/Sub API
57+
* Authenticates with Salesforce then, connects to the Pub/Sub API
5858
* @returns {Promise<void>} Promise that resolves once the connection is established
5959
*/
6060
async connect() {
61+
if (Configuration.isUserSuppliedAuth()) {
62+
throw new Error(
63+
'You selected user-supplied authentication mode so you cannot use the "connect()" method. Use "connectWithAuth(...)" instead.'
64+
);
65+
}
66+
6167
// Connect to Salesforce to obtain an access token
6268
let conMetadata;
6369
try {
@@ -70,7 +76,32 @@ export default class PubSubApiClient {
7076
cause: error
7177
});
7278
}
79+
return this.#connectToPubSubApi(conMetadata);
80+
}
81+
82+
/**
83+
* Connects to the Pub/Sub API with user-supplied authentication
84+
* @param {string} accessToken
85+
* @param {string} instanceUrl
86+
* @param {string} organizationId
87+
* @param {string} username
88+
* @returns {Promise<void>} Promise that resolves once the connection is established
89+
*/
90+
async connectWithAuth(accessToken, instanceUrl, organizationId, username) {
91+
return this.#connectToPubSubApi({
92+
accessToken,
93+
instanceUrl,
94+
organizationId,
95+
username
96+
});
97+
}
7398

99+
/**
100+
* Connects to the Pub/Sub API
101+
* @param {import('./auth.js').ConnectionMetadata} conMetadata
102+
* @returns {Promise<void>} Promise that resolves once the connection is established
103+
*/
104+
async #connectToPubSubApi(conMetadata) {
74105
// Connect to Pub/Sub API
75106
try {
76107
// Read certificates

src/configuration.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import * as dotenv from 'dotenv';
22
import fs from 'fs';
33

4-
const AUTH_USERNAME_PASSWORD = 'username-password',
4+
const AUTH_USER_SUPPLIED = 'user-supplied',
5+
AUTH_USERNAME_PASSWORD = 'username-password',
56
AUTH_OAUTH_CLIENT_CREDENTIALS = 'oauth-client-credentials',
67
AUTH_OAUTH_JWT_BEARER = 'oauth-jwt-bearer';
78

@@ -12,28 +13,31 @@ export default class Configuration {
1213
// Check mandatory variables
1314
Configuration.#checkMandatoryVariables([
1415
'SALESFORCE_AUTH_TYPE',
15-
'SALESFORCE_LOGIN_URL',
1616
'PUB_SUB_ENDPOINT'
1717
]);
18+
// Check variable for specific auth types
1819
if (Configuration.isUsernamePasswordAuth()) {
1920
Configuration.#checkMandatoryVariables([
21+
'SALESFORCE_LOGIN_URL',
2022
'SALESFORCE_USERNAME',
2123
'SALESFORCE_PASSWORD',
2224
'SALESFORCE_TOKEN'
2325
]);
2426
} else if (Configuration.isOAuthClientCredentialsAuth()) {
2527
Configuration.#checkMandatoryVariables([
28+
'SALESFORCE_LOGIN_URL',
2629
'SALESFORCE_CLIENT_ID',
2730
'SALESFORCE_CLIENT_SECRET'
2831
]);
2932
} else if (Configuration.isOAuthJwtBearerAuth()) {
3033
Configuration.#checkMandatoryVariables([
34+
'SALESFORCE_LOGIN_URL',
3135
'SALESFORCE_CLIENT_ID',
3236
'SALESFORCE_USERNAME',
3337
'SALESFORCE_PRIVATE_KEY_FILE'
3438
]);
3539
Configuration.getSfPrivateKey();
36-
} else {
40+
} else if (!Configuration.isUserSuppliedAuth()) {
3741
throw new Error(
3842
`Invalid value for SALESFORCE_AUTH_TYPE environment variable: ${Configuration.getAuthType()}`
3943
);
@@ -79,6 +83,10 @@ export default class Configuration {
7983
return process.env.PUB_SUB_ENDPOINT;
8084
}
8185

86+
static isUserSuppliedAuth() {
87+
return Configuration.getAuthType() === AUTH_USER_SUPPLIED;
88+
}
89+
8290
static isUsernamePasswordAuth() {
8391
return Configuration.getAuthType() === AUTH_USERNAME_PASSWORD;
8492
}

0 commit comments

Comments
 (0)