BigblueDevelopers

Verify signatures

Each webhook request is signed with HMAC-SHA256 using your account's shared secret.

Check the signature on every request. It's the only way to know a request comes from Bigblue.

How the signature is made

X-Bigblue-Hmac-SHA256 = base64(HMAC-SHA256(raw_body, shared_secret))
Algorithm
HMAC-SHA256
Key
Your shared secret
One 30-character secret per account, used for all webhooks. Find it in the Bigblue app.
Message
The raw request body
The exact bytes, before parsing.
Encoding
Standard Base64
With padding. Not URL-safe Base64.

Use the raw body

Compute the HMAC on the body exactly as received. If you parse the JSON and serialize it again, the bytes change and the signature won't match. In Express, use express.raw() instead of express.json(). In Next.js, read await req.text() before parsing.

Code

import crypto from 'node:crypto';

export function verify(rawBody: Buffer | string, header: string | null): boolean {
  if (!header) return false;

  const expected = crypto
    .createHmac('sha256', process.env.BIGBLUE_WEBHOOK_SECRET!)
    .update(rawBody)
    .digest('base64');

  const a = Buffer.from(header);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Compare signatures with a constant-time function, as in these examples, not with ==.

The shared secret

  • There is one secret per account, used for all webhooks.
  • Copy it from the Bigblue app before you register your first webhook. The verification request is already signed with it.
  • It's different from your API key. The API key authenticates your requests to Bigblue. The secret authenticates Bigblue's requests to you.
  • When you rotate the secret, old signatures stop working immediately. Rotate at a quiet time. Requests that fail during the change are retried.

Store the secret in a secret manager.

Invalid signatures

Return 401. Bigblue retries the request, so real events that failed during a secret rotation are delivered again. Log rejected requests with the event type and time.

On this page