こうりんのブログ

主に勉強した事を書いていくつもり

Google Cloud PlatformでSlackBot開発【Slack App認証編】

この前Herokuでbot動かすとこまでやったけど, 今回はGCP(Google Cloud Platform)で動かそうと思います.
GCP動かしてみたかった. Events APIも使ってみようというのが理由です.

でも, HTTPを用いるEvent APIは30,000 Events/hが上限らしいので, 一応注意.
(https://api.slack.com/events-api#rate_limiting)

という事で, GCP(Google Cloud Platform)上でbotを作成していきます.
今回は認証まで.

Slack Appの作成

前回とほぼ同じだと思うけど, 一応書く.
Slack APIのトップページから Start Building >> Create New App で新しいSlackアプリを作成できる.
Slack API | Slack

アプリ作成直後のページがこんな感じ.
左のサイドメニューから各種設定をする.

f:id:Kourin1996:20180430221639p:plain

まず, Basic InformationApp Credentials をみると, Client IDClient Secret というのが書いてある.
これは, アプリ自体の固有なIDの事らしい.

次に, Bot Users ではBotの設定ができる.
Add a Bot User を押すと, 名前などを設定でき, Add Bot User と押すと, アプリを追加した際にワークスペース上にBotが登場する.

次に, OAuth & PermissionsScopes の設定.
ここでは, アプリが使用できるAPIの種類を設定する.
あまり沢山のスコープを設定すると, アプリをSlackに公開する際に大変らしいので, 必要最低限の権限だけ追加する.
APIのメソッドとスコープの対応関係は以下に記載されている.

OAuth Scopes | Slack

そして, 最後にOAuth Tokens& Redirect URLs だが, GCPとの連携が必要なので後ほど述べる.

以上でSlack Appの最低限の設定は終わり.
次はGCPの設定に移っていく.

GCPの登録

GCPのページに行くとトップにTry It Freeと書いてある.
GCPではお試しとして, 12ヶ月$300分が使えるらしい.
(2018年4月30日現在)
お試し期間を過ぎてしまっても, 急に請求が来ることは無いらしい. (本当か?)
一番性能の低いVM1個だけだったら無料で使えたりというような, サービス毎に無料枠が設定されてることもある.
無料枠の範囲内ならお試し期間過ぎても大丈夫そう.

Try It Free を押すと登録画面に移動する.
利用規約の承諾をして進むと, 住所とクレジットカードの番号を入力する画面に移動する.
必要事項を入力して, 無料トライアルを始める を押すと登録が完了する.
クレジットカード番号を入力するが, もし仮に課金が開始されようとなるときはちゃんとGoogleから連絡来て承認してかららしい.
とりあえず, 安心.
(多分, 自分以外誰も使わないレベルでの運用なら課金なんて微々たる気がするけど)

GCPとSlack Appの連携

今回はSlack Appを動かすために必要な認証をGCP上で行う.
Slack Appはワークスペースに追加する際に, 特定のURLへリダイレクトをできる.
このリダイレクトを用いて認証する事で, HTTPの通信でトークンを発行する事ができる.

以下に今回の構成図を示す.
f:id:Kourin1996:20180430224246p:plain
先ほど言ったリダイレクト先をGCPのCloud Functionsに設定する.
プログラムのログとかはStackdriverで確認できるみたい.

GCP SDKのインスール

ローカルのターミナル上でGCPを操作するために, GCP SDKをインストールする.
以下のURLにアクセスして, 自分の環境にあったファイルをダウンロードする.

Installing Google Cloud SDK  |  Cloud SDK Documentation  |  Google Cloud

以下のコマンドを実行してインストールする.
ちなみに, gcloud initをするとブラウザが立ち上がるので使用するGoogleアカウントを選択すると自動でプロジェクトの一覧とか読んできてくれる.
すごくべんり

cd ~/Downloads
tar zxvf ./google-cloud-sdk-199.0.0-darwin-x86_64.tar.gz
./google-cloud-sdk/install.sh
./google-cloud-sdk/bin/gcloud init

環境変数の設定

認証にはAppのIDが必要だけど, ソースに直書きするのも色々問題なので, 環境変数から読み込むことにする.

GCP上での環境変数の設定はconfigという単位で管理される.
以下でconfigを作れる, 最後の引数がconfig名である.

gcloud beta runtime-config configs create (config名)

configのリストは以下で確認できる.

gcloud beta runtime-config configs list

今回は Client IDClient Secret環境変数にセットする.
SlackのAppの設定画面の Basic Information >> App Credentials から値を確認する.

環境変数の設定は以下のコマンドで実行できる.

gcloud beta runtime-config configs variables set (変数名) (値) --config-name (config名) --is-text

セットされた変数の名前と変数の値はそれぞれ以下のコマンドで確認できる.

gcloud beta runtime-config configs variables list --config-name (config名)

gcloud beta runtime-config configs variables get-value (変数名) --config-name (config名)

Cloud Functionsの作成

本題のCloud Functionを作成する.
GCPのホームから Cloud Functions を選択して関数を作成する.
名前と割り当てるメモリを選ぶ.
メモリは分からなかったので最小の128MBを選んだ.
トリガーはリダイレクトで実行するのでHTTPとする.
トリガーの以下にURLが表示されるので, これを後でリダイレクトURLとして設定.

次に実行する関数を記述する.
SlackAppの認証の流れは次の通りである.

  1. ワークスペースにアプリを追加する.(ユーザがブラウザ上で実行)
  2. 設定したサーバのURLにリダイレクト, リクエストはcodeというgetパラメータを持つ
  3. サーバはSlackAPIにcode, client id, client secretを付加したGETリクエストを送る
  4. Slackからjsonが返ってくる(okパラメータがtrueだったら認証成功)

今回はとりあえず, 認証を行った後は最終的に返ってくるjsonをそのまま表示するようにした.

index.js
/**
 * Responds to any HTTP request that can provide a "message" field in the body.
 *
 * @param {!Object} req Cloud Function request context.
 * @param {!Object} res Cloud Function response context.
 */

const request = require('request');
const rcloadenv = require('@google-cloud/rcloadenv');

exports.onRedirectSlackOAuth = (req, res) => {
    //リダイレクトのリクエストの検証
    if (req.method !== 'GET') {
        let error_message = "Illegal Request, wrong method";
        console.log(error_message, req.method);
        res.status(405).send(error_message);
        return;
    }
    if (req.query.code === undefined) {
        let error_message = "Illegal Request, no required query"
        console.log(error_message, req.query);
        res.status(400).send(error_message);
        return;
    }

    let code = req.query.code;

    //環境変数の取得
    rcloadenv.getAndApply('test-slackbot', {}).then((env) => {
        if (env.CLIENT_ID === undefined || env.CLIENT_SECRET === undefined) {
            console.error('Failure: TOKEN_ID and TOKEN_SECRET is not set');
            let error_message = 'Sorry Failure OAuth by Server Error';
            res.status(500).send(error_message);
            return;
        }

        //Slack APIを叩く
        let client_id = env.CLIENT_ID;
        let client_secret = env.CLIENT_SECRET;
        let url = 'https://slack.com/api/oauth.access';
        request.get({
            uri: url,
            headers: {
                'Content-type': 'application/json'
            },
            qs: {
                client_id: client_id,
                client_secret: client_secret,
                code: code
            },
            json: true
        }, function(err, req, data) {
            if (data) {
                if (data.ok) {
                    //認証成功
                    let access_token = data.access_token;
                    let team_name = data.team_name;
                    let team_id = data.team_id;
                    let message = "OAuth Successful"
                    console.log(message, data);
                    res.status(200).send(message);
                } else {
                    let error_message = 'OAuth Failure';
                    console.error(error_message, data.error);
                    res.status(500).send(error_message);
                }
            }
            if (err) {
                let error_message = 'OAuth Failure';
                console.error(error_message, err);
                res.status(500).send(error_message);
            }
        });
    }).catch((err) => {
            console.error('Failure: Config test-slackbot is not found');
            let error_message = 'Sorry Failure OAuth by Server Error';
            res.status(500).send(error_message);
    });
};
package.json
{
  "name": "Test-SlackApp-OAuth",
  "version": "0.0.1",
  "dependencies": {
    "@google-cloud/rcloadenv": "^0.2.1",
    "request":"^2.8.0"
  }
}

動作確認

Cloud Functionsの設定にあるURLをSlack Appに設定する.
OAuth & Permissions >> OAuth Tokens & Redirect URLs >> Redirect URLs で Add a new Redirect URLでCloud FunctionsのURLをセットする.

そして以下のURLにブラウザからアクセスする.

https://slack.com/oauth/authorize?client_id=(アプリのClient ID)&scope=(アプリのスコープ)

以下のページが出たら, Authorizeを押す.

f:id:Kourin1996:20180501013055p:plain

以下のようなJSONが返ってきたら, 認証は成功.

f:id:Kourin1996:20180501013316p:plain

参考

azriton.github.io

qiita.com