Webhook Verification
The SDK provides utilities to verify webhook signatures, ensuring that incoming webhook payloads genuinely originated from Felloh.
For general information about webhooks and the events available, see the Webhooks documentation.
Verifying Signatures
Felloh signs every webhook request with an HMAC-SHA256 signature sent in the X-Signature header. The Felloh::Webhooks module
provides two exportable functions to verify this signature:
verify_webhook_signature— returns1(true) or0(false)assert_webhook_signature— throwsFelloh::Error::WebhookSignatureif invalid
Both use timing-safe comparison to prevent timing attacks.
Parameters
- Name
payloadrequired- Type
- string
- Description
The raw request body. Must not be parsed or modified — use the raw body as received.
- Name
signaturerequired- Type
- string
- Description
The value of the
X-Signatureheader from the webhook request.
- Name
secretrequired- Type
- string
- Description
Your webhook signing secret from the Felloh dashboard.
Boolean Check
use Felloh::Webhooks qw(verify_webhook_signature);
my $is_valid = verify_webhook_signature(
payload => $raw_request_body,
signature => $request_headers->{'x-signature'},
secret => 'your-webhook-secret',
);
unless ($is_valid) {
# Return 401
print "Status: 401\n";
print "Invalid signature\n";
exit;
}
# Process the webhook...
Assert (dies on failure)
use Felloh::Webhooks qw(assert_webhook_signature);
use Felloh::Error;
eval {
assert_webhook_signature(
payload => $raw_request_body,
signature => $request_headers->{'x-signature'},
secret => 'your-webhook-secret',
);
};
if ($@ && $@->isa('Felloh::Error::WebhookSignature')) {
# Return 401
print "Status: 401\n";
print "Invalid signature\n";
exit;
}
# Signature is valid, process the webhook...
Mojolicious Example
When using Mojolicious, use $c->req->body to get the raw request body for signature verification.
Mojolicious Webhook Handler
use Mojolicious::Lite;
use Felloh::Webhooks qw(assert_webhook_signature);
use Felloh::Error;
use JSON::PP qw(decode_json);
post '/webhooks/felloh' => sub {
my ($c) = @_;
my $body = $c->req->body;
eval {
assert_webhook_signature(
payload => $body,
signature => $c->req->headers->header('X-Signature'),
secret => $ENV{FELLOH_WEBHOOK_SECRET},
);
};
if ($@ && $@->isa('Felloh::Error::WebhookSignature')) {
return $c->render(text => 'Invalid signature', status => 401);
}
my $event = decode_json($body);
app->log->info("Webhook received: $event->{type}");
$c->render(text => 'OK', status => 200);
};
app->start;
Dancer2 Example
When using Dancer2, use request->body to get the raw request body.
Dancer2 Webhook Handler
use Dancer2;
use Felloh::Webhooks qw(assert_webhook_signature);
use Felloh::Error;
use JSON::PP qw(decode_json);
post '/webhooks/felloh' => sub {
my $body = request->body;
eval {
assert_webhook_signature(
payload => $body,
signature => request->header('X-Signature'),
secret => $ENV{FELLOH_WEBHOOK_SECRET},
);
};
if ($@ && $@->isa('Felloh::Error::WebhookSignature')) {
status 401;
return 'Invalid signature';
}
my $event = decode_json($body);
debug "Webhook received: $event->{type}";
status 200;
return 'OK';
};
start;
