最終更新
役に立ちましたか?
役に立ちましたか?
const { createHmac } = require('node:crypto');
const express = require('express');
const bodyParser = require('body-parser');
const PORT = 4000;
const app = express();
app.use(bodyParser.json());
const isIncomingWebhookValid = (payload, signature) => {
// 生のWebhookシークレットを環境変数から取得する
const aikidoWebhookSecret = process.env.AIKIDO_WEBHOOK_SECRET;
// HMACインスタンスを作成する
const hmac = createHmac('sha256', aikidoWebhookSecret);
// ペイロードオブジェクトをJSON文字列に変換する
const rawPayload = JSON.stringify(payload);
// 文字列化したペイロードでHMACの内容を更新する
hmac.update(rawPayload);
// HMACの内容のダイジェストを計算し、16進数値として返す
const payloadDigest = hmac.digest('hex');
// ダイジェストが指定された署名と一致しない場合、Webhookは無効
if (payloadDigest !== signature) return false;
// 現在のエポックタイムスタンプを取得する
const currentEpochTimestamp = Math.floor(new Date().getTime() / 1000);
// ペイロードの dispatched_at エポックタイムスタンプが30秒より古い場合、Webhookは無効
if ((currentEpochTimestamp - (payload['dispatched_at'] ?? 0)) > 30) return false;
// Webhookはすべてのチェックに合格しており、有効
return true;
}
app.post('/webhooks/aikido/issue-created', async (req, res) => {
const isValid = isIncomingWebhookValid(req.body, req.headers['X-Aikido-Webhook-Signature']);
if (!isValid) {
throw new Error(`リクエストの署名が無効です`)
}
// ビジネスロジック
res.status(204);
});
app.listen(PORT, () => {
console.log(`サーバーはポート ${port} で待機しています`);
});