@huddle01/server-sdk
Version:
The Huddle01 Server SDK allows you to perform protected admin actions on your server side, like generating peer access tokens and starting and stopping meeting recordings and livestreams.
131 lines (127 loc) • 4.22 kB
JavaScript
;
var chunkOBGZSXTJ_cjs = require('./chunk-OBGZSXTJ.cjs');
// src/webhooks/WebhookReceiver.ts
var BufferClass;
try {
if (typeof Buffer !== "undefined") {
BufferClass = Buffer;
} else {
console.debug("Buffer not directly available, importing from node:buffer");
const nodeBuffer = chunkOBGZSXTJ_cjs.__require("buffer");
BufferClass = nodeBuffer.Buffer;
}
} catch (error) {
console.error("Unable to find Buffer", error);
}
var WebhookReceiver = class _WebhookReceiver {
#config;
algMap = {
sha256: "SHA-256",
sha512: "SHA-512"
};
constructor(config) {
this.#config = config;
}
/**
* Verify the webhook request and extract the webhook content
*
* @param body Body of the webhook request
* @param header Signature header of the webhook request
* @returns Extracted webhook data
*/
async receive(body, header) {
const { hashPayload, signatureAlgorithm, signature, data } = this.getBodyAndSignature(body, header);
if (signatureAlgorithm !== "sha256" && signatureAlgorithm !== "sha512")
throw new Error("Invalid signature algorithm");
const hmac = await _WebhookReceiver.asyncGenerateHmac(
this.algMap[signatureAlgorithm],
hashPayload,
this.#config.apiKey
);
const generatedSignature = `${signatureAlgorithm}=${hmac}`;
if (generatedSignature.length !== signature.length || !await _WebhookReceiver.timingSafeEqual(
generatedSignature,
signature,
64
)) {
throw new Error("Invalid signature");
}
return data;
}
/**
* Helper method to extract the body, signature, alogorithm and hashpayload
* from the webhook request. WARNING: This method does not verify the signature
*
* @param body Body of the webhook request
* @param header Signature header of the webhook request
* @returns body, signature, alogorithm and hashpayload
*/
getBodyAndSignature(body, header) {
const timestamp = header?.split(",")[0]?.split("=")?.[1];
const signature = header?.split(",")[1];
if (!timestamp || !signature) throw new Error("Invalid headers");
let data;
if (typeof body === "string") data = JSON.parse(body);
else if (typeof body === "object") data = body;
else throw new Error("Invalid body");
if (!data.id) throw new Error("Invalid body");
const hashPayload = `${data.id}.${timestamp}.${JSON.stringify(data)}`;
const signatureAlgorithm = signature.split("=")[0];
if (signatureAlgorithm !== "sha256" && signatureAlgorithm !== "sha512")
throw new Error("Invalid signature algorithm");
return { hashPayload, signatureAlgorithm, signature, data };
}
/**
* Helper function to create a typed webhook data object
*/
createTypedWebhookData(event, data) {
return { event, data };
}
static async asyncGenerateHmac(alg, message, secretKey) {
const encoder = new TextEncoder();
let keyData;
let key;
if (typeof secretKey === "string") {
keyData = encoder.encode(secretKey);
key = await crypto.subtle.importKey(
"raw",
keyData,
{ name: "HMAC", hash: alg },
false,
["sign"]
);
} else {
key = secretKey;
}
if (!key) {
throw new Error("Invalid secret key");
}
const signature = await crypto.subtle.sign(
"HMAC",
key,
encoder.encode(message)
);
return Buffer.from(signature).toString("hex");
}
/**
* Function to check if two strings are time safe equal.
* Based on: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy
*/
static async timingSafeEqual(expected, generated, keyLength) {
const secretKey = await crypto.subtle.generateKey(
{
name: "HMAC",
hash: { name: "SHA-256" },
length: keyLength * 8
},
true,
["sign"]
);
const [expectedHmac, generatedHmac] = await Promise.all([
_WebhookReceiver.asyncGenerateHmac("SHA-256", expected, secretKey),
_WebhookReceiver.asyncGenerateHmac("SHA-256", generated, secretKey)
]);
return expectedHmac === generatedHmac;
}
};
exports.WebhookReceiver = WebhookReceiver;