Skip to content

Redirect to the OAuth state parameter after a Slack install - #48

Closed
johnkors with Copilot wants to merge 3 commits into
mainfrom
copilot/fix-oauth-state-parameter
Closed

johnkors with Copilot wants to merge 3 commits into
mainfrom
copilot/fix-oauth-state-parameter

Conversation

Copilot AI commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

SlackbotCodeTokenExchangeMiddleware never read the state parameter that Slack round-trips through the OAuth flow, so every install ended on the single configured SuccessRedirectUri. An app that can start an install from more than one place (public landing page, admin screen, ...) had no way to send the user back to where they started.

On a successful install the middleware now prefers state as the redirect target, but only when it is a local url — otherwise the callback would be an open redirect.

// install started from an admin screen
// GET /slack/callback?code=...&state=/admin/slack?installed=1
// SuccessRedirectUri = "https://example.com/success"
// -> Location: https://example.com/admin/slack?installed=1

// GET /slack/callback?code=...&state=//evil.example
// -> Location: https://example.com/success

Changes

  • SlackbotCodeTokenExchangeMiddleware: reads state from the callback query and resolves the redirect target. A local state is resolved against SuccessRedirectUri's origin when that option is an absolute http(s) uri (so the user stays on the same site), and used as-is when it is relative. Everything else falls back to SuccessRedirectUri.
  • IsLocalUrl guard: mirrors IUrlHelper.IsLocalUrl semantics — accepts /foo, rejects absolute urls, protocol-relative //host and /\host, anything not starting with a single /, and values containing control characters (a browser strips tabs/newlines, which can turn /\t/host back into a protocol-relative url). Note Uri.TryCreate(..., UriKind.Absolute) is not usable as the rejection check here: on Unix /success parses as an absolute file:// uri.
  • Docs: XML comments on UseSlackbotDistribution and OAuthOptions.SuccessRedirectUri describe the precedence.
  • DistributionRedirectTests: drives the real pipeline (AddSlackbotDistribution + UseSlackbotDistribution) against a stubbed oauth.v2.access response and asserts the Location header for local, absolute, protocol-relative, backslash, non-rooted and empty state values, plus that the workspace still reaches IWorkspaceInstallationHandler.

The issue also floated handing raw state to IWorkspaceInstallationHandler instead; this takes the redirect route since it needs no change to the handler contract, and the two are not mutually exclusive if the hook is wanted later.

Copilot AI and others added 2 commits September 18, 2026 13:04
Co-authored-by: johnkors <206726+johnkors@users.noreply.github.com>
Co-authored-by: johnkors <206726+johnkors@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix distribution middleware to use OAuth state parameter Redirect to the OAuth state parameter after a Slack install Sep 18, 2026
Copilot AI requested a review from johnkors September 18, 2026 13:09
@johnkors johnkors closed this Sep 18, 2026
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.

Distribution middleware drops the OAuth state parameter

2 participants