verifyWebhookSignature function

bool verifyWebhookSignature({
  1. required String payload,
  2. required String signature,
  3. required String timestamp,
  4. required String secret,
  5. int toleranceSeconds = defaultToleranceSeconds,
})

Verifies an inbound webhook.

MisarMail signs each webhook as HMAC-SHA256(timestamp + "." + rawBody) with the endpoint's signing secret. Verify against the RAW body, not a re-encoded map: key order and whitespace both change the digest. The comparison is constant-time so a timing oracle cannot recover the digest byte by byte.

Implementation

bool verifyWebhookSignature({
  required String payload,
  required String signature,
  required String timestamp,
  required String secret,
  int toleranceSeconds = defaultToleranceSeconds,
}) {
  if (payload.isEmpty || signature.isEmpty || timestamp.isEmpty || secret.isEmpty) {
    return false;
  }

  final sentAt = double.tryParse(timestamp);
  if (sentAt == null) return false;

  // Rejecting stale timestamps is what stops a captured request from being
  // replayed forever, so this is a real check rather than a formality.
  final now = DateTime.now().millisecondsSinceEpoch / 1000;
  if ((now - sentAt).abs() > toleranceSeconds) return false;

  return _constantTimeEquals(signWebhook(payload, timestamp, secret), signature.trim());
}