Manually Verifying Webhook Signatures

PostGrid supports sending webhook payloads in two separate formats: a JSON web token (JWT) and regular JSON. Verifying the JWT with your webhook's secret is straightforward and automatically ensures that the payload was sent by PostGrid and is recent enough.

However, you may prefer to receive the payload as JSON since this is typically easier to integrate into existing codebases/tools. If you choose to do this, it is up to you to verify the request is sent by PostGrid.

You will have to:

  1. Get the raw body of the request
  2. Extract the signature header value
  3. Calculate the HMAC of the raw body using the SHA-256 hash function and the secret; and
  4. Compare the calculated HMAC with one sent in the PostGrid-Signature header, making sure that both values use the same encoding.

Verifying the webhook signature

PostGrid will make the request to your webhook endpoint with a PostGrid-Signature header. Here's an example of what that will look like:

PostGrid-Signature: t=1718932335515,v1=e888f596cf2db26b2f58631cb28d620882022c5727a8f5a558f328f8d886e11d

This signature begins with the timestamp (unix milliseconds) that PostGrid made the request, and is followed by a hash-based message authentication code (HMAC) using SHA-256.

Step 1: Extract the timestamp and signatures from the header

  1. Split the header using the , character. This gets a list of elements consisting of the timestamp t, and the corresponding signature v1.
  2. Split each element using the = character as the separator. This gives a prefix and value pair for both the timestamp, t and signature v1.

Step 2: Prepare the signedPayload string

The signedPayload string is created by concatenating

  • The timestamp, t (as a string) +
  • A period . +
  • The raw body of the request (JSON with no whitespace)

Step 3: Determine the expected signature

Compute the HMAC of the thesignedPayload (message/expected signature) by using the SHA256 algorithm and secret key (found in the 'Additional Information' of the 'Webhook Details' section in the dashboard)

Step 4: Compare the signatures

Compare the signature, v1 from the header, to the newly calculated expected signature. If they do not match the webhook event should be rejected.

To prevent timing attacks, use a constant-time-string comparison to compare the expected signature with each of the received signatures.