使用刷新令牌获取机器人访问令牌替换刷新令牌

Using a refresh token to get a bot access token replaces the refresh token

我正在尝试将我的 Slack 应用程序代码转换为在访问 Web 客户端时使用机器人刷新令牌而不是旧的机器人令牌 api。

我已经能够从 Postman 和使用 Slack Webclient node.js 模块调用 https://slack.com/api/oauth.v2.access 端点。

我正在传递我的应用程序的客户端 ID、客户端密码、我的机器人刷新令牌和 refresh_token 的 grant_type。

在这两种情况下,Slack API 都以新的 access_token 响应,但有时我也会得到一个新的 refresh_token 并且我无法使用我的原始刷新令牌来获得更多访问权限令牌。

这似乎不正确;

我已将来自应用程序 OAuth 网页的刷新令牌存储在秘密存储中。通常,您会根据需要使用不会过期的刷新令牌来获取访问令牌。

由于 Slack 也每次都刷新 refresh_token,我无法依赖秘密存储,我需要将此刷新令牌保存在其他地方,这可能是不安全的。

当刷新令牌更改时,它也会使我保留的 installation 对象失效,我需要重新执行“添加到 Slack”过程来修复它。

我是不是做错了什么,比如使用了错误的端点,或者 Slack API“坏了”?

更新和澄清

感谢评论中指出发布新刷新令牌在 OAuth 规范中的人。

这对我来说是个问题的原因是我需要在两个地方使用机器人访问令牌。在其中一个地方,令牌刷新由 Slack 的 Bolt 框架处理。另一方面,我自己处理令牌刷新。

由于刷新访问令牌时会发出新的刷新令牌,因此这两个地方之一不再知道“当前”刷新令牌,因此下次尝试刷新时会失败 访问令牌。哪一个失败取决于哪一个刷新令牌。

应用程序的 Slack OAuth 页面中关于刷新令牌的评论说“永不过期”,但这是一种误导,因为虽然它本身不会过期,但页面中显示的值在以下情况下会变得无效从中生成一个新的访问令牌。

这与其他“永不过期”令牌(例如应用程序客户端密钥)的行为截然不同,只有在您在设置页面中手动重新发布时才会失效。

在令牌轮换之前,Slack 应用程序使用可以在多个地方使用的不会过期的“Bot 令牌”。您似乎无法通过 OAuth 和令牌轮换获得确切的行为。

你没有做错任何事。但是,重新发布刷新令牌是符合规范的。

规范的相关部分是 Section 1.5. Refresh Token。步骤 H 说(强调我的):

(H) The authorization server authenticates the client and validates the refresh token, and if valid, issues a new access token (and, optionally, a new refresh token).

很遗憾,您必须在代码中适应这种行为。不清楚为什么不能更改秘密存储中的值(或者为什么不能将更新的值存储在秘密存储中)。如果您需要一些建议,请随时 post 一些代码

正如@Codebling 和@morganney 所指出的,在 OAuth 规范中,新的刷新令牌与新的访问令牌一起发布。

我的问题出现是因为我需要在我的应用程序的两个独立部分中使用访问令牌。一部分是使用 Slack 的 Bolt 框架,另一部分不是。这会导致潜在的冲突,刷新令牌由我的应用程序的一个或其他部分更新。

解决方案是在我的应用程序的非 Bolt 代码中利用 Slack Bolt InstallProvider(这要求非 Bolt 代码可以访问与 Bolt 代码相同的 InstallationStore 实现).

使用 InstallProvider 可确保如果需要刷新令牌,则始终更新 InstallationStore 并且我的应用程序的两个部分都可以使用一组一致的令牌(刷新和访问)。

在非 Bolt 代码中,我使用以下代码获取访问令牌以调用 Slack Web 客户端:

const installer = new InstallProvider({
          clientId: process.env.SLACK_CLIENT_ID!,
          clientSecret: process.env.SLACK_CLIENT_SECRET!,
          installationStore: new MyInstallationStore(),
          stateSecret: 'someSecret'
        })

const authorizeResult = await installer.authorize({
          isEnterpriseInstall: enterpriseId !== undefined,
          enterpriseId: enterpriseId,
          teamId: teamId
})

if (authorizeResult.botToken) {
    await this.webclient.chat.postMessage({
             channel: slackId,
             blocks: blocks,
             icon_emoji: `:wave:`,
             text: "Welcome message",
             token: authorizeResult.botToken,
    });
}