Why I write this post

Recently I built a Slack bot to help me search better. For details, checkout: Jessler.

The Jessler MVP implementation is straight-forward: It listens to user’s search commands, parses the search terms, and calls a bunch of APIs, gather results, and send back to the user. It was a fun experience.

But when I look back, the most time consuming part is dealing with Sign in with Slack and Add to Slack buttons. It involves with dealing with Slack OAuth APIs.

Although Slack documentation is up to date, some outdated articles are still talk about how to build legacy Slack bots, and cause confusion.

So I write this post to compare the Slack API, including the URL and JSON response differences.

If you are building a Slack app in 2020, you can use this post as a reference.

Specifically, if you are confused by the terms like:

  • OAuth 2.0
  • Legacy apps (V1)
  • New Slack apps (V2)

Then this post is for you.

Term confusion: OAuth 2.0 vs. Slack API v2.0

They are both called 2.0, but they mean different things.

OAuth 2.0 is the version of OAuth, which even Slack API V1 has support for.

Slack API V2 is simply the next version of Slack API, which also supports OAuth 2.0.

Comparing Slack APIs (V1 vs. V2)

V1 and V2 urls and responses are quite similar. I made mistakes many times in my development process. So it’s important to recognize the difference as early as possible.

Slack documentation briefly mentions:

The response from oauth.v2.access has a slightly different shape than from the previous, non-V2 endpoint.

But it doesn’t show what exactly are the difference. You would find the detailed comparisons in this section.

All snippets below are in Ruby

V2

Add to slack

url: https://slack.com/oauth/v2/authorize?client_id=xxx.xxx&scope=commands,incoming-webhook&user_scope=

callback url:

url = "https://slack.com/api/oauth.v2.access"
HTTParty.get(url, query: { code: params[:code], client_id: "...", client_secret: "...", redirect_uri: "..." }

response:

{"ok"=>true,
 "app_id"=>"A014DHZV90X",
 "authed_user"=>{"id"=>"U014UF952BD"},
 "scope"=>"commands,incoming-webhook",
 "token_type"=>"bot",
 "access_token"=>"xoxb-123-123-123",
 "bot_user_id"=>"U01236847866",
 "team"=>{"id"=>"T2455987456B6", "name"=>"Jessler"},
 "enterprise"=>nil,
 "incoming_webhook"=>
  {"channel"=>"#bot-tests",
   "channel_id"=>"C57887654654798C918",
   "configuration_url"=>"https://jessler.slack.com/services/B06541523DHD5",
   "url"=>
    "https://hooks.slack.com/services/T015321354566/B0132165HD5/caLg735469Z3SrIHvaQCMm"}}

Sign in with slack

button url: https://slack.com/oauth/v2/authorize?user_scope=search:read&client_id=xxx.xxx

callback url:

url = "https://slack.com/api/oauth.v2.access"
HTTParty.get(url, query: { code: params[:code], client_id: "...", client_secret: "...", redirect_uri: "..." }

response

{"ok"=>true,
 "app_id"=>"A0498764631468X",
 "authed_user"=>
  {"id"=>"U01354654BD",
   "scope"=>"search:read",
   "access_token"=>
    "xoxp-1131656849380-11354864387-122424564626-57aba75fd033+6548946478737e68",
   "token_type"=>"user"},
 "team"=>{"id"=>"T014446B6", "name"=>"Jessler"},
 "enterprise"=>nil}

V1 response

Add to slack Add to Slack button url: https://slack.com/oauth/authorize?client_id=11935894985110.112454531&scope=commands,incoming-webhook&user_scope=

Callback url:

url = "https://slack.com/api/oauth.access" # no .v2
HTTParty.get(url, query: { code: params[:code], client_id: "...", client_secret: "...", redirect_uri: "..." }

response:

{"ok"=>true,
 "access_token"=>
  "xoxp-1123165480-1164325465467-1270663826626-57a3216548964789ce6578737e68",
 "scope"=>"identify,commands,incoming-webhook,search:read",
 "user_id"=>"U0321562BD",
 "team_id"=>"T01502154454",
 "enterprise_id"=>nil,
 "team_name"=>"Jessler",
 "incoming_webhook"=>
  {"channel"=>"#bot-tests",
   "channel_id"=>"C02124458",
   "configuration_url"=>"https://jessler.slack.com/services/B321657657468N",
   "url"=>
    "https://hooks.slack.com/services/T01231654654E3HB6/B249878979N/Ggl35165464p7rwl"}}

If you compare the response, you would find the fields are different from V2. So if your bot is running on V1, you can’t simply switch to v2. You need to make changes to the response parsing logic.

Differences Summary

V2 vs. V1: Difference summary

  • Granular permissions (it was simply bot scope before)
  • The response shape is slightly different
  • The request url is different:
    • v1
      • Request: https://slack.com/oauth/authorize
      • Callback: https://slack.com/api/oauth.access
    • v2
      • Request: https://slack.com/oauth/v2/authorize
      • Callback: https://slack.com/api/oauth.v2.access

Personal Experience: Ruby libraries don’t work well with Slack API V2

I tried using omniauth gem, but couldn’t get the redirect callback controller to work properly.

Given that, the redirect logic is simple, I build my own callback controller that does only one thing: Send a GET request to Slack:

url = "https://slack.com/api/oauth.v2.access"
HTTParty.get(url, query: { code: params[:code], client_id: "...", client_secret: "...", redirect_uri: "..." }

Email Newsletter

I write about code and entrepreneurship. Sign up and get my updates straight to your inbox!



Junji Zhi

Senior Software Engineer.