HMAC still verifies after forwarding through Adal
Receiving webhooks from a payment provider is one of those cases where “almost correct” is not enough.
I use Adal in production to receive webhooks from a payment provider. The request goes through Adal Server and Adal CLI, then reaches the final handler — and its HMAC signature verifies successfully. This is practical confirmation that the signed data arrives unchanged.
A webhook like this can report a successful payment, a refund, a cancellation, or a change in transaction status. It is not enough to simply receive it. You also need to make sure the request really came from the payment provider and was not modified along the way. That is why providers usually sign webhooks with HMAC.
This week, an ordinary work situation reminded me again why I use Adal in my own production infrastructure.
Why Adal sits between the payment provider and the handler
My route looks like this:
Payment service → Adal Server → Adal CLI → webhook handler
Here, Adal acts as an intermediate layer. It receives the incoming request at a stable public HTTPS URL, stores it, and forwards it through Adal CLI into a private environment where the final handler runs.
This gives me several practical benefits:
the webhook is first accepted reliably by Adal;
- the request and its delivery state are visible in the UI;
- if the handler is temporarily unavailable, delivery can be retried;
- the private environment does not need a public inbound address or an open port just to receive webhooks.
For payment events, this is especially useful. If something goes wrong, I still have the accepted Request, the delivery attempt history, and a way to investigate the problem without guessing from scattered logs.
But an intermediate layer has one important requirement: it must not silently change the data that signature verification depends on.
What HMAC verifies in this route
The exact scheme depends on the payment provider. Typically, the sender calculates a signature from the request body and a shared secret; sometimes the signed string also includes a timestamp or other fields. The receiver performs the same calculation and compares the result with the signature from the request.
If even a single byte of the signed body changes — for example, if JSON is transformed, whitespace changes, or the encoding is altered — the calculated signature will no longer match. The same applies to other values if they are part of that provider’s signature scheme.
In my case, verification does not happen inside Adal. It happens in the final handler, after the request has passed through the whole chain:
receive → store → send via CLI → deliver → HMAC check
And the verification succeeds.
That means the data included in the signature by the payment provider reached the handler in the same form in which it was sent. Adal did not parse the JSON and assemble it again, did not change the payload formatting, and did not introduce a hidden transformation that would break the signature.
Why this matters more than a synthetic test
You can write a test, send a prepared body, and compare the result. Those tests are useful, but a real payment webhook checks more things at once:
the request was created by an external system I do not control;
- the signature was calculated by that system according to its own rules;
- the request was accepted by the public Adal Server;
- it was stored and forwarded through the real production route;
- the final application independently verified the signature.
The successful result is not based on Adal and a test using the same prebuilt string. It is an end-to-end check on real traffic.
It is important to state the conclusion precisely: HMAC confirms that the parts of the request included in a given provider’s signature were not changed. It does not prove byte-for-byte equality for every element of the HTTP connection — transport headers, for example, may be regenerated on each hop. But for the webhook payload and other signed values, it is strong and practically meaningful confirmation.
A product I use myself
Adal was built around a simple principle: webhook delivery should be transparent and predictable, without hidden transformations.
What I especially like about this case is that the confirmation does not come from marketing copy. I use Adal for my own production tasks, I can see accepted requests and delivery history, I can retry delivery when needed — and my handler still successfully verifies the payment provider’s HMAC after the request has passed through Adal.
That is exactly how an intermediate layer should behave: it should add reliability and observability without becoming a source of unexpected changes.