fix(slack): update slack config to support refresh token rotation (#1642)

This commit is contained in:
Waleed
2025-10-15 11:54:33 -07:00
committed by GitHub
parent fd67fd220c
commit 4cceb22f21
5 changed files with 72 additions and 58 deletions

View File

@@ -278,7 +278,22 @@ export async function refreshTokenIfNeeded(
logger.info(`[${requestId}] Successfully refreshed access token`)
return { accessToken: refreshedToken, refreshed: true }
} catch (error) {
logger.error(`[${requestId}] Error refreshing token`, error)
logger.warn(
`[${requestId}] Refresh attempt failed, checking if another concurrent request succeeded`
)
const freshCredential = await getCredential(requestId, credentialId, credential.userId)
if (freshCredential?.accessToken) {
const freshExpiresAt = freshCredential.accessTokenExpiresAt
const stillValid = !freshExpiresAt || freshExpiresAt > new Date()
if (stillValid) {
logger.info(`[${requestId}] Found valid token from concurrent refresh, using it`)
return { accessToken: freshCredential.accessToken, refreshed: true }
}
}
logger.error(`[${requestId}] Refresh failed and no valid token found in DB`, error)
throw error
}
}

View File

@@ -48,7 +48,7 @@ export async function GET() {
<rss version="2.0">
<channel>
<title>Sim Changelog</title>
<link>https://sim.dev/changelog</link>
<link>https://sim.ai/changelog</link>
<description>Latest changes, fixes and updates in Sim.</description>
<language>en-us</language>
${items}

View File

@@ -19,6 +19,7 @@ interface ChannelSelectorInputProps {
onChannelSelect?: (channelId: string) => void
isPreview?: boolean
previewValue?: any | null
previewContextValues?: Record<string, any>
}
export function ChannelSelectorInput({
@@ -28,15 +29,18 @@ export function ChannelSelectorInput({
onChannelSelect,
isPreview = false,
previewValue,
previewContextValues,
}: ChannelSelectorInputProps) {
const params = useParams()
const workflowIdFromUrl = (params?.workflowId as string) || ''
// Use the proper hook to get the current value and setter (same as file-selector)
const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlock.id)
// Reactive upstream fields
const [authMethod] = useSubBlockValue(blockId, 'authMethod')
const [botToken] = useSubBlockValue(blockId, 'botToken')
const [connectedCredential] = useSubBlockValue(blockId, 'credential')
const effectiveAuthMethod = previewContextValues?.authMethod ?? authMethod
const effectiveBotToken = previewContextValues?.botToken ?? botToken
const effectiveCredential = previewContextValues?.credential ?? connectedCredential
const [selectedChannelId, setSelectedChannelId] = useState<string>('')
const [_channelInfo, setChannelInfo] = useState<SlackChannelInfo | null>(null)
@@ -49,16 +53,16 @@ export function ChannelSelectorInput({
isPreview,
})
// Choose credential strictly based on auth method
// Choose credential strictly based on auth method - use effective values
const credential: string =
(authMethod as string) === 'bot_token'
? (botToken as string) || ''
: (connectedCredential as string) || ''
(effectiveAuthMethod as string) === 'bot_token'
? (effectiveBotToken as string) || ''
: (effectiveCredential as string) || ''
// Determine if connected OAuth credential is foreign (not applicable for bot tokens)
const { isForeignCredential } = useForeignCredential(
'slack',
(authMethod as string) === 'bot_token' ? '' : (connectedCredential as string) || ''
(effectiveAuthMethod as string) === 'bot_token' ? '' : (effectiveCredential as string) || ''
)
// Get the current value from the store or prop value if in preview mode (same pattern as file-selector)

View File

@@ -1,5 +1,6 @@
import React, { useCallback, useEffect, useState } from 'react'
import { AlertCircle, PlusIcon, Server, WrenchIcon, XIcon } from 'lucide-react'
import type React from 'react'
import { useCallback, useEffect, useState } from 'react'
import { PlusIcon, Server, WrenchIcon, XIcon } from 'lucide-react'
import { useParams } from 'next/navigation'
import { Button } from '@/components/ui/button'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
@@ -374,42 +375,40 @@ function FileUploadSyncWrapper({
)
}
// Error boundary component for tool input
class ToolInputErrorBoundary extends React.Component<
{ children: React.ReactNode; blockName?: string },
{ hasError: boolean; error?: Error }
> {
constructor(props: any) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError(error: Error) {
return { hasError: true, error }
}
componentDidCatch(error: Error, info: React.ErrorInfo) {
console.error('ToolInput error:', error, info)
}
render() {
if (this.state.hasError) {
return (
<div className='rounded-md bg-red-50 p-4 text-red-800 text-sm dark:bg-red-900/20 dark:text-red-200'>
<div className='flex items-center gap-2'>
<AlertCircle className='h-4 w-4' />
<span className='font-medium'>Tool Configuration Error</span>
</div>
<p className='mt-1 text-xs opacity-80'>
{this.props.blockName ? `Block "${this.props.blockName}": ` : ''}
Invalid tool reference. Please check the workflow configuration.
</p>
</div>
)
}
return this.props.children
}
function ChannelSelectorSyncWrapper({
blockId,
paramId,
value,
onChange,
uiComponent,
disabled,
previewContextValues,
}: {
blockId: string
paramId: string
value: string
onChange: (value: string) => void
uiComponent: any
disabled: boolean
previewContextValues?: Record<string, any>
}) {
return (
<GenericSyncWrapper blockId={blockId} paramId={paramId} value={value} onChange={onChange}>
<ChannelSelectorInput
blockId={blockId}
subBlock={{
id: paramId,
type: 'channel-selector' as const,
title: paramId,
provider: uiComponent.provider || 'slack',
placeholder: uiComponent.placeholder,
}}
onChannelSelect={onChange}
disabled={disabled}
previewContextValues={previewContextValues}
/>
</GenericSyncWrapper>
)
}
export function ToolInput({
@@ -1060,19 +1059,14 @@ export function ToolInput({
case 'channel-selector':
return (
<ChannelSelectorInput
<ChannelSelectorSyncWrapper
blockId={blockId}
subBlock={{
id: `tool-${toolIndex || 0}-${param.id}`,
type: 'channel-selector' as const,
title: param.id,
provider: uiComponent.provider || 'slack',
placeholder: uiComponent.placeholder,
}}
onChannelSelect={onChange}
paramId={param.id}
value={value}
onChange={onChange}
uiComponent={uiComponent}
disabled={disabled}
isPreview={true}
previewValue={value}
previewContextValues={currentToolParams as any}
/>
)

View File

@@ -858,6 +858,7 @@ function getProviderAuthConfig(provider: string): ProviderAuthConfig {
clientId,
clientSecret,
useBasicAuth: false,
supportsRefreshTokenRotation: true,
}
}
case 'reddit': {