By Steve Gill
Published: 2020-03-02
このガイドでは、OAuth を使用した Slack アプリの権限付与と配信について説明します。また、アプリに必要なスコープを特定し、OAuth を使用してスコープをリクエストする方法も説明します。
特定のチャンネルに参加するユーザーに対してダイレクトメッセージを送信するアプリを作成しています。Welcome App というアプリです。ワークスペースにインストールされると、 #the-welcome-channel というチャンネルが作成されます (まだない場合)。このチャンネルは、チャンネルに参加したユーザーに対し感謝を示すために使用されますが、チャンネル内でのマナーの説明など、それぞれのユースケースを取り入れることもできます。
このアプリは、Python SDK を使用して Python で作成されています。このガイドではアプリのコードスニペットをシェアしていますが、ソースコード全体は GitHub で参照できます。OAuth のコードと実装は一般的であるため、Python がそれほど得意でない場合でも付いていけるでしょう。
最初に、アプリをインストールする権限がある開発ワークスペースがあることを確認してください。このような開発ワークスペースをまだセットアップしていない場合は、作成してください。アプリをまだ作成していない場合は、新しいアプリを作成する必要があります。では、始めましょう。
スコープは、Web API メソッドの呼び出しや Events API のイベントの受信といった Slack での機能を実行する権限をアプリに付与するために使用します。ユーザーがアプリのインストールフローに従って進むと、ユーザーはアプリがリクエストするスコープへのアクセスを許可する必要があります。
以前は、Slack はさまざまな権限と機能を要求するボットスコープを提供していました。つまりアプリが、必要としない権限をリクエストすることがよくありました。その結果、ユーザーはプライバシーとセキュリティ上の懸念からアプリをインストールしたくないと考えるようになりました。このため現在では、Slack ではアプリがその動作に必要な小さなスコープのみを選択できるようになりました。これにより、以前よりもユーザーがアプリをインストールするようになります。
アプリに必要なスコープを判断するため、アプリが行う処理を詳しく確認する必要があります。個人的には、アプリにとって妥当であると思われるスコープの全体リストを調べる代わりに、アプリに必要なイベントやメソッドを考えながら、スコープを決めていきたいと思います。
conversations.list
を見つけました。これは、パブリックチャンネルとプライベートチャンネルの名前を取得するために使用できます。また、このメソッドを使用するために必要なスコープも判明しました。このケースでは、channels:read
と groups:read
が必要です (ダイレクトメッセージの名前については心配していないので、im:read
と mpim:read
は不要です)。import os
from slack_sdk import WebClient
# "the-welcome-channel" というチャンネルが存在するか確認するメソッド
def channel_exists():
token = os.environ["SLACK_BOT_TOKEN"]
client = WebClient(token=token)
# ワークスペース内のチャンネルを全てリストする
clist = client.conversations_list()
exists = False
for k in clist["channels"]:
# 既存のチャンネルの中に、"the-welcome-channel" を探し出す
if k['name'] == 'the-welcome-channel':
exists = True
break
if exists is False:
# "the-welcome-channel" チャンネルが存在しない場合のチャンネル作成
create_channel()
conversations.create
が目にとまりました。これには channels:manage
というスコープが必要です。# "the-welcome-channel" というチャンネルを作成するメソッド
def create_channel():
token = os.environ["SLACK_BOT_TOKEN"]
client = WebClient(token=token)
resp = client.conversations_create(name="the-welcome-channel")
member_joined_channel
が、必要なイベントです (注: イベントは api.slack.com/apps を使用してアプリの設定に追加する必要があります)。このイベントに必要なスコープは channels:read
と groups:read
(ステップ 1 と同じ) です。次にダイレクトメッセージを送信するため、chat.postMessage
メソッドを使用する必要があります。このメソッドには chat:write
スコープが必要です。# "member_joined_channel" というイベントのリスナー
# メンバーがチャンネルに参加すると DM を送信する
@slack_events_adapter.on("member_joined_channel")
def member_joined_channel(event_data):
user = event_data['event']['user']
token = os.environ["SLACK_BOT_TOKEN"]
client = WebClient(token=token)
msg = 'Welcome! Thanks for joining the-welcome-channel'
client.chat_postMessage(channel=user, text=msg)
したがって、最終的に必要なスコープは channels:read
、groups:read
、channels:manage
、および chat:write
になります。
Slack アプリを初めて開発する方は、OAuth をまだ実装したことがなく、基本的なアプリの設定を利用していたかもしれません。これは、1つのワークスペースのみにアプリをインストールする場合を想定しています。ユーザーがアプリを他のワークスペースにもインストールできるようにする場合や、App ディレクトリからインストールできるようにする場合には、OAuth を実装する必要があります。
API サイトで説明されている、Slack で OAuth を使用する一般的なフローに従います。このフローを次の図に示します。
1. スコープを要求する
最初のステップは、Slack へのリダイレクトまたは**[Add to Slack] ボタンとも呼ばれます。このステップでは、Slack にリダイレクトし、必要なスコープ** (scope)、クライアント ID (client_id)、および状態 (state) のリストをクエリパラメーターとして URL に渡します。クライアント ID は、アプリの Basic Information セクションから取得できます。状態は任意の値ですが、CSRF 攻撃の防止のために推奨されています。
https://slack.com/oauth/v2/authorize?scope=channels:read,groups:read,channels:manage,chat:write&client_id=YOUR_CLIENT_ID&state=STATE_STRING
注: URL に redirect_uri を渡すこともできます。redirect_uri は、ユーザーがアプリに権限を付与した後に、Slack がリクエストの送信先を認識できるようにするために使用します。このガイドの例では、URL にこのパラメーターを渡す代わりに、api.slack.com/apps の OAuth and Permissions セクションでアプリの設定に Redirect URL を追加してください。
次に、上記の URL を使用して Add to Slack ボタンを含むルートをアプリに作成します。
# 環境変数から client_id を保存する
client_id = os.environ["SLACK_CLIENT_ID"]
# CSRF 攻撃を防ぐため、ランダムな文字列を state として使う
from uuid import uuid4
state = str(uuid4())
# OAuth フローを開始するルート
@app.route("/begin_auth", methods=["GET"])
def pre_install():
return f'<a href="https://slack.com/oauth/v2/authorize?scope=channels:read,groups:read,channels:manage,chat:write&client_id={ client_id }&state={ state }"><img alt=""Add to Slack"" height="40" width="139" src="https://platform.slack-edge.com/img/add_to_slack.png" srcset="https://platform.slack-edge.com/img/add_to_slack.png 1x, https://platform.slack-edge.com/img/add_to_slack@2x.png 2x" /></a>'
これで、ユーザーがこのルートに移動すると、Add to Slack ボタン が表示されます。
このボタンをクリックすると次のステップがトリガーされます。
2. ユーザーがリクエストスコープを承認するまで待機する
ユーザーにアプリインストール UI (以下参照) が表示されます。ユーザーが権限を承認し、ワークスペースへのアプリのインストールを許可するオプションがあります。
3. 一時承認コードとアクセストークンを交換する
ユーザーがアプリを承認したら (ステップ 2 )、Slack によりユーザーは指定のリダイレクト URL へリダイレクトされます。ステップ 1 で説明したように、Add to Slack ボタンに redirect_uri を組み込んでいません。したがってこのアプリは、アプリの OAuth and Permissions ページで指定されているリダイレクト URL を使用します。
リダイレクト URL 機能は HTTP リクエストを解析して code と state のクエリパラメーターを取得します。state パラメーターがこのアプリによって作成されていることをチェックする必要があります。作成されている場合は、コードをアクセストークンと交換できます。このためには、コード、クライアント ID、およびクライアントシークレットを指定した oauth.v2.access メソッド
を呼び出す必要があります。oauth.v2.access
からアクセストークンが返されます。このアクセストークンを (できれば永続データベースに) 保存しておくと、今後呼び出す Slack メソッドに使用できます。(注: このアクセストークンは、前述の「スコープ」セクションで説明したすべての Slack メソッド呼び出しに使用する必要があります。)
# 環境変数から client_secret を保存する
client_secret = os.environ["SLACK_CLIENT_SECRET"]
# ユーザーがスコープを承認した後の OAuth フローのルート
@app.route("/finish_auth", methods=["GET", "POST"])
def post_install():
# リクエストパラメータから認可 code と state を取得する
# Retrieve the auth code and state from the request params
auth_code = request.args['code']
received_state = request.args['state']
# このリクエストにはトークンは不要なので空文字でもよい
client = WebClient(token="")
# state パラメータが最初に送信した時と同じであることを確認する
if received_state == state:
# Slack の認可トークンをリクエストする
response = client.oauth_v2_access(
client_id=client_id,
client_secret=client_secret,
code=auth_code
)
else:
return "Invalid State"
# Bot トークンを環境変数に保存する、またはデータベースに保存する
os.environ["SLACK_BOT_TOKEN"] = response['access_token']
# "the-welcome-channel" というチャンネルが存在するか確認する。しない場合チャンネル作成する
channel_exists()
# ユーザに認可が成功したことを知らせる
return "Auth complete!"
これで、アプリに必要なスコープと、OAuth を使用してこれらのスコープをリクエストする方法について理解できたことでしょう。以下のリソースも参照してください。
ご質問やコメントがありましたら、開発者サポート (@SlackAPI または support@slack.com) までご連絡ください。