Зовнішній сервіс не може напряму звернутися до localhost. Тунель надає публічний HTTPS URL і передає локальному серверу справжні headers та незмінений body. Збережіть raw body до JSON parser. Спочатку перевірте підпис і дані, надійно запишіть доставку й лише потім запускайте бізнес-логіку.
Як webhook Linear потрапляє до локального застосунку
Зовнішній сервіс не може напряму звернутися до localhost. Тунель надає публічний HTTPS URL і передає локальному серверу справжні headers та незмінений body. >офіційна документація
1. Створіть endpoint зі збереженням raw body
Збережіть raw body до JSON parser. Спочатку перевірте підпис і дані, надійно запишіть доставку й лише потім запускайте бізнес-логіку.
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);
Збережіть raw body до JSON parser. Спочатку перевірте підпис і дані, надійно запишіть доставку й лише потім запускайте бізнес-логіку. >офіційна документація
2. Відкрийте локальний порт через HTTPS
Запустіть сервер і тримайте тунель відкритим у другому терміналі. Відповідь 401 на запит без підпису підтверджує правильний routing.
npx portpreview 3000
https://example.portpreview.dev/webhooks/linear
Запустіть сервер і тримайте тунель відкритим у другому терміналі. Відповідь 401 на запит без підпису підтверджує правильний routing.
3. Налаштуйте webhook у Linear
Укажіть повний HTTPS URL, підпишіться лише на потрібні події та виконайте реальний сценарій у тестовому середовищі. Зберігайте secret поза репозиторієм.
Укажіть повний HTTPS URL, підпишіться лише на потрібні події та виконайте реальний сценарій у тестовому середовищі. Зберігайте secret поза репозиторієм.
Перевірте підпис за точними байтами
Обчисліть HMAC із webhook secret і точними даними зі специфікації. Порівнюйте за сталий час і не записуйте 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);
}
Обчисліть HMAC із webhook secret і точними даними зі специфікації. Порівнюйте за сталий час і не записуйте secret у логи. >практичний посібник
Захистіться від replay за допомогою timestamp
Після перевірки підпису контролюйте свіжість timestamp і синхронізуйте годинник сервера.
Після перевірки підпису контролюйте свіжість timestamp і синхронізуйте годинник сервера.
Розберіться з payload та ID доставки
Обробляйте відомі type, приймайте додаткові поля та усувайте дублікати за стабільним ID доставки.
Обробляйте відомі type, приймайте додаткові поля та усувайте дублікати за стабільним ID доставки.
Відповідайте швидко й обробляйте ідемпотентно
Резервуйте ID доставки та створюйте job в одній transaction до відповіді 200. Якщо storage недоступний, поверніть помилку для retry.
Резервуйте ID доставки та створюйте job в одній transaction до відповіді 200. Якщо storage недоступний, поверніть помилку для retry. >практичний посібник
Протестуйте весь локальний потік
- Запустіть сервер і тримайте тунель відкритим у другому терміналі. Відповідь 401 на запит без підпису підтверджує правильний routing.
- Укажіть повний HTTPS URL, підпишіться лише на потрібні події та виконайте реальний сценарій у тестовому середовищі. Зберігайте secret поза репозиторієм.
- Обчисліть HMAC із webhook secret і точними даними зі специфікації. Порівнюйте за сталий час і не записуйте secret у логи.
- Резервуйте ID доставки та створюйте job в одній transaction до відповіді 200. Якщо storage недоступний, поверніть помилку для retry.
Перевірте routing, headers, підпис, час відповіді та ідемпотентність. Синтетичні запити корисні для сценаріїв відмови.
Діагностика помилок webhook Linear
- Запустіть сервер і тримайте тунель відкритим у другому терміналі. Відповідь 401 на запит без підпису підтверджує правильний routing.
- Обчисліть HMAC із webhook secret і точними даними зі специфікації. Порівнюйте за сталий час і не записуйте secret у логи.
- Після перевірки підпису контролюйте свіжість timestamp і синхронізуйте годинник сервера.
- Перевірте POST path, port тунелю, raw body, secret, годинник і затримку бази даних.
Перевірка безпеки для розробки та production
- Вимагайте HTTPS, обмежте розмір і method, захищайте secrets, очищуйте логи та видаляйте застарілі тестові URL.
- Збережіть raw body до JSON parser. Спочатку перевірте підпис і дані, надійно запишіть доставку й лише потім запускайте бізнес-логіку.
- Резервуйте ID доставки та створюйте job в одній transaction до відповіді 200. Якщо storage недоступний, поверніть помилку для retry.
Вимагайте HTTPS, обмежте розмір і method, захищайте secrets, очищуйте логи та видаляйте застарілі тестові URL. >практичний посібник
