Slack Signature Verification (#2667)

* Slack Verify

* Adding await

* Update slackVerify.ts

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
This commit is contained in:
sean-brydon 2022-05-04 00:40:01 +01:00 committed by GitHub
parent 02b935bcde
commit fa1ca5fba0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 1 deletions

View file

@ -2,6 +2,7 @@ import type { NextApiRequest, NextApiResponse } from "next";
import { showCreateEventMessage, showTodayMessage } from "../lib"; import { showCreateEventMessage, showTodayMessage } from "../lib";
import showLinksMessage from "../lib/showLinksMessage"; import showLinksMessage from "../lib/showLinksMessage";
import slackVerify from "../lib/slackVerify";
export enum SlackAppCommands { export enum SlackAppCommands {
CREATE_EVENT = "create-event", CREATE_EVENT = "create-event",
@ -12,7 +13,7 @@ export enum SlackAppCommands {
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "POST") { if (req.method === "POST") {
const command = req.body.command.split("/").pop(); const command = req.body.command.split("/").pop();
await slackVerify(req, res);
switch (command) { switch (command) {
case SlackAppCommands.CREATE_EVENT: case SlackAppCommands.CREATE_EVENT:
return await showCreateEventMessage(req, res); return await showCreateEventMessage(req, res);

View file

@ -1,6 +1,7 @@
import { NextApiRequest, NextApiResponse } from "next"; import { NextApiRequest, NextApiResponse } from "next";
import createEvent from "../lib/actions/createEvent"; import createEvent from "../lib/actions/createEvent";
import slackVerify from "../lib/slackVerify";
enum InteractionEvents { enum InteractionEvents {
CREATE_EVENT = "cal.event.create", CREATE_EVENT = "cal.event.create",
@ -8,6 +9,7 @@ enum InteractionEvents {
export default async function interactiveHandler(req: NextApiRequest, res: NextApiResponse) { export default async function interactiveHandler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "POST") { if (req.method === "POST") {
await slackVerify(req, res);
const payload = JSON.parse(req.body.payload); const payload = JSON.parse(req.body.payload);
const actions = payload.view.callback_id; const actions = payload.view.callback_id;

View file

@ -0,0 +1,36 @@
import { createHmac } from "crypto";
import dayjs from "dayjs";
import { NextApiRequest, NextApiResponse } from "next";
import { stringify } from "querystring";
import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
let signingSecret = "";
export default async function slackVerify(req: NextApiRequest, res: NextApiResponse) {
const body = req.body;
const timeStamp = req.headers["x-slack-request-timestamp"] as string; // Always returns a string and not a string[]
const slackSignature = req.headers["x-slack-signature"] as string;
const currentTime = dayjs().unix();
let { signing_secret } = await getAppKeysFromSlug("slack");
if (typeof signing_secret === "string") signingSecret = signing_secret;
if (!timeStamp) {
return res.status(400).json({ message: "Missing X-Slack-Request-Timestamp header" });
}
if (!signingSecret) {
return res.status(400).json({ message: "Missing Slack's signing_secret" });
}
if (Math.abs(currentTime - parseInt(timeStamp)) > 60 * 5) {
return res.status(400).json({ message: "Request is too old" });
}
const signature_base = `v0:${timeStamp}:${stringify(body)}`;
const signed_sig = "v0=" + createHmac("sha256", signingSecret).update(signature_base).digest("hex");
if (signed_sig !== slackSignature) {
return res.status(400).json({ message: "Invalid signature" });
}
}