LoginSignup
6
4

More than 5 years have passed since last update.

python-social-authでSlackのOAuth2.0と仲良くする

Last updated at Posted at 2017-12-06

はじめに

mixiグループ Advent Calendar 2017 の7日目の記事です。
チケットキャンプを運営している株式会社フンザでサーバサイドの開発を担当している@cgetcです。

弊社では業務でSlackを利用しており、煩雑なコミュニケーションを改善するために、SlackBotの開発を試みました。
しかし2点ほど、OAuth2.0関連で嵌まることがあったので、その事象と解決法をまとめました。
弊社はPython/Djangoで開発することが多いので、PythonでメジャーなOAuth2.0のライブラリであるpython-social-auth/social-app-djangoのサンプルを載せています。

Q. SlackのOAuth認証では認証時のScopeが指定できない

SlackのOAuth認証では認証する際にユーザ情報を取得するスコープのみが指定でき、投稿に必要なパーミッションは一度では取得できません。
なので、SlackのOAuth認証をした後にもう一度、ユーザにはスコープを変更してリンクを踏んでもらう必要があります。
(Using OAuth 2.0 Handling Multiple Authorizationsを参照ください。)

A. OAuth認証後にスコープを変更したAuthorization RequestのURLにリダイレクトさせる

Authorization Grant のダイアログが2度表示されてしまいますが、Slackの仕様上、これがベストプラクティスだと思います。

パーミッションを許可してもらうパイプラインを実装

必要なパーミッションがあるかを確認して、ない場合にはリダイレクトさせるだけです。
リダイレクト後にも再度パイプラインを通りますが、そのときにはすでにパーミッションが付与されているので、何もせず次のパイプラインが実行されます。

sample.pipline.py
from django.shortcuts import redirect


def extra_scope(backend, details, response, *args, **kwargs):
    if 'chat:write:user' not in response.get('scope'):
        backend.get_scope = lambda: ['chat:write:user']
        auth_url = backend.auth_url()
        return redirect(auth_url)

パイプラインを設定に追加

パーミッションがあるユーザと無いユーザが混在するとややこしくなるので、なるべく始めの方に定義しています。

settings.py

SOCIAL_AUTH_PIPELINE = (
    # Get the information we can about the user and return it in a simple
    # format to create the user instance later. On some cases the details are
    # already part of the auth response from the provider, but sometimes this
    # could hit a provider API.
    'social_core.pipeline.social_auth.social_details',

    # Get the social uid from whichever service we're authing thru. The uid is
    # the unique identifier of the given user in the provider.
    'social_core.pipeline.social_auth.social_uid',

    """ 省略 """

    # Update the user record with any changed info from the auth service.
    'social_core.pipeline.user.user_details',
)

Q. Add To SlackのOAuth認証でstateパラメータがない

python-socail-authのSlackバックエンドはstateパラメータが必須になっています。しかし、Add To SlackのAuthorization Request にはstateパラメータが含まないために Authorization Responseにも含まれていません。

A. Slackバックエンドを継承したバックエンドを作成する

STATE_PARAMETER = False にして対応します。
(stateパラメータはOAuth 2.0のCSRF対策として重要なパラメータなので、無効にするのは避けるべきです。今回はこのような対応をしましたが、state パラメータを含む方法があれば、ご教授ください。Add To SlackがボタンなどでURLを埋め込んで利用される前提なので、仕様上stateパラメータをAuthorization Requestに含めるのが難しいため、このような仕様だと思われます。)

これだけでも動きますが、Add To Slackの場合に必要な情報は、ユーザ情報ではなく、チーム情報の方が多いと思います。ですので、チーム情報を取得・格納するように get_user_details user_dataを実装します。

sample/social/backends/slack.py
from social_core.backends.slack import SlackOAuth2


class AddToSlackOAuth2(SlackOAuth2):
    STATE_PARAMETER = False

    def get_user_details(self, response):
        """Return user details from Slack team"""
        # Build the username with the team $username@$team_url
        # Necessary to get unique names for all of slack
        team = response['team']

        return {
            'username': team['name'],
            'email': '{domain}@{email_domain}'.format(**team),
            'fullname': team['domain'],
        }

    def user_data(self, access_token, *args, **kwargs):
        """Loads user data from service"""
        response = self.get_json('https://slack.com/api/team.info', params={'token': access_token})
        if not response.get('id', None):
            response['id'] = response['team']['id']

        return response

AUTHENTICATION_BACKENDS を上記のクラスに差し替えます。

settings.py
AUTHENTICATION_BACKENDS = (
    'sample.social.backends.slack.AddToSlackOAuth2',
)

認証が完了するとUserにチーム情報が格納されます。
アクセストークンを取得するには、python-socail-authと変わりません。

from social_django.models import UserSocialAuth


def get_access_token(team_id):
    return UserSocialAuth.get_social_auth('slack', team_id).extra_data['access_token']

team_idはWebhookのPayloadなどから取得できます。

さいごに

Djangoでの例のみを示しましたが、他のWEBフレームワークでも応用できますし、SlackのOAuth2.0の実装による問題なので、言語問わず参考になるかと思います。
SlackBotの開発の助けになったら幸いです。

6
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4