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

如何让Slack斜杠命令请求携带用户Access Token而非仅验证令牌?

实现斜杠命令携带用户Access Token的方案

其实斜杠命令本身并不会直接携带用户的access token——因为Slack发送斜杠命令请求时,是从Slack服务器直接打给你的Express端点,不会带上用户端的token信息。不过我们可以通过关联用户Slack ID和已存储的access token来实现这个需求,具体步骤如下:

1. 授权时存储用户的Slack ID与Access Token

首先,当用户通过passport-slack完成OAuth授权后,你需要把用户的Slack唯一ID(profile.user.id)和对应的access token关联存储到数据库(比如MongoDB、PostgreSQL)或者缓存(比如Redis)里。

示例代码(passport-slack回调):

const SlackStrategy = require('passport-slack').Strategy;

passport.use(new SlackStrategy({
    clientID: process.env.SLACK_CLIENT_ID,
    clientSecret: process.env.SLACK_CLIENT_SECRET,
    callbackURL: "/auth/slack/callback",
    scope: ["commands", "users:read", "offline_access"] // offline_access用于获取refresh token
  },
  async (accessToken, refreshToken, profile, done) => {
    try {
      // 用Slack用户ID查找或创建用户记录,存储token
      const user = await User.findOneAndUpdate(
        { slackUserId: profile.user.id },
        { 
          slackAccessToken: accessToken,
          slackRefreshToken: refreshToken // 存储refresh token用于后续刷新过期的access token
        },
        { upsert: true, new: true }
      );
      done(null, user);
    } catch (err) {
      done(err);
    }
  }
));

2. 处理斜杠命令请求时获取Access Token

当Slack向你的端点发送斜杠命令请求时,请求体里会包含user_id字段(用户在当前Slack团队的唯一ID)。我们可以利用这个ID,从存储中取出对应的access token。

不过第一步必须验证Slack的请求签名,这是Slack强制要求的安全措施,防止恶意伪造请求。

示例代码(Express斜杠命令端点):

const crypto = require('crypto');
const fetch = require('node-fetch');

// 验证Slack请求签名的工具函数
function verifySlackRequest(signature, timestamp, rawBody, signingSecret) {
  // 拒绝5分钟前的请求,防止重放攻击
  const fiveMinutesAgo = Math.floor(Date.now() / 1000) - 300;
  if (timestamp < fiveMinutesAgo) return false;

  const baseString = `v0:${timestamp}:${rawBody}`;
  const hmac = crypto.createHmac('sha256', signingSecret);
  const computedSignature = `v0=${hmac.update(baseString).digest('hex')}`;

  // 用timingSafeEqual避免时序攻击
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(computedSignature));
}

app.post('/slack/commands/my-command', async (req, res) => {
  // 1. 验证请求合法性
  const slackSignature = req.headers['x-slack-signature'];
  const slackTimestamp = req.headers['x-slack-request-timestamp'];
  // 注意:需要配置body-parser才能获取rawBody,比如:
  // app.use(bodyParser.urlencoded({ extended: true, verify: (req, res, buf) => req.rawBody = buf.toString() }));
  const rawBody = req.rawBody;

  if (!verifySlackRequest(slackSignature, slackTimestamp, rawBody, process.env.SLACK_SIGNING_SECRET)) {
    return res.status(403).send('Invalid request signature');
  }

  // 2. 提取用户Slack ID
  const { user_id } = req.body;

  // 3. 从数据库获取对应的access token
  const user = await User.findOne({ slackUserId: user_id });
  if (!user || !user.slackAccessToken) {
    // 用户未授权,引导完成授权
    return res.send(`请先完成账号授权:<${process.env.BASE_URL}/auth/slack|点击这里>`);
  }

  // 4. 现在你可以使用access token调用Slack API了
  try {
    const apiRes = await fetch('https://slack.com/api/users.info', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${user.slackAccessToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ user: user_id })
    });
    const data = await apiRes.json();

    if (data.ok) {
      res.send(`Hi ${data.user.real_name}! 已成功获取你的Access Token`);
    } else {
      // 如果token过期,尝试用refresh token刷新
      if (data.error === 'invalid_auth') {
        // 这里可以添加刷新token的逻辑,更新数据库中的access token
        res.send('你的授权已过期,请重新授权');
      } else {
        res.send('获取用户信息失败');
      }
    }
  } catch (err) {
    res.send('服务器内部错误');
  }
});

关键注意事项

  • 安全第一:一定要验证Slack的请求签名,并且加密存储access token,不要明文保存。
  • Token刷新:如果用户长时间未使用,access token可能会过期,此时可以用之前存储的refresh token去Slack API刷新新的access token(需要授权时请求offline_access scope)。
  • 用户引导:如果用户还未完成OAuth授权,要在斜杠命令的响应中引导用户去授权页面,否则无法获取到access token。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:34:36