Skip to content

Instantly share code, notes, and snippets.

@erdum
Created February 10, 2026 19:47
Show Gist options
  • Select an option

  • Save erdum/0bb90c4176e55249ca4b95a145848055 to your computer and use it in GitHub Desktop.

Select an option

Save erdum/0bb90c4176e55249ca4b95a145848055 to your computer and use it in GitHub Desktop.
Middleware for Verifying Inter-Service API Calls
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class VerifyHMAC
{
public function handle(Request $request, Closure $next)
{
$signature = $request->header('X-Signature');
$timestamp = $request->header('X-Timestamp');
$nonce = $request->header('X-Nonce');
if (!$signature || !$timestamp || !$nonce) {
abort(401, 'Missing HMAC headers');
}
$now = time();
$ttl = config('services.hmac.ttl', 30);
if (!is_numeric($timestamp) || abs($now - (int) $timestamp) > $ttl) {
abort(401, 'Expired request');
}
$nonce_key = "hmac_nonce:{$nonce}";
if (! Cache::add($nonce_key, true, $ttl)) {
abort(401, 'Replay detected');
}
$body = $request->getContent() ?? '';
$body_hash = hash('sha256', $body);
$canonical = implode("\n", [
strtoupper($request->method()),
'/' . ltrim($request->path(), '/'),
$body_hash,
$timestamp,
$nonce,
]);
$expected = hash_hmac(
'sha256',
$canonical,
config("services.hmac.key")
);
if (!hash_equals($expected, $signature)) {
abort(401, 'Invalid signature');
}
return $next($request);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment