jeffersongoncalves/laravel-webhook-signatures
Installation:
composer require jeffersongoncalves/laravel-webhook-signatures
Publish the config file (if needed):
php artisan vendor:publish --provider="JeffersonGoncalves\LaravelWebhookSignatures\WebhookSignaturesServiceProvider"
First Use Case: Verify a webhook payload from Mailgun (example):
use JeffersonGoncalves\LaravelWebhookSignatures\Facades\WebhookSignatures;
$payload = file_get_contents('php://input');
$headers = getallheaders();
$isValid = WebhookSignatures::verify(
$payload,
$headers['X-Signature'],
'your-mailgun-webhook-secret'
);
if ($isValid) {
// Process the webhook
}
Key Config:
Check config/webhook-signatures.php for supported services (Mailgun, SendGrid, etc.) and their default headers.
Middleware for Automatic Verification: Create a middleware to validate all incoming webhooks:
namespace App\Http\Middleware;
use Closure;
use JeffersonGoncalves\LaravelWebhookSignatures\Facades\WebhookSignatures;
class VerifyWebhookSignature
{
public function handle($request, Closure $next)
{
$service = config('webhook-signatures.services.mailgun.secret');
$isValid = WebhookSignatures::verify(
$request->getContent(),
$request->header('X-Signature'),
$service
);
if (!$isValid) {
abort(403, 'Invalid webhook signature');
}
return $next($request);
}
}
Register in app/Http/Kernel.php:
protected $routeMiddleware = [
'verify.webhook' => \App\Http\Middleware\VerifyWebhookSignature::class,
];
Use in routes:
Route::post('/webhook/mailgun', [WebhookController::class, 'handle'])
->middleware('verify.webhook');
Dynamic Service Configuration: Override service-specific settings per route:
Route::post('/webhook/sendgrid', [WebhookController::class, 'handle'])
->middleware(function ($request, $next) {
config(['webhook-signatures.services.sendgrid.secret' => 'your-sendgrid-secret']);
return $next($request);
});
Event-Based Processing: Dispatch events after validation:
if ($isValid) {
event(new WebhookReceived($payload, $headers));
}
if (!$isValid) {
\Log::warning('Webhook validation failed', [
'service' => 'mailgun',
'signature' => $headers['X-Signature'] ?? null,
]);
}
WebhookSignatures::fake() for unit tests:
public function test_webhook_validation()
{
WebhookSignatures::fake(['mailgun' => true]);
$response = $this->post('/webhook/mailgun', [], [
'X-Signature' => 'valid-sig',
]);
$response->assertOk();
}
Header Case Sensitivity:
Some services (e.g., AWS SNS) use X-Amz-Signature instead of X-Signature. Ensure your config matches the exact header name.
Payload Encoding:
Always use file_get_contents('php://input') or $request->getContent()—never json_decode($request->input()) directly, as it may strip headers or alter payload structure.
Secret Management:
Avoid hardcoding secrets in middleware. Use Laravel’s config() or environment variables:
$secret = config('services.mailgun.webhook_secret');
Time Synchronization: Webhook signatures rely on timestamps. Ensure your server’s clock is synchronized (e.g., via NTP).
Signature Mismatch: If validation fails, compare the computed signature with the received one:
$computedSig = WebhookSignatures::computeSignature($payload, $secret);
\Log::debug('Expected:', [$headers['X-Signature']]);
\Log::debug('Computed:', [$computedSig]);
Service-Specific Quirks:
X-SendGrid-Signature and includes a timestamp in the payload.X-Amz-Signature-Version header (default: 2).Custom Services: Extend the package by adding a new service class:
namespace App\Services;
use JeffersonGoncalves\LaravelWebhookSignatures\Contracts\WebhookService;
class CustomService implements WebhookService
{
public function verify($payload, $signature, $secret)
{
// Custom logic
}
}
Register in config/webhook-signatures.php:
'services' => [
'custom' => [
'secret' => env('CUSTOM_WEBHOOK_SECRET'),
'class' => \App\Services\CustomService::class,
],
];
Override Default Headers: Customize headers per service in the config:
'services' => [
'mailgun' => [
'secret' => env('MAILGUN_SECRET'),
'header' => 'X-Mailgun-Signature', // Override default
],
];
Algorithm Flexibility:
If the package doesn’t support your signing algorithm (e.g., HMAC-SHA512), fork the package or extend the computeSignature method.
How can I help you explore Laravel packages today?