You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Node.js和GCP Cloud Function开发Slash命令遇403错误排查

Slack斜杠命令GCP Cloud Function返回403错误排查与解决

问题描述

使用Node.js 18开发Slack斜杠命令,部署到GCP Cloud Function(Gen1/Gen2版本均尝试),触发命令时请求返回403错误。已为函数URL授予allUsers调用权限,且环境变量SLACK_TOKEN已配置,怀疑是Payload解析或Token校验逻辑导致问题。

用户代码如下:

const { WebClient } = require('@slack/web-api');

exports.slashCommand = (req, res) => {
  // Verify that the request is coming from Slack
  if (!req.body.token || req.body.token !== process.env.SLACK_TOKEN) {
    res.status(403).end();
    return;
  }

  // Parse the request body and extract the Slack user ID, command text, and response URL
  const { user_id, text, response_url } = req.body;

  // Use the Slack Web API client to post a message to the response URL
  const web = new WebClient();
  web.chat.postMessage({
    channel: user_id,
    blocks: [
      {
        type: 'section',
        text: {
          type: 'plain_text',
          text: 'This is a section block'
        }
      },
      {
        type: 'divider'
      },
      {
        type: 'section',
        fields: [
          {
            type: 'plain_text',
            text: 'This is a field'
          },
          {
            type: 'plain_text',
            text: 'This is another field'
          }
        ]
      }
    ]
  });

  // Send a 200 OK response to acknowledge receipt of the request
  res.status(200).end();
};

可能的原因及解决步骤

1. Token校验逻辑错误

Slack斜杠命令请求中的token字段是应用的Verification Token,而非Bot Token或OAuth Token。请确认:

  • 登录Slack开发者后台,进入对应应用 → Basic Information → App Credentials,复制Verification Token
  • 对比GCP Cloud Function的环境变量SLACK_TOKEN值,确保完全一致(注意大小写、空格、特殊字符)

2. 请求Body未正确解析

Slack斜杠命令的请求格式为application/x-www-form-urlencoded,默认情况下GCP Cloud Function可能未解析该格式,导致req.body为空或无法读取token字段:

  • Gen1/Gen2通用解法:使用Express中间件解析请求体
const express = require('express');
const { WebClient } = require('@slack/web-api');
const app = express();

// 解析urlencoded格式的请求体
app.use(express.urlencoded({ extended: true }));

app.post('/', (req, res) => {
  if (!req.body.token || req.body.token !== process.env.SLACK_TOKEN) {
    res.status(403).end();
    return;
  }

  const { user_id, text, response_url } = req.body;
  // 后续逻辑...
  res.status(200).end();
});

exports.slashCommand = app;
  • Gen1手动解析:使用querystring模块解析
const querystring = require('querystring');
const { WebClient } = require('@slack/web-api');

exports.slashCommand = (req, res) => {
  // 手动解析urlencoded请求体
  const body = querystring.parse(req.body);
  
  if (!body.token || body.token !== process.env.SLACK_TOKEN) {
    res.status(403).end();
    return;
  }

  const { user_id, text, response_url } = body;
  // 后续逻辑...
  res.status(200).end();
};

3. 改用Slack官方推荐的请求签名校验

Verification Token已被标记为Legacy,Slack推荐使用请求签名校验来验证请求来源,更安全:

  • 安装依赖:npm install @slack/events-api
  • 修改代码实现签名校验:
const { createEventAdapter } = require('@slack/events-api');
const { WebClient } = require('@slack/web-api');

// 从环境变量获取Signing Secret(来自Slack应用后台Basic Information)
const slackSigningSecret = process.env.SLACK_SIGNING_SECRET;
const slackEvents = createEventAdapter(slackSigningSecret);

exports.slashCommand = (req, res) => {
  // 验证请求签名
  slackEvents.verifyRequest(req)
    .then(() => {
      // 签名验证通过,处理请求
      const { user_id, text } = req.body;
      
      const web = new WebClient(process.env.SLACK_BOT_TOKEN);
      web.chat.postMessage({
        channel: user_id,
        blocks: [/* ... 你的块内容 ... */]
      });

      res.status(200).end();
    })
    .catch(() => {
      // 签名验证失败,返回403
      res.status(403).end();
    });
};
  • 在GCP环境变量中配置SLACK_SIGNING_SECRET,值为Slack应用后台的Signing Secret

4. Gen2函数CORS配置问题

如果是Gen2函数,可能需要额外配置CORS允许Slack的请求来源:

  • 在cloudfunctions.yaml中添加CORS配置:
availableMemoryMb: 256
runtime: nodejs18
entryPoint: slashCommand
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/slashCommand
  cors:
    allowedOrigins:
      - "https://slack.com"

额外注意事项

  • Slack要求斜杠命令的处理函数必须在3秒内返回200响应,否则会重试请求。如果后续逻辑耗时较长,建议使用response_url异步发送消息:
// 使用response_url发送消息,无需WebClient
fetch(response_url, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    channel: user_id,
    blocks: [/* ... 你的块内容 ... */]
  })
});
  • 初始化WebClient时必须传入Bot Token,否则web.chat.postMessage会失败:
const web = new WebClient(process.env.SLACK_BOT_TOKEN);

内容的提问来源于stack exchange,提问作者manthysBR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 07:45:29