ボットの 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 ではアプリがその動作に必要な小さなスコープのみを選択できるようになりました。これにより、以前よりもユーザーがアプリをインストールするようになります。

アプリに必要なスコープを判断するため、アプリが行う処理を詳しく確認する必要があります。個人的には、アプリにとって妥当であると思われるスコープの全体リストを調べる代わりに、アプリに必要なイベントメソッドを考えながら、スコープを決めていきたいと思います。

  1. インストール時に、アプリはチャンネルが存在しているかどうかを確認します (同名の新規チャンネルを作成できないので、プライベートとパブリックのチャンネルが含まれます)。メソッドのリストをクイック検索し、conversations.list を見つけました。これは、パブリックチャンネルとプライベートチャンネルの名前を取得するために使用できます。また、このメソッドを使用するために必要なスコープも判明しました。このケースでは、channels:readgroups:read が必要です (ダイレクトメッセージの名前については心配していないので、im:readmpim: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()
  1. チャンネルがまだ存在していない場合は作成する必要があります。メソッドのリストで、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")
  1. 新規に作成したチャンネルにユーザーが参加すると、アプリからユーザーにダイレクトメッセージが送信されます。ユーザーがチャンネルに参加したことを確認するには、イベントをリスンする必要があります。イベントのリストにある member_joined_channel が、必要なイベントです (注: イベントは api.slack.com/apps を使用してアプリの設定に追加する必要があります)。このイベントに必要なスコープは channels:readgroups: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:readgroups:readchannels:manage、および chat:write になります。

OAuth の設定とスコープのリクエスト

Slack アプリを初めて開発する方は、OAuth をまだ実装したことがなく、基本的なアプリの設定を利用していたかもしれません。これは、1つのワークスペースのみにアプリをインストールする場合を想定しています。ユーザーがアプリを他のワークスペースにもインストールできるようにする場合や、App ディレクトリからインストールできるようにする場合には、OAuth を実装する必要があります。

API サイトで説明されている、Slack で OAuth を使用する一般的なフローに従います。このフローを次の図に示します。

OAuth flow

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/appsOAuth 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 ボタン が表示されます。

Oauth button

このボタンをクリックすると次のステップがトリガーされます。

2. ユーザーがリクエストスコープを承認するまで待機する

ユーザーにアプリインストール UI (以下参照) が表示されます。ユーザーが権限を承認し、ワークスペースへのアプリのインストールを許可するオプションがあります。

Permission screen

3. 一時承認コードとアクセストークンを交換する

ユーザーがアプリを承認したら (ステップ 2 )、Slack によりユーザーは指定のリダイレクト URL へリダイレクトされます。ステップ 1 で説明したように、Add to Slack ボタンredirect_uri を組み込んでいません。したがってこのアプリは、アプリの OAuth and Permissions ページで指定されているリダイレクト URL を使用します。

リダイレクト URL 機能は HTTP リクエストを解析して codestate のクエリパラメーターを取得します。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 を使用してこれらのスコープをリクエストする方法について理解できたことでしょう。以下のリソースも参照してください。

  • Slack-Python-OAuth-Example - このチュートリアルのアプリのコード スニペットを使用しました。README には、ngrok を使ったアプリのローカルな実行と、OAuth の リダイレクト URLとイベントのリクエスト URL の設定に関する詳細情報があります。
  • Slack での OAuth についての詳細情報。ここでは、アプリがユーザーの代わりに処理を行う場合に、user_scope パラメーターを使用してユーザートークンを取得する方法についても説明します。
  • 利用可能なスコープのリストを参照する。
  • 利用可能なイベントタイプのリストを参照する。ほとんどのイベントは、動作するために特定のスコープが必要です。

ご質問やコメントがありましたら、開発者サポート (@SlackAPI または support@slack.com) までご連絡ください。


おすすめの関連記事 🦄