Azure Function Webhook Signature Verification Failing

Can’t get this config to load properly…

Context:
I have a .NET 8 Azure Function consuming Genesys Cloud webhooks. I am trying to implement replay attack protection by verifying the X-Genesys-Signature header against the request body using HMAC-SHA256. The function runs in France Central. My code logic is straightforward:

var key = Encoding.UTF8.GetBytes(_secret);
var hmac = new HMACSHA256(key);
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
var computedSig = Convert.ToBase64String(hash);
// comparison fails here

Question:
Is Genesys sending the signature based on the raw HTTP body bytes or a specific string representation? The computed hash never matches the header value, even though the secret is correct.

The official documentation states the signature covers the raw HTTP request body, not the deserialized object. Ensure you read the stream directly before any JSON parsing.

var body = await req.Content.ReadAsStringAsync();
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
var expected = Convert.ToBase64String(hash);

If you deserialize first, the HMAC will fail.

TL;DR: Signature mismatch stems from stream handling.

You might want to look at reading the body before deserialization. The suggestion above is correct. In .NET, ReadAsStringAsync consumes the stream. If you need to parse JSON later, you must reset the position.

var body = await req.Content.ReadAsStringAsync();
req.Content.Seek(0, SeekOrigin.Begin); // Reset for downstream
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));

The easiest fix here is this is ensuring you hash the exact raw buffer, not a string conversion that might alter encoding. In Node.js middleware, I usually buffer the stream first to avoid deserialization side effects.

const hash = crypto.createHmac('sha256', secret).update(rawBody).digest('base64');

Warning: Verify your secret key matches the exact webhook credential from the Genesys admin console.

It depends, but generally… stream consumption is the culprit.

  1. Read the raw body buffer first.
  2. Compute the HMAC-SHA256.
  3. Reset the stream position to zero for downstream processing.
var body = await req.Content.ReadAsStringAsync();
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
req.Content.Seek(0, SeekOrigin.Begin);