emfasemfas
Webhooks

Signature verification

Verify that a webhook request genuinely came from Emfas using the HMAC signature.

Every delivery is signed with your endpoint's secret so you can confirm it came from Emfas and wasn't tampered with. Always verify the signature before acting on a request.

The signature header

X-Emfas-Signature: t=1717406504,v1=3f8a1c...e9
  • t — the Unix timestamp (seconds) when Emfas signed the request.
  • v1 — the hex-encoded HMAC-SHA256 signature.

How to verify

  1. Read the raw request body — verify against the exact bytes received, before any JSON parsing or re-serialization.
  2. Build the signed payload by concatenating the timestamp, a ., and the raw body: {t}.{body}.
  3. Compute HMAC-SHA256(secret, signedPayload) and hex-encode it.
  4. Compare it to v1 using a constant-time comparison.
  5. Optionally, reject requests whose timestamp t is too old (e.g. more than 5 minutes) to limit replay.
import crypto from "node:crypto"

export function verifyEmfasSignature(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")))
  const { t, v1 } = parts
  if (!t || !v1) return false

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest("hex")

  // Constant-time comparison
  const a = Buffer.from(expected)
  const b = Buffer.from(v1)
  if (a.length !== b.length) return false
  if (!crypto.timingSafeEqual(a, b)) return false

  // Optional replay protection (5 minute tolerance)
  const age = Math.abs(Date.now() / 1000 - Number(t))
  return age < 300
}

Verify against the raw body bytes. If your framework parses JSON before you can read the raw body, configure it to expose the original payload — re-serializing the parsed object can change byte order or whitespace and break verification.

Rotating the secret

You can rotate an endpoint's secret in Settings → Emfas API → Webhooks. Rotation issues a new secret immediately and invalidates the old one, so update your verification configuration as part of the rotation.