Skip to content

Commit bd79ba1

Browse files
committed
fix(credentials): keep the provider-reported name when identity lookup degrades
Box and Salesforce returned early on a missing user id, discarding a `name` or `login` the response did carry and relabeling the credential to the enterprise or host fallback. Only the principal should degrade; the human label still beats an id-derived string. Also notes the Salesforce `openid` scope in the connect help text. The client credentials minter sends no scope parameter — effective scopes come from the customer's Connected App — so without `openid` the userinfo lookup can 403 and the run-as user silently never reaches the audit record.
1 parent 416af7c commit bd79ba1

5 files changed

Lines changed: 25 additions & 7 deletions

File tree

apps/sim/lib/credentials/client-credential-accounts/descriptors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ export const CLIENT_CREDENTIAL_ACCOUNT_DESCRIPTORS: Record<
274274
],
275275
docsUrl: 'https://docs.sim.ai/integrations/salesforce-service-account',
276276
helpText:
277-
'The Connected App must have "Enable Client Credentials Flow" checked with a "Run As" integration user set under Edit Policies — every call executes with that user\'s permissions, and deactivating or freezing the user stops all runs.',
277+
'The Connected App must have "Enable Client Credentials Flow" checked with a "Run As" integration user set under Edit Policies — every call executes with that user\'s permissions, and deactivating or freezing the user stops all runs. Selecting the "openid" scope lets Sim record which run-as user the credential authenticates as; without it the connection still works but the identity is not captured.',
278278
},
279279
[ZOHO_DESK_SERVICE_ACCOUNT_PROVIDER_ID]: {
280280
providerId: ZOHO_DESK_SERVICE_ACCOUNT_PROVIDER_ID,

apps/sim/lib/credentials/client-credential-accounts/minters/box.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ describe('mintBoxServiceAccountToken', () => {
122122
kind: 'lookup_failed',
123123
reason: 'response missing user id',
124124
})
125+
// Only the principal degrades — a name that did come back still beats the
126+
// Enterprise-ID fallback, so the credential does not lose its label.
127+
expect(result.identity?.displayName).toBe('Sim Automation')
125128
})
126129

127130
it('still succeeds when the identity request itself throws', async () => {

apps/sim/lib/credentials/client-credential-accounts/minters/box.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,14 @@ async function fetchBoxServiceAccountIdentity(
7575
accessToken: string,
7676
orgId: string
7777
): Promise<ClientCredentialAccountIdentity> {
78-
const degraded = (reason: string): ClientCredentialAccountIdentity => ({
79-
displayName: `Box enterprise ${orgId}`,
78+
/**
79+
* `label` keeps whatever human name the lookup did return. A response can
80+
* carry `name`/`login` but no `id` — the principal is then unusable, but the
81+
* label still beats the Enterprise-ID fallback, so only the principal
82+
* degrades and the credential does not silently lose its name.
83+
*/
84+
const degraded = (reason: string, label?: string): ClientCredentialAccountIdentity => ({
85+
displayName: label ?? `Box enterprise ${orgId}`,
8086
principal: { kind: 'lookup_failed', reason },
8187
auditMetadata: { boxEnterpriseId: orgId },
8288
storedMetadata: { enterpriseId: orgId },
@@ -105,7 +111,7 @@ async function fetchBoxServiceAccountIdentity(
105111
status: res.status,
106112
enterpriseId: orgId,
107113
})
108-
return degraded('response missing user id')
114+
return degraded('response missing user id', name ?? login)
109115
}
110116
return {
111117
displayName: name ?? login ?? `Box enterprise ${orgId}`,

apps/sim/lib/credentials/client-credential-accounts/minters/salesforce.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,9 @@ describe('mintSalesforceServiceAccountToken', () => {
294294
kind: 'lookup_failed',
295295
reason: 'response missing user_id',
296296
})
297+
// Only the principal degrades — a name that did come back still beats the
298+
// host fallback, so the credential does not lose its label.
299+
expect(result.identity?.displayName).toBe('Integration User')
297300
})
298301

299302
it('ignores a non-Salesforce instance_url and falls back to the validated host', async () => {

apps/sim/lib/credentials/client-credential-accounts/minters/salesforce.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,14 @@ async function fetchSalesforceIdentity(
118118
instanceUrl: string,
119119
host: string
120120
): Promise<ClientCredentialAccountIdentity> {
121-
const degraded = (reason: string): ClientCredentialAccountIdentity => ({
122-
displayName: `Salesforce ${host}`,
121+
/**
122+
* `label` keeps whatever human name userinfo did return. A response can carry
123+
* `name`/`preferred_username` but no `user_id` — the principal is then
124+
* unusable, but the label still beats the host fallback, so only the
125+
* principal degrades and the credential does not silently lose its name.
126+
*/
127+
const degraded = (reason: string, label?: string): ClientCredentialAccountIdentity => ({
128+
displayName: label ?? `Salesforce ${host}`,
123129
principal: { kind: 'lookup_failed', reason },
124130
auditMetadata: { salesforceMyDomainHost: host },
125131
storedMetadata: { myDomainHost: host, instanceUrl },
@@ -155,7 +161,7 @@ async function fetchSalesforceIdentity(
155161
status: res.status,
156162
host,
157163
})
158-
return degraded('response missing user_id')
164+
return degraded('response missing user_id', name ?? username)
159165
}
160166
return {
161167
displayName: name ?? username ?? `Salesforce ${host}`,

0 commit comments

Comments
 (0)