タスク管理サービスのAsanaでタスクを追加した時にslackに通知するbotを作りました。
通常は以下の画像のようにAsanaとIntegrationできるアプリがたくさんあるので1からコードを書く必要はほぼ無いのですが、会社Asanaと会社slackにて追加できるアプリにそれぞれ制限があり、今回わざわざコードを書くことになりました。
Webhookをやろうとした時に、ちゃんとセキュリティを担保したいけれどその辺りの情報が全然ない…!ということで困ったので、今後何かAsanaのWebhookでアプリを作る人のお役に立てば幸いです。
認証用のPersonal Access Tokenを作成する
最初に認証のためのPersonal Access Tokenを作成します。
まず、AsanaのDeveloper App Consoleにアクセスします。
このURL「 https://app.asana.com/0/developer-console 」にアクセスするか、
Asanaでログインした画面の右上にあるプロフィール画像から「My Profile Settings」をクリックし、ポップアップの「Apps」タブから「Manage Developer Apps」をクリックしても同じ画面に遷移します。
「+ New Access Token」をクリックしToken名を入力すると以下のような形式のトークンを取得できます。プログラムやcurlからのアクセスにはこのTokenを使っていきます。
1/1234567890123456:bive3xuos5Ohcavei4yie7ha0Oovaph8
Webhookを登録する
Webhookを登録するにはTaskが登録されるProjectのIDと、イベントが発生した時に情報を送信するURLが必要です。
ProjectのIDはAsanaのURLから確認できます。タスクを登録するプロジェクトのリストやボードを確認した時のURLに含まれている数字がProject IDを示します。
以下の例であれば 1234567890123456
がProject IDです。
https://app.asana.com/0/1234567890123456/board
先ほど取得したAccess Tokenを{access-token}
、Project IDを{project-id}
、Webhookイベントの送信先URLを{target-url}
に置き換えてcurlコマンドを実行します。
action : added
でタスクの追加イベントを検知します。added
の他にもchanged
removed
deleted
undeleted
などが指定できるようです。
$ curl -X POST https://app.asana.com/api/1.0/webhooks \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer {access-token}' \
-d '{
"data": {
"filters": [
{
"action": "added",
"resource_type": "task"
}
],
"resource": "{project-id}",
"target": "https://{target-url}"
}
}'
Handshakeをする
一番最初にWebhookのイベントが発生すると、Handshake用にX-Hook-Secret
をHTTPヘッダーに含めたリクエストがtarget-url
に設定したURLへ送られます。
このリクエストに対して 200 OK
もしくは 204 No Content
のレスポンスを返すことで登録が完了します。
以下が初期登録時のNode.jsでのAWS Lambdaコードです。2行目でHTTPヘッダーの X-Hook-Secret
をログ出力します。
このX-Hook-Secret
の値は後ほど使うのでMEMOしておいてください。
また、このコードは初回登録時の1回のみ利用します。
exports.handler = async (event) => {
console.log('this is secret header ----->' + event.headers['X-Hook-Secret'])
const response = {
statusCode: 200,
headers: {"X-Hook-Secret": event.headers['X-Hook-Secret']},
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
X-Hook-Secret
はja0ge6aChaiPhee7ioph7cucoongaht3
のような形式の文字列です。
また、このコードが正常に動作しているかどうかはこちらのサイトを利用すると簡単に確認できます。
https://asana-webhook-tool.herokuapp.com/
Webhookで受け取ったリクエストを検証する
次に、実際にWebhookが送られてきたデータを受け取ります。
先ほど得た X-Hook-Secret
を利用することで、target urlへ送られてきたリクエストがAsanaから送られたものであることを確認でき、安全にリクエストを処理できます。
以下はAWS Lambdaのコードです。{X-Hook-Secret}
は先ほど得た値に置き換えてください。
AsanaからはX-Hook-Signature
ヘッダーを持つリクエストが送られてきます。このX-Hook-Signature
の値はリクエストのbodyをX-Hook-Secret
を使ってSHA 256 HMACでHash化したものになります。
Hash化した値とX-Hook-Signature
が同じ値かどうか確認し、同じ値であれば正常なリクエストとして処理します。
var crypto = require("crypto");
exports.handler = async (event) => {
const signature = event.headers['X-Hook-Signature'];
const hash = crypto.createHmac('sha256', '{X-Hook-Secret}')
.update(String(event.body))
.digest('hex');
if (signature != hash) {
console.error('Calculated digest does not match digest from API. This event is not trusted. : ' + signature);
return response = {
statusCode: 401
};
}
const response = {
statusCode: 200
};
return response;
};
以上で基本的なWebhookの設定は完了です。
ヘッダーのチェックをした後は、HTTPリクエストのbodyを処理していけば安全にWebhookのリクエストを操作できます。
参考リンク
公式ドキュメント
Node.js
Crypto | Node.js v15.0.0 Documentation