Criei uma etapa de código para calcular uma assinatura. Existe uma Web Crypto API que pode calcular um HMAC-SHA256 muito mais facilmente, mas ela usa uma promise e isso não funciona dentro de um eval. Então, pedi para uma IA criar um método nativo em JavaScript para calcular HMAC-SHA256, adicionei uma variável com a chave secret e o segredo como seu valor (, e acabei com um script de Código como este:
// Variáveis disponíveis:
// $input.item.json - dados JSON do item atual
// $input.all() - array de todos os itens de entrada
// $json - atalho para $input.item.json
// $("NodeName").item - acessar o item pareado de outro nó
// $vars.KEY - variáveis de fluxo de trabalho
// $site_settings.NAME - configurações do site
// $execution - metadados de execução (id, workflow_name, ...)
// $current_user - usuário executando o fluxo de trabalho
// $helpers.absoluteUrl(path) - converter um caminho relativo em uma URL absoluta
// console.log/warn/error - log
function hmacSha256(message, key) {
function utf8Bytes(str) {
const bytes = [];
for (let i = 0; i < str.length; i++) {
let code = str.charCodeAt(i);
// Lidar com pares de substitutos (surrogate pairs)
if (
code >= 0xd800 &&
code <= 0xdbff &&
i + 1 < str.length
) {
const next = str.charCodeAt(i + 1);
if (next >= 0xdc00 && next <= 0xdfff) {
code =
0x10000 +
((code - 0xd800) << 10) +
(next - 0xdc00);
i++;
}
}
if (code <= 0x7f) {
bytes.push(code);
} else if (code <= 0x7ff) {
bytes.push(
0xc0 | (code >> 6),
0x80 | (code & 0x3f)
);
} else if (code <= 0xffff) {
bytes.push(
0xe0 | (code >> 12),
0x80 | ((code >> 6) & 0x3f),
0x80 | (code & 0x3f)
);
} else {
bytes.push(
0xf0 | (code >> 18),
0x80 | ((code >> 12) & 0x3f),
0x80 | ((code >> 6) & 0x3f),
0x80 | (code & 0x3f)
);
}
}
return bytes;
}
function rotr(x, n) {
return (x >>> n) | (x << (32 - n));
}
function sha256(bytes) {
const K = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
];
const H = [
0x6a09e667,
0xbb67ae85,
0x3c6ef372,
0xa54ff53a,
0x510e527f,
0x9b05688c,
0x1f83d9ab,
0x5be0cd19
];
const data = bytes.slice();
const bitLength = data.length * 8;
// Preenchimento SHA-256
data.push(0x80);
while ((data.length % 64) !== 56) {
data.push(0);
}
// Anexar o comprimento da mensagem de 64 bits em big-endian.
// Inteiros JS são seguros aqui para tamanhos normais de payload de fluxo de trabalho.
const high = Math.floor(bitLength / 0x100000000);
const low = bitLength >>> 0;
data.push(
(high >>> 24) & 0xff,
(high >>> 16) & 0xff,
(high >>> 8) & 0xff,
high & 0xff,
(low >>> 24) & 0xff,
(low >>> 16) & 0xff,
(low >>> 8) & 0xff,
low & 0xff
);
const w = new Array(64);
for (let offset = 0; offset < data.length; offset += 64) {
for (let i = 0; i < 16; i++) {
const j = offset + i * 4;
w[i] =
((data[j] << 24) |
(data[j + 1] << 16) |
(data[j + 2] << 8) |
data[j + 3]) >>> 0;
}
for (let i = 16; i < 64; i++) {
const s0 =
rotr(w[i - 15], 7) ^
rotr(w[i - 15], 18) ^
(w[i - 15] >>> 3);
const s1 =
rotr(w[i - 2], 17) ^
rotr(w[i - 2], 19) ^
(w[i - 2] >>> 10);
w[i] =
(
w[i - 16] +
s0 +
w[i - 7] +
s1
) >>> 0;
}
let a = H[0];
let b = H[1];
let c = H[2];
let d = H[3];
let e = H[4];
let f = H[5];
let g = H[6];
let h = H[7];
for (let i = 0; i < 64; i++) {
const S1 =
rotr(e, 6) ^
rotr(e, 11) ^
rotr(e, 25);
const ch = (e & f) ^ (~e & g);
const temp1 =
(
h +
S1 +
ch +
K[i] +
w[i]
) >>> 0;
const S0 =
rotr(a, 2) ^
rotr(a, 13) ^
rotr(a, 22);
const maj =
(a & b) ^
(a & c) ^
(b & c);
const temp2 = (S0 + maj) >>> 0;
h = g;
g = f;
f = e;
e = (d + temp1) >>> 0;
d = c;
c = b;
b = a;
a = (temp1 + temp2) >>> 0;
}
H[0] = (H[0] + a) >>> 0;
H[1] = (H[1] + b) >>> 0;
H[2] = (H[2] + c) >>> 0;
H[3] = (H[3] + d) >>> 0;
H[4] = (H[4] + e) >>> 0;
H[5] = (H[5] + f) >>> 0;
H[6] = (H[6] + g) >>> 0;
H[7] = (H[7] + h) >>> 0;
}
const result = [];
for (let i = 0; i < H.length; i++) {
result.push(
(H[i] >>> 24) & 0xff,
(H[i] >>> 16) & 0xff,
(H[i] >>> 8) & 0xff,
H[i] & 0xff
);
}
return result;
}
function bytesToHex(bytes) {
return bytes
.map(b => b.toString(16).padStart(2, "0"))
.join("");
}
const blockSize = 64;
let keyBytes = utf8Bytes(key);
const messageBytes = utf8Bytes(message);
// Chaves maiores que o tamanho do bloco SHA-256 são primeiro hasheadas.
if (keyBytes.length > blockSize) {
keyBytes = sha256(keyBytes);
}
// Preencher a chave até 64 bytes.
while (keyBytes.length < blockSize) {
keyBytes.push(0);
}
const innerPad = new Array(blockSize);
const outerPad = new Array(blockSize);
for (let i = 0; i < blockSize; i++) {
innerPad[i] = keyBytes[i] ^ 0x36;
outerPad[i] = keyBytes[i] ^ 0x5c;
}
const innerHash = sha256(
innerPad.concat(messageBytes)
);
const hmac = sha256(
outerPad.concat(innerHash)
);
return bytesToHex(hmac);
}
const payload = JSON.stringify({
"the-json-payload": "goes-here"
});
const signature = "sha256=" + hmacSha256( payload, $vars.secret);
return {
"payload": payload,
"signature": signature
};
Isso é seguido por uma etapa de requisição HTTP com um cabeçalho com a chave X-Discourse-Workflow-Secret e o valor {{ $("Code").item.json.signature }}, tipo de conteúdo JSON e corpo da requisição {{ $("Code").item.json.payload }}.
O resultado é uma requisição HTTP como esta:
User-Agent: Faraday v2.14.4
Content-Length: 41
Accept: */*
Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Content-Type: application/json
X-Discourse-Workflow-Secret: sha256=9ad2820b545013bdd18d4e7e6139624b91f576498e528201fa7759ec58dd598c
{"the-json-payload": "goes-here"}
O X-Discourse-Workflow-Secret é uma assinatura HMAC-SHA256 usando o valor do argumento secret… assim como um webhook assinado! 
Pode não ser bonito, mas parece estar funcionando.