-
Notifications
You must be signed in to change notification settings - Fork 254
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Salesforce] Alternative authentication flow (#2023)
* Implementation of a flow where the conventional OAuth refreshToken flow is attempted and the username+password flow is tried if that fails * Throw an error if no authentication method returns rather than returning undefined, fixes build * WIP - generate new request client that is extended with password generated access tokens. No Build * Exports createRequestClient from core and uses it in salesforce * Correctly use login.salesforce url rather than individual instance url when authenticating * Reverts refreshAccessToken to original version. Uses process.env. client ID and secret to pull from chamber * Uses original access_token reference for result of refresh request * References salesforce_client_id/secret rather than the actions_ prefixed verion, which doesn't exist * Adds username+password auth to all actions, including dynamic fields * Removes unused auth methods in the Salesforce class * Uses username+password for refreshAccessToken flow where appropriate, uses security_token field to construct password sent to salesforce, updates descriptions * Unit test for username+password flow * Tests functionality when no security_token is provided as well * Adds a comment
- Loading branch information
Showing
11 changed files
with
213 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,9 @@ | ||
import nock from 'nock' | ||
import createRequestClient from '../../../../../core/src/create-request-client' | ||
import Salesforce from '../sf-operations' | ||
import Salesforce, { authenticateWithPassword } from '../sf-operations' | ||
import { API_VERSION } from '../sf-operations' | ||
import type { GenericPayload } from '../sf-types' | ||
import { Settings } from '../generated-types' | ||
|
||
const settings = { | ||
instanceUrl: 'https://test.salesforce.com/' | ||
|
@@ -771,4 +772,70 @@ describe('Salesforce', () => { | |
) | ||
}) | ||
}) | ||
|
||
describe('Username & Password flow', () => { | ||
const usernamePasswordOnly: Settings = { | ||
username: '[email protected]', | ||
auth_password: 'gary1997', | ||
instanceUrl: 'https://spongebob.salesforce.com/', | ||
isSandbox: false | ||
} | ||
|
||
const usernamePasswordAndToken: Settings = { | ||
username: '[email protected]', | ||
auth_password: 'gary1997', | ||
instanceUrl: 'https://spongebob.salesforce.com/', | ||
isSandbox: false, | ||
security_token: 'abc123' | ||
} | ||
|
||
process.env['SALESFORCE_CLIENT_ID'] = 'id' | ||
process.env['SALESFORCE_CLIENT_SECRET'] = 'secret' | ||
|
||
it('should authenticate using the username and password flow when only the username and password are provided', async () => { | ||
nock('https://login.salesforce.com/services/oauth2/token') | ||
.post('', { | ||
grant_type: 'password', | ||
client_id: 'id', | ||
client_secret: 'secret', | ||
username: usernamePasswordOnly.username, | ||
password: usernamePasswordOnly.auth_password | ||
}) | ||
.reply(201, { | ||
access_token: 'abc' | ||
}) | ||
|
||
const res = await authenticateWithPassword( | ||
usernamePasswordOnly.username as string, // tells typescript that these are defined | ||
usernamePasswordOnly.auth_password as string, | ||
usernamePasswordOnly.security_token, | ||
usernamePasswordOnly.isSandbox | ||
) | ||
|
||
expect(res.accessToken).toEqual('abc') | ||
}) | ||
|
||
it('should authenticate using the username and password flow when the username, password and security token are provided', async () => { | ||
nock('https://login.salesforce.com/services/oauth2/token') | ||
.post('', { | ||
grant_type: 'password', | ||
client_id: 'id', | ||
client_secret: 'secret', | ||
username: usernamePasswordAndToken.username, | ||
password: `${usernamePasswordAndToken.auth_password}${usernamePasswordAndToken.security_token}` | ||
}) | ||
.reply(201, { | ||
access_token: 'abc' | ||
}) | ||
|
||
const res = await authenticateWithPassword( | ||
usernamePasswordAndToken.username as string, // tells typescript that these are defined | ||
usernamePasswordAndToken.auth_password as string, | ||
usernamePasswordAndToken.security_token, | ||
usernamePasswordAndToken.isSandbox | ||
) | ||
|
||
expect(res.accessToken).toEqual('abc') | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
packages/destination-actions/src/destinations/salesforce/generated-types.ts
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import type { DestinationDefinition } from '@segment/actions-core' | ||
import { DestinationDefinition } from '@segment/actions-core' | ||
import type { Settings } from './generated-types' | ||
// This has to be 'cases' because 'case' is a Javascript reserved word | ||
import cases from './cases' | ||
|
@@ -7,6 +7,7 @@ import opportunity from './opportunity' | |
import customObject from './customObject' | ||
import contact from './contact' | ||
import account from './account' | ||
import { authenticateWithPassword } from './sf-operations' | ||
|
||
interface RefreshTokenResponse { | ||
access_token: string | ||
|
@@ -33,9 +34,39 @@ const destination: DestinationDefinition<Settings> = { | |
'Enable to authenticate into a sandbox instance. You can log in to a sandbox by appending the sandbox name to your Salesforce username. For example, if a username for a production org is [email protected] and the sandbox is named test, the username to log in to the sandbox is [email protected]. If you are already authenticated, please disconnect and reconnect with your sandbox username.', | ||
type: 'boolean', | ||
default: false | ||
}, | ||
username: { | ||
label: 'Username', | ||
description: | ||
'The username of the Salesforce account you want to connect to. When all three of username, password, and security token are provided, a username-password flow is used to authenticate. This field is hidden to all users except those who have opted in to the username+password flow.', | ||
type: 'string' | ||
}, | ||
auth_password: { | ||
// auth_ prefix is used because password is a reserved word | ||
label: 'Password', | ||
description: | ||
'The password of the Salesforce account you want to connect to. When all three of username, password, and security token are provided, a username-password flow is used to authenticate. This field is hidden to all users except those who have opted in to the username+password flow.', | ||
type: 'string' | ||
}, | ||
security_token: { | ||
label: 'Security Token', | ||
description: | ||
'The security token of the Salesforce account you want to connect to. When all three of username, password, and security token are provided, a username-password flow is used to authenticate. This value will be appended to the password field to construct the credential used for authentication. This field is hidden to all users except those who have opted in to the username+password flow.', | ||
type: 'string' | ||
} | ||
}, | ||
refreshAccessToken: async (request, { auth, settings }) => { | ||
if (settings.username && settings.auth_password) { | ||
const { accessToken } = await authenticateWithPassword( | ||
settings.username, | ||
settings.auth_password, | ||
settings.security_token, | ||
settings.isSandbox | ||
) | ||
|
||
return { accessToken } | ||
} | ||
|
||
// Return a request that refreshes the access_token if the API supports it | ||
const baseUrl = settings.isSandbox ? 'https://test.salesforce.com' : 'https://login.salesforce.com' | ||
const res = await request<RefreshTokenResponse>(`${baseUrl}/services/oauth2/token`, { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.