fix(slack): resolve scope validation and default channel issues

- Add missing 'channels:join' and 'channels:manage' to required OAuth scopes
- Update scope validation to handle comma-separated scope format
- Include defaultChannel in 're_install_required' status response
- Ensures complete integration data is returned in all status cases

Fixes scope validation for existing integrations and missing default channel data in API responses.

Co-Authored-By: Dallin Bentley <dallinbentley98@gmail.com>
This commit is contained in:
Devin AI 2025-07-17 22:33:00 +00:00
parent 623aaf2fb1
commit e115dd28b2
2 changed files with 27 additions and 1 deletions

View File

@ -1,6 +1,8 @@
export const SLACK_OAUTH_SCOPES = [
'app_mentions:read',
'channels:history',
'channels:join',
'channels:manage',
'channels:read',
'chat:write',
'chat:write.public',

View File

@ -8,7 +8,12 @@ import { oauthStateStorage, tokenStorage } from './token-storage';
* Validates if an integration has all required OAuth scopes
*/
function validateScopes(currentScopeString?: string | null): boolean {
const currentScopes = currentScopeString ? currentScopeString.split(' ') : [];
if (!currentScopeString) return false;
const currentScopes = currentScopeString.includes(',')
? currentScopeString.split(',').map(s => s.trim())
: currentScopeString.split(' ').map(s => s.trim());
const requiredScopes = [...SLACK_OAUTH_SCOPES];
return requiredScopes.every((scope) => currentScopes.includes(scope));
}
@ -268,6 +273,19 @@ export class SlackOAuthService {
// Validate scopes
if (!validateScopes(integration.scope)) {
// Cast defaultChannel to the expected type
const defaultChannel = integration.defaultChannel as
| { id: string; name: string }
| Record<string, never>
| null;
// Check if defaultChannel has content
const hasDefaultChannel =
defaultChannel &&
typeof defaultChannel === 'object' &&
'id' in defaultChannel &&
'name' in defaultChannel;
return {
connected: true,
status: 're_install_required',
@ -277,6 +295,12 @@ export class SlackOAuthService {
...(integration.teamDomain != null && { teamDomain: integration.teamDomain }),
installedAt: integration.installedAt || integration.createdAt,
...(integration.lastUsedAt != null && { lastUsedAt: integration.lastUsedAt }),
...(hasDefaultChannel && {
defaultChannel: {
id: defaultChannel.id,
name: defaultChannel.name,
},
}),
defaultSharingPermissions: integration.defaultSharingPermissions,
},
};