# Проверка подписи

Как убедиться, что уведомление пришло от нас.

Секрет уведомлений (`whsec_…`) показывается один раз — при создании ключа или когда вы просите новый.

Подпись — HMAC-SHA256 от строки `<t>.<тело запроса как есть>` вашим секретом, в шестнадцатеричном виде:

1. возьмите из заголовка `Golosar-Signature` значения `t` и `v1`;
2. посчитайте HMAC-SHA256 от `t + "." + тело` секретом;
3. сравните с `v1` безопасным сравнением;
4. проверьте, что `t` не старше 30 минут — так старое уведомление не повторят.

Подпись считается от тела **как оно пришло** — не разбирайте JSON и не собирайте его заново перед проверкой.

```python
import hashlib, hmac, os, time

def verify(secret: str, body: bytes, header: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t = int(parts["t"])
    if abs(time.time() - t) > 1800:
        return False
    mac = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(mac, parts["v1"])

# Flask
from flask import Flask, request, abort
app = Flask(__name__)

@app.post("/golosar/webhook")
def hook():
    if not verify(os.environ["GOLOSAR_WHSEC"], request.get_data(), request.headers["Golosar-Signature"]):
        abort(400)
    event = request.get_json()
    print(event["type"], event["data"]["job"]["id"])
    return "", 204
```

```javascript
import crypto from "node:crypto";
import express from "express";

export function verify(secret, rawBody, header) {
  const p = Object.fromEntries(header.split(",").map((x) => x.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(p.t)) > 1800) return false;
  const mac = crypto.createHmac("sha256", secret).update(`${p.t}.`).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(p.v1));
}

const app = express();
app.post("/golosar/webhook", express.raw({ type: "application/json" }), (req, res) => {
  if (!verify(process.env.GOLOSAR_WHSEC, req.body, req.get("Golosar-Signature"))) return res.sendStatus(400);
  const event = JSON.parse(req.body);
  console.log(event.type, event.data.job.id);
  res.sendStatus(204);
});
```
