ボットの OAuth スコープについて
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
は不要です)。
# verifies if "the-welcome-channel" already exists
def channel_exists():
token = os.environ["SLACK_BOT_TOKEN"]
client = slack.WebClient(token=token)
# grab a list of all the channels in a workspace
clist = client.conversations_list()
exists = False
for k in clist["channels"]:
# look for the channel in the list of existing channels
if k['name'] == 'the-welcome-channel':
exists = True
break
if exists == False:
# create the channel since it doesn't exist
create_channel()
2.チャンネルがまだ存在していない場合は作成する必要があります。メソッドのリストで、conversations.create
が目にとまりました。これには channels:manage
というスコープが必要です。
# creates a channel named "the-welcome-channel"
def create_channel():
token = os.environ["SLACK_BOT_TOKEN"]
client = slack.WebClient(token=token)
resp = client.conversations_create(name="the-welcome-channel")
3.新規に作成したチャンネルにユーザーが参加すると、アプリからユーザーにダイレクトメッセージが送信されます。ユーザーがチャンネルに参加したことを確認するには、イベントをリスンする必要があります。イベントのリストにある member_joined_channel
が、必要なイベントです (注: イベントは api.slack.com/apps を使用してアプリの設定に追加する必要があります)。このイベントに必要なスコープは channels:read
と groups:read
(ステップ1と同じ) です。次にダイレクトメッセージを送信するため、chat.postMessage
メソッドを使用する必要があります。このメソッドには chat:write
スコープが必要です。
# Create an event listener for "member_joined_channel" events
# Sends a DM to the user who joined the channel
@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 = slack.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
になります。
OAuth の設定とスコープのリクエスト
Slack アプリを初めて開発する方は、OAuth をまだ実装したことがなく、基本的なアプリの設定を利用していたかもしれません。これは、1つのワークスペースのみにアプリをインストールする場合を想定しています。ユーザーがアプリを他のワークスペースにもインストールできるようにする場合や、App ディレクトリからインストールできるようにする場合には、OAuth を実装する必要があります。
API サイトで説明されている、Slack で OAuth を使用する一般的なフローに従います。このフローを次の図に示します。
スコープを要求する
最初のステップは、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 ボタンを含むルートをアプリに作成します。
# Grab client ID from your environment variables
client_id = os.environ["SLACK_CLIENT_ID"]
# Generate random string to use as state to prevent CSRF attacks
from uuid import uuid4
state = str(uuid4())
# Route to kick off Oauth flow
@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 メソッド呼び出しに使用する必要があります。)
# Grab client Secret from your environment variables
client_secret = os.environ["SLACK_CLIENT_SECRET"]
# Route for Oauth flow to redirect to after user accepts scopes
@app.route("/finish_auth", methods=["GET", "POST"])
def post_install():
# Retrieve the auth code and state from the request params
auth_code = request.args['code']
received_state = request.args['state']
# An empty string is a valid token for this request
client = slack.WebClient(token="")
# verify state received in params matches state we originally sent in auth request
if received_state == state:
# Request the auth tokens from Slack
response = client.oauth_v2_access(
client_id=client_id,
client_secret=client_secret,
code=auth_code
)
else:
return "Invalid State"
# Save the bot token to an environmental variable or to your data store
os.environ["SLACK_BOT_TOKEN"] = response['access_token']
# See if "the-welcome-channel" exists. Create it if it doesn't.
channel_exists()
# Don't forget to let the user know that auth has succeeded!
return "Auth complete!"
次のステップ
これで、アプリに必要なスコープと、OAuth を使用してこれらのスコープをリクエストする方法について理解できたことでしょう。以下のリソースも参照してください。
- Slack-Python-OAuth-Example - このチュートリアルのアプリのコード スニペットを使用しました。README には、ngrok を使ったアプリのローカルな実行と、OAuth の リダイレクト URLとイベントのリクエスト URL の設定に関する詳細情報があります。
- Slack での OAuth についての詳細情報。ここでは、アプリがユーザーの代わりに処理を行う場合に、user_scope パラメーターを使用してユーザートークンを取得する方法についても説明します。
- 利用可能なスコープのリストを参照する。
- 利用可能なイベントタイプのリストを参照する。ほとんどのイベントは、動作するために特定のスコープが必要です。
ご質問やコメントがありましたら、開発者サポート (@SlackAPI または support@slack.com) までご連絡ください。