Skip to content

Commit fcd414e

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(slack): surface selector authentication failures
1 parent 5d9a783 commit fcd414e

2 files changed

Lines changed: 52 additions & 2 deletions

File tree

apps/sim/app/api/tools/slack/server-resolved-selectors.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,4 +218,38 @@ describe('server-resolved Slack selectors', () => {
218218
users: [{ id: 'U111', name: 'bill', real_name: 'Bill' }],
219219
})
220220
})
221+
222+
it.each([
223+
[
224+
'stored credential',
225+
{
226+
ok: true,
227+
accessToken: 'xoxb-stored',
228+
isBotToken: false,
229+
credentialAccess: { credentialType: 'oauth' },
230+
},
231+
{ error: 'Slack authentication failed', authRequired: true },
232+
],
233+
[
234+
'direct token',
235+
{ ok: true, accessToken: 'xoxb-direct', isBotToken: true },
236+
{ error: 'Slack authentication failed' },
237+
],
238+
])('sanitizes invalid_auth for a %s', async (_name, credentialResult, expectedBody) => {
239+
mocks.resolveSlackCredential.mockResolvedValue(credentialResult)
240+
vi.stubGlobal(
241+
'fetch',
242+
vi.fn().mockResolvedValue(Response.json({ ok: false, error: 'invalid_auth' }))
243+
)
244+
245+
const response = await listUsers(
246+
request('/api/tools/slack/users', {
247+
credential: 'credential-or-token',
248+
workflowId: 'workflow-1',
249+
})
250+
)
251+
252+
expect(response.status).toBe(401)
253+
expect(await response.json()).toEqual(expectedBody)
254+
})
221255
})

apps/sim/app/api/tools/slack/users/route.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createLogger } from '@sim/logger'
2+
import { getErrorMessage } from '@sim/utils/errors'
23
import { type NextRequest, NextResponse } from 'next/server'
34
import { slackUsersListOrDetailContract } from '@/lib/api/contracts/selectors/slack'
45
import { parseRequest } from '@/lib/api/server'
@@ -29,6 +30,9 @@ interface SlackUsersResult {
2930
}
3031

3132
export const POST = withRouteHandler(async (request: NextRequest) => {
33+
let providerRequestStarted = false
34+
let reauthorizationAvailable = false
35+
3236
try {
3337
const requestId = generateRequestId()
3438
const authentication = await authenticateSelectorRequest(request)
@@ -64,7 +68,9 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
6468
{ status: resolvedCredential.status }
6569
)
6670
}
67-
const { accessToken, isBotToken } = resolvedCredential
71+
const { accessToken, isBotToken, credentialAccess } = resolvedCredential
72+
reauthorizationAvailable = credentialAccess !== undefined
73+
providerRequestStarted = true
6874

6975
if (userId) {
7076
const userData = await fetchSlackUser(accessToken, userId)
@@ -95,7 +101,17 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
95101
tokenType: isBotToken ? 'bot_token' : 'oauth',
96102
})
97103
return NextResponse.json({ users })
98-
} catch {
104+
} catch (error) {
105+
if (providerRequestStarted && getErrorMessage(error) === 'invalid_auth') {
106+
logger.warn('Slack rejected selector authentication')
107+
return NextResponse.json(
108+
{
109+
error: 'Slack authentication failed',
110+
...(reauthorizationAvailable ? { authRequired: true } : {}),
111+
},
112+
{ status: 401 }
113+
)
114+
}
99115
logger.error('Error processing Slack users request')
100116
return NextResponse.json({ error: 'Failed to retrieve Slack users' }, { status: 500 })
101117
}

0 commit comments

Comments
 (0)