Skip to content

feat(slack): dynamic botToken resolver and custom webhookVerifier#421

Merged
dancer merged 7 commits into
mainfrom
slack-dynamic-bot-token-and-webhook-verifier
Apr 26, 2026
Merged

feat(slack): dynamic botToken resolver and custom webhookVerifier#421
dancer merged 7 commits into
mainfrom
slack-dynamic-bot-token-and-webhook-verifier

Conversation

@dvoytenko
Copy link
Copy Markdown
Member

Summary

  • SlackAdapterConfig.botToken now accepts string | (() => string | Promise<string>). The resolver is invoked per API call, enabling token rotation or lazy fetch from a secret manager. Defaults (env var fallback, single- vs multi-workspace mode detection) are unchanged.
  • New SlackAdapterConfig.webhookVerifier: (request: Request) => string | Promise<string> runs in place of signingSecret when set (and signingSecret is not). It receives the incoming Request and must return the verified raw body text; throwing yields 401 Invalid signature. signingSecret wins if both are configured.
  • Internal: getToken() / withToken() are now async; WebClient is constructed without a static token (every call passes one explicitly). Attachment fetchData snapshots the per-request ctx.token at creation and defers default-provider resolution to fetch time.

Test plan

  • pnpm --filter @chat-adapter/slack typecheck
  • pnpm --filter @chat-adapter/slack test — 360 tests passing, including:
    • 5 new tests under botToken as function (sync resolver, async resolver, per-call rotation, single-workspace auth.test wiring, error propagation)
    • 3 new tests under handleWebhook - webhookVerifier (used in place of signingSecret, throws → 401, signingSecret precedence)
  • pnpm --filter @chat-adapter/integration-tests test src/replay-multi-workspace-slack.test.ts
  • pnpm --filter @chat-adapter/slack build
  • pnpm -w run check packages/adapter-slack/src

🤖 Generated with Claude Code

Allow `botToken` to be a function returning `string | Promise<string>`
so apps can rotate or lazily fetch tokens; the resolver is invoked per
API call. Add `webhookVerifier: (request) => string | Promise<string>`
as an alternative to `signingSecret` for custom request verification —
returns the verified body text or throws to produce a 401.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
chat Ready Ready Preview, Comment, Open in v0 Apr 26, 2026 10:53pm
chat-sdk-nextjs-chat Ready Ready Preview, Comment, Open in v0 Apr 26, 2026 10:53pm

- scheduleMessage cancel(): re-resolve token in single-workspace mode so
  rotation works. Slack rotated tokens have a 12h TTL and scheduled messages
  can outlive their schedule-time token, leaving cancel() with stale auth.
  Multi-workspace still snapshots ctx.token since cancel() runs outside
  the AsyncLocalStorage frame.
- webhookVerifier: when it returns a string, use it as the verified body
  for downstream parsing. JSDoc previously implied this contract; the code
  only checked truthiness.
- webhookVerifier JSDoc: explicit SECURITY note that timestamp/replay
  protection is the implementer's responsibility when bypassing signingSecret.
- Tests: cover Attachment.fetchData snapshot semantics — multi-workspace
  uses the ctx token captured at attachment creation; single-workspace
  re-resolves the default provider per fetch (rotation-safe).
A webhookVerifier passed in config was being silently shadowed by
SLACK_SIGNING_SECRET in the env (read by both createSlackAdapter and the
SlackAdapter constructor). An explicit verifier now opts out of that
fallback in both code paths. Added a regression test that stubs the env
var via vi.stubEnv.
handleReactionEvent does async work (conversations.replies, users.info)
before delegating to chat.processReaction, which is the only point that
registers a waitUntil task. The outer prep work was untracked, so callers
that drained waitUntil tasks could complete before the reaction handler
finished — flaky in CI under tight microtask scheduling. Track the outer
promise too so the full handler is awaited.
@dancer dancer merged commit 2531e9c into main Apr 26, 2026
13 checks passed
@dancer dancer deleted the slack-dynamic-bot-token-and-webhook-verifier branch April 26, 2026 23:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants