mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: implement WhatsApp webhook authorization middleware for signature verification
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
} from "Common/Types/WhatsApp/WhatsAppTemplates";
|
||||
import WhatsAppStatus from "Common/Types/WhatsAppStatus";
|
||||
import ClusterKeyAuthorization from "Common/Server/Middleware/ClusterKeyAuthorization";
|
||||
import WhatsAppAuthorization from "Common/Server/Middleware/WhatsAppAuthorization";
|
||||
import WhatsAppLogService from "Common/Server/Services/WhatsAppLogService";
|
||||
import GlobalConfigService from "Common/Server/Services/GlobalConfigService";
|
||||
import Express, {
|
||||
@@ -371,6 +372,7 @@ router.get("/webhook", async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
|
||||
router.post(
|
||||
"/webhook",
|
||||
WhatsAppAuthorization.isAuthorizedWhatsAppRequest,
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
const body: JSONObject = req.body as JSONObject;
|
||||
|
||||
93
Common/Server/Middleware/WhatsAppAuthorization.ts
Normal file
93
Common/Server/Middleware/WhatsAppAuthorization.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import {
|
||||
ExpressResponse,
|
||||
NextFunction,
|
||||
OneUptimeRequest,
|
||||
} from "../Utils/Express";
|
||||
import Response from "../Utils/Response";
|
||||
import BadDataException from "../../Types/Exception/BadDataException";
|
||||
import GlobalConfig from "../../Models/DatabaseModels/GlobalConfig";
|
||||
import GlobalConfigService from "../Services/GlobalConfigService";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import crypto from "crypto";
|
||||
import logger from "../Utils/Logger";
|
||||
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
||||
|
||||
export default class WhatsAppAuthorization {
|
||||
@CaptureSpan()
|
||||
public static async isAuthorizedWhatsAppRequest(
|
||||
req: OneUptimeRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction,
|
||||
): Promise<void> {
|
||||
logger.debug("Starting WhatsApp webhook signature verification");
|
||||
|
||||
const signature: string | undefined = req.headers[
|
||||
"x-hub-signature-256"
|
||||
] as string | undefined;
|
||||
|
||||
if (!signature) {
|
||||
logger.error(
|
||||
"WhatsApp webhook request missing X-Hub-Signature-256 header.",
|
||||
);
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException(
|
||||
"Missing X-Hub-Signature-256 header.",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const globalConfig: GlobalConfig | null =
|
||||
await GlobalConfigService.findOneBy({
|
||||
query: {
|
||||
_id: ObjectID.getZeroObjectID().toString(),
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
metaWhatsAppAppSecret: true,
|
||||
},
|
||||
});
|
||||
|
||||
const appSecret: string | undefined =
|
||||
globalConfig?.metaWhatsAppAppSecret?.trim() || undefined;
|
||||
|
||||
if (!appSecret) {
|
||||
logger.error(
|
||||
"Meta WhatsApp App Secret is not configured. Cannot verify webhook signature.",
|
||||
);
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException(
|
||||
"Meta WhatsApp App Secret is not configured.",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const rawBody: string = req.rawBody || "";
|
||||
|
||||
const expectedSignature: string = `sha256=${crypto.createHmac("sha256", appSecret).update(rawBody).digest("hex")}`;
|
||||
|
||||
if (
|
||||
!crypto.timingSafeEqual(
|
||||
Buffer.from(expectedSignature) as Uint8Array,
|
||||
Buffer.from(signature) as Uint8Array,
|
||||
)
|
||||
) {
|
||||
logger.error("WhatsApp webhook signature verification failed.");
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException(
|
||||
"WhatsApp webhook signature verification failed.",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
logger.debug("WhatsApp webhook signature verified successfully");
|
||||
next();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user