# OAuth v2 installation flow and code exchange

*Slack Developer Study Guide · Design for Security · Last updated October 21, 2026 · https://www.certifyforce.com/study-guide/slack-developer/design-for-security/oauth-v2-installation-flow-and-code-exchange*

How a Slack app gets its tokens: building the v2 authorize URL (scope, user_scope, redirect_uri, state, team), checking state, exchanging the ten-minute code with oauth.v2.access, reading the response, and the Bolt for JavaScript OAuth defaults.

- Certification: Salesforce Slack Developer
- Exam section: Design for Security
- Study guide: https://www.certifyforce.com/study-guide/slack-developer

**Official exam objective:**

> Implement the OAuth installation flow.

## What the exam expects

- Name the three steps an app handles to get a token: request scopes, wait for approval at the Redirect URL, exchange the code.
- Build an authorize URL that puts bot scopes in `scope` and user scopes in `user_scope`, and send a `redirect_uri` that matches on the exchange.
- Reject a callback whose `state` does not match, and exchange the code within its ten-minute life using the client ID and secret.
- Read the `oauth.v2.access` response to find the bot token, the installer's user token and scopes, and where the app was actually installed.
- Configure Bolt for JavaScript OAuth: `clientId`, `clientSecret`, `stateSecret`, `scopes`, `installerOptions` and the default routes.

Key terms: **authorize URL** (`https://slack.com/oauth/v2/authorize`, where the user approves scopes); **Redirect URL** (the HTTPS callback Slack returns the user to with a `code`); **temporary authorization code** (the one-time `code` exchanged for tokens); **`state`** (a per-session value the app sends and checks to detect forged callbacks); **`authed_user`** (the response object holding the installer's user token).

## Key facts

| Fact | Detail | Source |
|---|---|---|
| Authorize endpoint | `https://slack.com/oauth/v2/authorize`; bot scopes in `scope`, user scopes in `user_scope` | [Installing with OAuth](https://docs.slack.dev/authentication/installing-with-oauth/) |
| Code lifetime | The temporary authorization code expires after ten minutes | [Installing with OAuth](https://docs.slack.dev/authentication/installing-with-oauth/) |
| `state` mismatch | Treat the authorization as a forgery | [Installing with OAuth](https://docs.slack.dev/authentication/installing-with-oauth/) |
| Client credentials | Prefer HTTP Basic auth over `client_id`/`client_secret` parameters | [oauth.v2.access](https://docs.slack.dev/reference/methods/oauth.v2.access) |
| `redirect_uri` on exchange | Required if it was sent on authorize; mismatch returns `bad_redirect_uri` | [oauth.v2.access](https://docs.slack.dev/reference/methods/oauth.v2.access) |
| Redirect URL rules | HTTPS only, no `#` anchor; `redirect_uri` may be a subdirectory | [Installing with OAuth](https://docs.slack.dev/authentication/installing-with-oauth/) |
| Installer's user scopes | `authed_user.scope`; top-level `scope` is the bot token's | [Installing with OAuth](https://docs.slack.dev/authentication/installing-with-oauth/) |
| Where it installed | `team.id`, or `enterprise.id` with `is_enterprise_install` | [Installing with OAuth](https://docs.slack.dev/authentication/installing-with-oauth/) |
| `unapproved_scope` | Published app requests a scope not yet approved in review | [Installing with OAuth](https://docs.slack.dev/authentication/installing-with-oauth/) |
| Channel chosen by installer | `incoming-webhook` scope; read `incoming_webhook.url` from the response | [Incoming webhooks](https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks) |
| Bolt default routes | `/slack/install` and `/slack/oauth_redirect` | [Bolt OAuth](https://tools.slack.dev/bolt-js/concepts/authenticating-oauth) |
| Bolt `directInstall` | Default `false`; `true` redirects to the authorize URL without rendering a page | [Bolt OAuth](https://tools.slack.dev/bolt-js/concepts/authenticating-oauth) |

## Check yourself

- Why does Slack make the app exchange a code instead of sending the token to the Redirect URL?
- When must `oauth.v2.access` include `redirect_uri`?
- Which response field lists the user scopes the installer granted?
- Why should a Bolt app keep `stateVerification` at its default?

## Sources

- [Installing via OAuth authorization code flow](https://docs.slack.dev/authentication/installing-with-oauth/)
- [oauth.v2.access method](https://docs.slack.dev/reference/methods/oauth.v2.access)
- [Authenticating with OAuth (Bolt for JavaScript)](https://tools.slack.dev/bolt-js/concepts/authenticating-oauth)
- [Sending messages using incoming webhooks](https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks)
- [Submission guidelines and requirements](https://docs.slack.dev/slack-marketplace/slack-marketplace-app-guidelines-and-requirements/)

## About the full unit

The "Core concepts", "Exam traps" and "Worked scenario" sections of this unit, its 13 linked practice questions, and unlimited timed mock exams are included with the Slack Developer pack (USD $19): https://www.certifyforce.com/practice-exams
