Um serviço remoto não consegue acessar localhost diretamente. O túnel oferece uma URL HTTPS pública e encaminha os headers reais e o body original ao servidor local. Preserve o raw body antes do JSON parser. Valide assinatura e dados, grave a entrega de forma durável e só então execute a lógica de negócio.
Como o webhook do Linear chega a uma aplicação local
Um serviço remoto não consegue acessar localhost diretamente. O túnel oferece uma URL HTTPS pública e encaminha os headers reais e o body original ao servidor local. >documentação oficial
1. Crie um endpoint que preserve o raw body
Preserve o raw body antes do JSON parser. Valide assinatura e dados, grave a entrega de forma durável e só então execute a lógica de negócio.
import express from "express";
import crypto from "node:crypto";
const app = express();
const secret = process.env.LINEAR_WEBHOOK_SECRET;
app.post(
"/webhooks/linear",
express.raw({ type: "application/json", limit: "1mb" }),
async (req, res) => {
const rawBody = req.body;
const signature = req.get("linear-signature");
if (!verifyLinearSignature(signature, rawBody, secret)) {
return res.sendStatus(401);
}
let payload;
try {
payload = JSON.parse(rawBody.toString("utf8"));
} catch {
return res.sendStatus(400);
}
if (!Number.isFinite(payload.webhookTimestamp) ||
Math.abs(Date.now() - payload.webhookTimestamp) > 60_000) {
return res.sendStatus(401);
}
const deliveryId = req.get("linear-delivery") || payload.webhookId;
if (!deliveryId) return res.sendStatus(400);
try {
await recordAndEnqueueOnce(deliveryId, payload);
return res.sendStatus(200);
} catch (error) {
console.error("Linear webhook persistence failed", error);
return res.sendStatus(500);
}
}
);
app.use(express.json());
app.listen(3000);
Preserve o raw body antes do JSON parser. Valide assinatura e dados, grave a entrega de forma durável e só então execute a lógica de negócio. >documentação oficial
2. Exponha a porta local por HTTPS
Inicie o servidor e mantenha o túnel aberto em outro terminal. Um 401 para uma requisição sem assinatura confirma que o routing funciona e a autenticação rejeita corretamente.
npx portpreview 3000
https://example.portpreview.dev/webhooks/linear
Inicie o servidor e mantenha o túnel aberto em outro terminal. Um 401 para uma requisição sem assinatura confirma que o routing funciona e a autenticação rejeita corretamente.
3. Configure o webhook no Linear
Cadastre a URL HTTPS completa, assine apenas os eventos necessários e gere um caso real no ambiente de teste. Guarde o secret fora do repositório.
Cadastre a URL HTTPS completa, assine apenas os eventos necessários e gere um caso real no ambiente de teste. Guarde o secret fora do repositório.
Valide a assinatura sobre os bytes exatos
Calcule o HMAC com o webhook secret e os dados exatos definidos pelo provedor. Compare em tempo constante e nunca registre o secret.
function verifyLinearSignature(signature, rawBody, secret) {
if (!secret || typeof signature !== "string" ||
!/^[0-9a-f]{64}$/i.test(signature)) {
return false;
}
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest();
const actual = Buffer.from(signature, "hex");
return actual.length === expected.length &&
crypto.timingSafeEqual(actual, expected);
}
Calcule o HMAC com o webhook secret e os dados exatos definidos pelo provedor. Compare em tempo constante e nunca registre o secret. >guia prático
Evite ataques de replay com o timestamp
Confira a validade do timestamp depois da assinatura e mantenha o relógio do host sincronizado.
Confira a validade do timestamp depois da assinatura e mantenha o relógio do host sincronizado.
Entenda o payload e os identificadores de entrega
Processe apenas type conhecidos, aceite campos adicionais e elimine duplicidades com um ID de entrega estável em armazenamento durável.
Processe apenas type conhecidos, aceite campos adicionais e elimine duplicidades com um ID de entrega estável em armazenamento durável.
Responda rápido e processe com idempotência
Reserve o ID e crie o job na mesma transaction antes de responder 200. Se a persistência falhar, retorne erro para permitir retry.
Reserve o ID e crie o job na mesma transaction antes de responder 200. Se a persistência falhar, retorne erro para permitir retry. >guia prático
Teste o fluxo local completo
- Inicie o servidor e mantenha o túnel aberto em outro terminal. Um 401 para uma requisição sem assinatura confirma que o routing funciona e a autenticação rejeita corretamente.
- Cadastre a URL HTTPS completa, assine apenas os eventos necessários e gere um caso real no ambiente de teste. Guarde o secret fora do repositório.
- Calcule o HMAC com o webhook secret e os dados exatos definidos pelo provedor. Compare em tempo constante e nunca registre o secret.
- Reserve o ID e crie o job na mesma transaction antes de responder 200. Se a persistência falhar, retorne erro para permitir retry.
Confira routing, headers, assinatura, tempo de resposta e idempotência. Use requisições sintéticas principalmente para testar rejeições.
Resolva falhas do webhook do Linear
- Inicie o servidor e mantenha o túnel aberto em outro terminal. Um 401 para uma requisição sem assinatura confirma que o routing funciona e a autenticação rejeita corretamente.
- Calcule o HMAC com o webhook secret e os dados exatos definidos pelo provedor. Compare em tempo constante e nunca registre o secret.
- Confira a validade do timestamp depois da assinatura e mantenha o relógio do host sincronizado.
- Revise o POST path, a port do túnel, o raw body, o secret, o relógio e a latência do banco de dados.
Checklist de segurança para desenvolvimento e produção
- Exija HTTPS, limite tamanho e method, proteja secrets, remova dados sensíveis dos logs e exclua URLs de teste antigas.
- Preserve o raw body antes do JSON parser. Valide assinatura e dados, grave a entrega de forma durável e só então execute a lógica de negócio.
- Reserve o ID e crie o job na mesma transaction antes de responder 200. Se a persistência falhar, retorne erro para permitir retry.
Exija HTTPS, limite tamanho e method, proteja secrets, remova dados sensíveis dos logs e exclua URLs de teste antigas. >guia prático
