-
Notifications
You must be signed in to change notification settings - Fork 655
Description
Summary
Slack interactivity payloads (block_actions from button clicks, including AI App feedback_buttons) sent to the Nango webhook endpoint at POST /webhook/:environmentUuid/slack are never forwarded to the customer’s configured webhook URL. Events API webhooks (JSON, application/json) forwarding works correctly through the same endpoint and integration.
Reproduction
Configure a Slack integration with forward_webhooks enabled (Events API forwarding already works)
Set the Slack app’s Interactivity Request URL to https://api.nango.dev/webhook//slack (same URL as the Events API Request URL)
Trigger a block_actions event (e.g., click a button in a Slack message posted by the app)
Expected: The parsed payload is forwarded to the customer’s webhook URL as a NangoForwardWebhookBody
Actual: Nothing is forwarded. No errors visible in customer-side logs. Slack receives a response (no timeout), but the payload silently disappears.
Verification
Pointing the Interactivity Request URL directly at our backend (bypassing Nango) confirms Slack is sending valid block_actions payloads. The issue is isolated to Nango’s handling/forwarding.
Likely Root Cause
packages/server/lib/webhook/slack-webhook-routing.ts, line 9 — strict content-type equality check:
if (headers['content-type'] === 'application/x-www-form-urlencoded') {
try {
payload = JSON.parse(body['payload'] || body);
} catch {
payload = body;
}
} else {
payload = body; // ← Falls through here if content-type has extra params
}
If Slack sends Content-Type: application/x-www-form-urlencoded; charset=utf-8 (with charset parameter), the strict === check fails. The else branch sets payload = body, where body is the Express-parsed form object { payload: '' } (thanks to express.urlencoded() on routes.public.ts:102). Then line 24:
const teamId = payload['team_id'] || payload['team']['id'];
payload['team'] is undefined → TypeError: Cannot read properties of undefined (reading ‘id’). This is caught by webhook.manager.ts:82-84, which returns Err(...) → statusCode: 500 → forwarding is skipped (forwarding requires statusCode === 200 at line 106).
Suggested Fix
Replace the strict equality with a prefix match:
// Before (line 9):
if (headers['content-type'] === 'application/x-www-form-urlencoded') {
// After:
if (headers['content-type']?.startsWith('application/x-www-form-urlencoded')) {
Alternatively, split on ; and trim:
const contentType = headers['content-type']?.split(';')[0]?.trim();
if (contentType === 'application/x-www-form-urlencoded') {
Secondary Issue: No rawBody for form-encoded requests
routes.public.ts only sets req.rawBody in the express.json() verify callback (line 90-92). express.urlencoded() (line 102) has no verify callback:
publicAPI.use(express.json({
limit: bodyLimit,
verify: (req: Request, _, buf) => { req.rawBody = buf.toString(); } // ← rawBody set here
}));
// ...
publicAPI.use(express.urlencoded({ extended: true, limit: bodyLimit })); // ← No verify, no rawBody
For Slack interactivity requests, req.rawBody is undefined. This isn’t currently causing a crash (the Slack handler doesn’t use rawBody), but it means rawBody passed to routeWebhook() is undefined, which could affect any future use.
Environment
Nango version: 0.69.31
Slack integration with OAuth2, team.id in token_response_metadata
forward_webhooks: true, webhook URL configured
Events API forwarding works correctly for JSON payloads