Slackアプリ(OAuth)をささっと開発し、Slack App ディレクトリに掲載するまでの方法と対策まとめ

Repsona LLCの[@GussieTech](https://twitter.com/GussieTech)です。[「ガントチャートも無料でサクサク便利なプロジェクト管理ツール」Repsona（レプソナ）](https://repsona.com/ja)を開発しています。

クリックだけでできるSlack連携を実装しました。**案外簡単**で、Slack App ディレクトリへの掲載ハードルも**高くなかった**のです。実装や掲載までの方法、ハマったポイントなどを書きたいと思います。同様の機能を検討している方の参考になれば嬉しいです。

## なぜ作ったのか？

Slack連携は、Repsonaユーザーのみなさまからかなり多くのご要望をいただきました。開発後すぐに[Slackコミュニティ](https://note.com/gussietech/n/n932a928ff7ea)でも連携開始しましたが、更新がリアルタイムで確認できるのはかなり便利です。

[Incoming Webhooks](https://slack.com/apps/A0F7XDUAZ--incoming-webhook-)でWebhook URLを払い出してユーザーにコピペしてもらう方式でも同様の連携は実現できるのですが、Repsonaの設定画面から**クリックだけで連携が完了する体感**を作りたかったのです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Repsona♡Slack<br><br>ポチポチっと2-3回クリックするとSlack連携できるようになる予定です。Slack App Directoryに公開申請しにいってきます。 <a href="https://t.co/astuRZyLx0">pic.twitter.com/astuRZyLx0</a></p>&mdash; ガッシー@ひとりでRepsona作る人 (@GussieTech) <a href="https://twitter.com/GussieTech/status/1220196085945102336?ref_src=twsrc%5Etfw">January 23, 2020</a></blockquote>

これを実現するためには、「Slackアプリであること」と「App ディレクトリへの掲載」が必要でした。

## Slackアプリ（OAuth）の仕組み

簡単な流れは下記のような感じです。

- [Your Apps](https://api.slack.com/apps)の画面でSlack上に「アプリ」を作る
- ユーザーに、ワークスペースへのインストールを許可してもらう
- 許可してもらうとアクセストークンがもらえる（OAuth）
- アクセストークンを使うとAPIをコールできるようになる

シーケンス図にするとこんな感じです（これが[公式ドキュメント](https://api.slack.com/authentication/basics)になくて、なかなか流れがイメージできなかった・・ので、初めての方にはかなり参考になるのではないでしょうか）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17680/da83e7f4-c9cf-a600-1034-479134b03509.png)

登場人物は「ユーザー」、「サーバー（ここではRepsona）」、「Slack API」、「Slackアプリ」です。この流れがわかってしまえばもう簡単です。

## Slackアプリの作り方

### Slack上で「アプリ」を作ります

[Create New App](https://api.slack.com/apps)からApp Nameを入力してCreate App。これだけです。
![スクリーンショット 2020-01-29 10.28.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17680/90c0ebcd-b96d-9670-5c15-65ffa35cdef6.png)

アプリ名やアイコンなどを設定をしていきます。わくわくしますね。

![スクリーンショット 2020-01-29 10.36.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17680/675f8794-420b-08c5-53a9-3fb05d4648bb.png)

スコープに、Incoming Webhooksを設定して、なぜ必要なのか（Request Reason）を記載します（App ディレクトリへの申請で必要です）。

![スクリーンショット 2020-01-29 10.38.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17680/106de7d5-8eaf-b0cb-c999-0b123d4f12da.png)

リダイレクトURLを指定します。ここに設定したURLに[マッチしたものしか](https://api.slack.com/docs/oauth#redirect_uris)リダイレクトできません。

![スクリーンショット 2020-01-29 10.40.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17680/ccfedb7a-a8ba-960e-068b-3899390c6936.png)

Save URLs を押さないと反映されないので注意です。

### Slackの認証画面を表示してみる

OAuthを開始するために**パラメータをつけてSlackにリンク**します。

```html
<a href="https://slack.com/oauth/v2/authorize?scope=incoming-webhook&client_id=00000000.000000000000&redirect_uri={redirect_uri}">Slack連携する</a>
```

`scope`には`incoming-webhook`、`client_id`は、Basic Informationに記載されてます。`redirect_uri`にはリダイレクトで戻したいURLをいれます。

![スクリーンショット 2020-01-29 10.55.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17680/9f3b789b-7a60-314f-6ab3-9f18125f2cda.png)

クリックしてアクセスするとインストール許可の画面が出てきます。ここまでくると興奮しますね。

![スクリーンショット 2020-01-29 12.04.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17680/4847a2ce-0997-62d0-178d-18f87b363f09.png)

でもまだサーバーが実装されていないので動きません。

### サーバーを実装します

RepsonaサーバーはNodeで開発していますが、API利用は[SDKが用意されており](https://github.com/slackapi/node-slack-sdk)かなり便利です。以下、SDKを使ったサンプルです。

ユーザーが「許可する」をクリックすると、リダイレクトURLにリダイレクトされてきます。このときワークスペースへのインストールが成功していると`code`という引数がついてきます。

これを使って、以下のようにアクセストークンを受け取り、保持する実装をします（[公式ドキュメント](https://slack.dev/node-slack-sdk/web-api#exchange-an-oauth-grant-for-a-token)）。

```js
const { WebClient } = require('@slack/web-api');

// App credentials found in the Basic Information section of the app configuration
const clientId = process.env.SLACK_CLIENT_ID;
const clientSecret = process.env.SLACK_CLIENT_SECRET;

// Not shown: received an authorization grant called `code`.
(async () => {
  // Create a client instance just to make this single call, and use it for the exchange
  const result = await (new WebClient()).oauth.v2.access({
    client_id: clientId,
    client_secret: clientSecret,
    redirect_uri: redirectUri,
    code: code
  });

  // It's now a good idea to save the access token to your database
  // Some fictitious database
  await db.createAppInstallation(result.team_id, result.enterprise_id, result.access_token, result.bot);
})();
```
`code`に渡ってきた`code`を、`redirect_uri`にはSlackの認証画面に飛ばす時に渡した`redirect_uri`を設定します。これが一致しないと通りません。

成功すると[こんな感じ](https://api.slack.com/messaging/webhooks#2._grab_incoming_webhook_url_from_the_oauth_response)で`アクセストークン`や`チャンネルID`、そして`Webhook URL`が取得できます。

```js
{
    "ok": true,
    "access_token": "xoxp-XXXXXXXX-XXXXXXXX-XXXXX",
    "scope": "identify,bot,commands,incoming-webhook,chat:write:bot",
    "user_id": "XXXXXXXX",
    "team_name": "Your Workspace Name",
    "team_id": "XXXXXXXX",
    "incoming_webhook": {
        "channel": "#channel-it-will-post-to",
        "channel_id": "C05002EAE",
        "configuration_url": "https://workspacename.slack.com/services/BXXXXX",
        "url": "https://hooks.slack.com/TXXXXX/BXXXXX/XXXXXXXXXX"
    }
}
```

これをDBなどに永続化して、この後の連携に利用します。

Webhook URLが取得できればあとは簡単です（[公式ドキュメント](https://api.slack.com/messaging/webhooks#posting_with_webhooks)）。jsonをpostするだけでご指定のチャンネルにメッセージが送れます。Repsonaでは更新があるごとに、多少整形して、Webhook URLにpostする実装を追加しました。


```
POST https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
Content-type: application/json
{
    "text": "Hello, world."
}
```

SDKでやるならこんな感じです（[公式ドキュメント](https://slack.dev/node-slack-sdk/webhook#send-a-notification)）。とっても簡単ですね。

```js
const { IncomingWebhook } = require('@slack/webhook');
const url = process.env.SLACK_WEBHOOK_URL;

const webhook = new IncomingWebhook(url);

// Send the notification
(async () => {
  await webhook.send({
    text: 'I\'ve got news for you...',
  });
})();
```

## Slack App ディレクトリとは？

Slackチームが審査したアプリが掲載されている場所です。ここに掲載されているアプリは、**どのワークスペースでもインストールすることができる**ようになります。

Repsonaは申請からわずか35時間で掲載されました。**Slackチームさん仕事早い！!**
https://slack.com/apps/ASNKGK9U3-repsona

## Slack App ディレクトリに掲載するには？

公式の[Slack App ディレクトリにアプリを提出しよう](https://api.slack.com/lang/ja-jp/submitting-apps-to-the-directory)に全てが書いてあります。

![スクリーンショット 2020-01-29 12.21.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17680/72a5a419-8e47-5b17-d7d1-847c7ba272fc.png)

英語で**47個チェック項目があります**が、苦手な方でもGoogle翻訳などを使えば簡単です。申請する前に確認しておいた方がいいものは下記あたりです。

- トークン等をハードコーディングしていないこと
- スコープの使用目的を記載しているか
- リダイレクトURLはhttpsであるか（テスト用も残しちゃダメ）
- インストールまでの詳細な説明を書いた[ランディングページ](https://repsona.com/slack-integration)
- プライバシーポリシーのページ
- サポートの手段（メールアドレスなど）
- 審査のためのログインアカウントなどが必要であれば用意する


## 苦労話

1. そもそもの流れを掴むのに苦労しました（初歩的ですが、どうやって始めるのか、`code`がどこからくるのかがわからなかったり・・）。

1. 公式ドキュメントは、実装の流れよりも、説明の流れが重視されているので、やりたいことをやるために、かなりいったりきたりしました。[この辺とか](https://api.slack.com/docs/oauth)、[この辺とか](https://slack.dev/node-slack-sdk/web-api#exchange-an-oauth-grant-for-a-token)、[この辺とか](https://api.slack.com/messaging/webhooks)。

1. やりたい連携の仕組みの作り方から、App ディレクトリ申請までを含めた一連の情報がまとまっていなくて、開発できるまで情報を整理するのに苦労しました。


## まとめ

- Slackアプリ作ること自体はとっても簡単
- Slack App ディレクトリの敷居は決して高くない
- Slackの中の人はテンションが高くて好き

苦労話はありましたが、実装は簡単で、Slackチームはスピーディーで、やりたいことは簡単に実現できました。いまは更新情報をWebhookしているだけですが、今後はRepsona内のタスクや情報の追加更新や検索などもできるようにしていきたいと思います。

Slack連携も簡単にできて便利な[「プロジェクト管理と情報共有のためのツール（ガントチャート無料）」Repsona（レプソナ）](https://repsona.com/ja)もぜひお試しください。