Skip to content

Commit 9310074

Browse files
committed
fix(integrations): offer an OAuth connect only for a provider id the deployment can complete
The OAuth availability list named each service's primary provider id but not its additional ones, and the workspace directory read an unlisted id as available. Salesforce's sandbox therefore offered an OAuth sign-in on deployments with no Salesforce client. The list now covers every provider id a service signs in through, and the directory offers OAuth only on an explicit yes.
1 parent ba718d8 commit 9310074

6 files changed

Lines changed: 39 additions & 16 deletions

File tree

‎apps/sim/lib/integrations/availability.server.oauth-configured.test.ts‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ import {
44
getIntegrationAvailability,
55
getOAuthServiceAvailability,
66
} from '@/lib/integrations/availability.server'
7+
import { getAllOAuthServices } from '@/lib/oauth'
78

89
setEnv({
910
GITHUB_APP_CLIENT_ID: 'repository-client',
1011
GITHUB_APP_CLIENT_SECRET: 'repository-secret',
1112
GOOGLE_CLIENT_ID: undefined,
1213
GOOGLE_CLIENT_SECRET: undefined,
14+
SALESFORCE_CLIENT_ID: 'salesforce-client',
15+
SALESFORCE_CLIENT_SECRET: 'salesforce-secret',
1316
})
1417
afterAll(resetEnvMock)
1518

@@ -37,6 +40,16 @@ describe('OAuth service availability projection', () => {
3740
])
3841
})
3942

43+
it('judges every provider id a service signs in through, not only its primary one', () => {
44+
const salesforce = getAllOAuthServices().filter(
45+
(service) => service.providerId === 'salesforce'
46+
)
47+
expect(getOAuthServiceAvailability(salesforce)).toEqual([
48+
{ providerId: 'salesforce', available: true },
49+
{ providerId: 'salesforce-sandbox', available: true },
50+
])
51+
})
52+
4053
it('does not expose deployment fields or credentials', () => {
4154
const result = getOAuthServiceAvailability([
4255
{ providerId: 'github-repositories', authType: 'oauth' },

‎apps/sim/lib/integrations/availability.server.ts‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,23 @@ export function getIntegrationAvailability() {
2424
return resolveIntegrationAvailability(env)
2525
}
2626

27-
/** OAuth clients are independent of the authentication method a workflow block exposes. */
27+
/**
28+
* Whether the deployment can complete each provider id an OAuth service signs in through, its
29+
* additional ones (such as Salesforce's sandbox) included. OAuth clients are independent of the
30+
* authentication method a workflow block exposes.
31+
*/
2832
export function getOAuthServiceAvailability(
29-
services: readonly Pick<OAuthServiceMetadata, 'providerId' | 'authType'>[]
33+
services: readonly Pick<
34+
OAuthServiceMetadata,
35+
'providerId' | 'authType' | 'additionalProviderIds'
36+
>[]
3037
): { providerId: string; available: boolean }[] {
3138
return services
3239
.filter((service) => service.authType === 'oauth')
33-
.map((service) => ({
34-
providerId: service.providerId,
35-
available: isOAuthServiceDeploymentAvailable(service.providerId),
40+
.flatMap((service) => [service.providerId, ...(service.additionalProviderIds ?? [])])
41+
.map((providerId) => ({
42+
providerId,
43+
available: isOAuthServiceDeploymentAvailable(providerId),
3644
}))
3745
}
3846

‎apps/sim/platform/lib/data/workspace-integrations.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ async function readWorkspaceApps(workspaceId: string): Promise<WorkspaceAppDetai
6565
return integrationCatalog.flatMap((entry) => {
6666
const app = projectWorkspaceApp(entry, {
6767
access: (blockType) => resolveIntegrationAccess(blockType, policy),
68-
oauthAvailable: (providerId) => oauth.get(providerId.toLowerCase()),
68+
oauthAvailable: (providerId) => oauth.get(providerId.toLowerCase()) === true,
6969
})
7070
return app
7171
? [{ ...app, example: entry.example, skills: entry.skills, templates: entry.templates }]

‎apps/sim/platform/lib/integrations/workspace-catalog.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ const gmail: IntegrationCatalogEntry = {
5959

6060
function verdicts(
6161
access: ReturnType<WorkspaceAppVerdicts['access']>,
62-
oauth?: boolean
62+
oauth = false
6363
): WorkspaceAppVerdicts {
6464
return { access: () => access, oauthAvailable: () => oauth }
6565
}
@@ -83,9 +83,9 @@ describe('projectWorkspaceApp', () => {
8383
})
8484

8585
it('offers only the methods this page can complete, a personal token among them', () => {
86-
const ids = (oauth?: boolean) =>
86+
const ids = (oauth: boolean) =>
8787
projectWorkspaceApp(gmail, verdicts('available', oauth))?.connections.map(({ id }) => id)
88-
expect(ids()).toEqual(['google-email', 'google-service-account'])
88+
expect(ids(true)).toEqual(['google-email', 'google-service-account'])
8989
expect(ids(false)).toEqual(['google-service-account'])
9090
const gitlab: IntegrationCatalogEntry = {
9191
...gmail,

‎apps/sim/platform/lib/integrations/workspace-catalog.ts‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ export interface WorkspaceAppDetail extends WorkspaceApp {
6060
export interface WorkspaceAppVerdicts {
6161
/** The integration access verdict for a block type, as `resolveIntegrationAccess` answers it. */
6262
access: (blockType: string) => 'available' | 'unconfigured' | 'requestable' | 'denied'
63-
/** Whether the deployment can complete an OAuth provider's sign-in; absent reads as available. */
64-
oauthAvailable: (providerId: string) => boolean | undefined
63+
/** Whether the deployment can complete an OAuth provider's sign-in. */
64+
oauthAvailable: (providerId: string) => boolean
6565
}
6666

6767
const ATLASSIAN_SERVICE_ACCOUNT_PROVIDER = 'atlassian-service-account'
@@ -90,8 +90,7 @@ export function projectWorkspaceApp(
9090
? entry.connections.filter(
9191
(connection) =>
9292
CONNECTABLE_KINDS.has(connection.kind) &&
93-
(connection.kind !== 'oauth' ||
94-
verdicts.oauthAvailable(connection.providerId) !== false)
93+
(connection.kind !== 'oauth' || verdicts.oauthAvailable(connection.providerId))
9594
)
9695
: []
9796
return {

‎apps/sim/scripts/test-assistant-actions-e2e.ts‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -843,9 +843,12 @@ try {
843843
await expect(
844844
page.getByRole('region', { name: 'Connected credentials', exact: true }).getByRole('link')
845845
).toHaveCount(0)
846-
// No member method is left and this deployment configures no Salesforce client for the
847-
// workspace, so the page keeps Connect integration only as a disabled control naming why.
848-
await expect(connectIntegration).toBeDisabled()
846+
// No member method is left and this deployment configures no Salesforce OAuth client, for
847+
// production or sandbox, so the workspace offers only its integration-user form.
848+
await expect(connectIntegration).toHaveCount(0)
849+
await expect(
850+
page.getByRole('button', { name: 'Create credential', exact: true })
851+
).toBeEnabled()
849852
await capture('integration-disabled-app-empty')
850853
} finally {
851854
await sql`update credential set managed_oauth_status = 'active' where id = ${nativeCredentialId}`

0 commit comments

Comments
 (0)