先日Slackアプリを開発していて、作成者である自分以外のユーザーでもアプリを介してメッセージを投稿できるようにする必要があったのですが、欲しい情報になかなか辿り着けず小一時間ほど溶かしました。
「slack oauth token」や「slack oauth 認証」などで調べれば欲しい情報が出てくるのですが、「slack ユーザートークン 発行」などのキーワードではSlackアプリ作成方法のような記事しか出ず、公式のドキュメントも少し煩雑で探しづらいと感じたのでこのようなタイトルで記事を残しておきます。
トークン発行までの流れ
トークン発行までのざっくりとした流れは以下のようになります。
-
Slackアプリからワークスペースのユーザーにアクセスするためのリクエスト(認可リクエスト)を送る
-
ユーザーからリクエストが承認されると指定したURL(後述)にGETパラメータ付きでリダイレクトされる
-
受け取ったGETパラメータに含まれている認可コードを利用してSlackのAPIにPOSTするとトークンが発行されレスポンスとして返却される
Slackアプリ側で必要な設定
Slackアプリから認可リクエストを送るために必要な設定をします。
※Slackアプリの作成方法については調べればすぐ出てくるので省略します
Redirect URLs
こちらから作成したアプリのページを開き、左側のメニューにあるOAuth & Permissions
を選択します。
遷移した画面の中ほどにRedirect URLs
という項目があります。
トークン発行までの流れの2で少し触れたように、認可リクエストが承認されるとリダイレクトが発生するのでリダイレクト先のURLをここで設定します。
localhostなど、ローカルで立ち上げたサーバーのURLを設定している記事も見かけましたが、現在はSSL化されたURLでなければ設定できないようになっていたので予め用意する必要があります。
私はCloud functionsを利用しましたがHerokuやCloudflare Workersなどアプリケーションが実行できる環境であれば何でも良いです。
後ほどここで用意したサーバーにSlackのAPIへPOSTする処理をデプロイしていきます。
Scopes
次にRedirect URLs
の下にあるScopes
の設定をします。
Scopes
ではSlackアプリに対してSlack上で実行できる操作の権限を与えます。
Add an OAuth Scope
をクリックすると権限の一覧が表示されるので必要に応じて選択してください。
メッセージを投稿するだけであればchat:write
のみで大丈夫です。
認可リクエストを送る
ここまででアプリ側の設定はできたのでSlackワークスペースとユーザーに対して認可リクエストを送ります。
認可リクエストを送るためには以下のURLに必要なパラメータを付与してアクセスします。
Params | Description |
---|---|
user_scope | Scopes で設定した権限 |
client_id | Slackアプリを作成した時に発行されるID。Basic Informationから確認できる |
redirect_uri | Redirect URLs で設定したURL |
アクセスすると「【アプリ名】 が 【ワークスペース名】ワークスペースにアクセスする権限をリクエストしています」という画面が表示されます。
ページ下部の「許可する」を押下すると認可リクエストが送信されRedirect URLs
で設定したURLにcode
というパラメータ付きでリダイレクトします。
認可コードを元にトークンを発行する
最後にGETパラメータで受け取った認可コードを用いてSlackのAPIにPOSTします。
使用するAPIはこちらです。
必要なパラメータを付与してPOSTすることでトークンが含まれたレスポンスが返却されます。
サンプルとして以下にNode.js(Nest.js)のコードを記載しておきます。
client_id
とclient_secret
の部分は自身の環境に応じて変更してください。
// app.controller.ts
import { Controller, Get, Req } from '@nestjs/common';
import { AppService } from './app.service';
import { Request } from 'express';
import type { OauthV2AccessResponse } from '@slack/web-api';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
async getToken(
@Req() request: Request,
): Promise<OauthV2AccessResponse | string> {
const code = request.query.code as string | undefined;
const error = request.query.error as string | undefined;
const clientId = process.env.client_id; // 認可リクエストを送る際にも使用したClient ID
const clientSecret = process.env.client_secret; // Client IDの下で確認できるClient Secret
return this.appService.getToken(code, error, clientId, clientSecret);
}
}
// app.service.ts
import { Injectable } from '@nestjs/common';
import { WebClient } from '@slack/web-api';
import type { OauthV2AccessResponse } from '@slack/web-api';
@Injectable()
export class AppService {
async getToken(
code: string | undefined,
error: string | undefined,
clientId: string,
clientSecret: string,
): Promise<OauthV2AccessResponse | string> {
if (code) {
const slack = new WebClient();
const result = await slack.oauth.v2.access({
client_id: clientId,
client_secret: clientSecret,
code,
});
return `access_token: ${result.authed_user.access_token}`;
} else if (error) {
return '認証がキャンセルされました。';
}
}
}
まとめ
OAuthについてある程度理解していて、SlackアプリがOAuth認証を利用していることが分かっている人にとっては難しい話ではないのですが、そうでない人にとっては欲しい情報に辿り着くまでに多少労力を費やしそうだなと感じました(私もそのうちの一人でした)。
この記事によって誰かの労力を少しでも減らすことができれば幸いです。