Verifying Webhooks
Shopfront supplies two headers that allow you to ensure that the webhook provided has come from Shopfront and is genuine. The signature can also assist in preventing against replay attacks.
Verification of webhooks is optional, but is highly recommended.
The two headers are:
X-Shopfront-Signature- The signature of the requestX-Shopfront-Signature-Time- The time that the request was signed
Verifying the webhook
Shopfront uses both the body of the request and the X-Shopfront-Signature-Time to generate
the X-Shopfront-Signature. First the signature time is prepended to the raw body and is then
signed using your webhook's signing key using SHA256.
To verify the webhook signature, you should create a signature on your side and compare it for exact equality with our signature provided.
This can be represented simply as the following:
HMAC("sha256", HEADER_SIGNATURE_TIME + REQUEST_BODY, WEBHOOK_SIGNING_KEY) = HEADER_SIGNATURE.
Verifying Webhooks
import { createHmac } from "crypto";
routes.post("/webhook", (request) => {
const body = request.rawBody;
const signatureTime = request.headers["X-Shopfront-Signature-Time"];
const signature = request.headers["X-Shopfront-Signature"];
const webhookId = request.body.webhookId;
// Get the signing key for your webhook from somewhere, it's better to
// cache or store this on your side instead of sending a request to
// Shopfront each time you receive a webhook.
const signingKey = getWebhookSigningKey(webhookId);
const hmac = createHmac("sha256", signingKey);
hmac.update(`${signatureTime}${body}`);
const recreated = hmac.digest("hex");
if(recreated === signature) {
// Webhook is valid and from Shopfront
} else {
// Webhook is invalid. Reject the request.
}
});
<?php
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
Route::post("/webhook", function(ServerRequestInterface $request): ResponseInterface {
$body = (string)$request->getBody();
$signatureTime = $request->getHeader("X-Shopfront-Signature-Time")[0];
$signature = $request->getHeader("X-Shopfront-Signature")[0];
$webhookId = $request->getParsedBody()->webhookId;
// Get the signing key for your webhook from somewhere, it's better to
// cache or store this on your side instead of sending a request to
// Shopfront each time you receive a webhook.
$signingKey = getWebhookSigningKey($webhookId);
if(hash_hmac("sha256", $signatureTime . $body, $signingKey) === $signature) {
// Webhook is valid and from Shopfront
} else {
// Webhook is invalid. Reject the request
}
});
Obtaining Your Webhook's Signing Key
Each webhook has its own signing key, this is a field on the webhook object
called signatureKey.
Each signing key should be treated as a credential and should not be exposed publicly or committed into code.
This key is randomly generated and will be individual per-webhook (not per-endpoint). It will not change unless
you call the regenerateWebhookSignature GraphQL mutation.
Regenerating Your Webhook's Signing Key
If your webhook signature is exposed or you need to regenerate it for any reason, you can call the
regenerateWebhookSignature GraphQL mutation method
with the ID of the webhook you want to regenerate the signature of.