Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Psr7 Hmac Bundle Laravel Package

uma/psr7-hmac-bundle

A PSR-7 HMAC bundle for signing and verifying HTTP requests between services. It adds shared-secret authentication to PSR-7 middleware flows, helping protect APIs from tampering and unauthorized access, and supports validating incoming requests or signing outgoing calls.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first: uma/psr7-hmac-bundle is a Symfony bundle, not a Laravel package. It ships a DI extension, a security firewall listener, and a config tree that only exists inside symfony/framework-bundle + symfony/security. There is no service provider, no facade, no publishable config — nothing Laravel's container will autoload. It was last tagged in April 2017 and is archived, so the bundle itself is effectively a reference implementation, not a dependency.

The practical first step is therefore:

composer require 1ma/psr7-hmac

…and treat the bundle as documentation for how to wire the library into a framework's security layer. The library underneath (Psr7Hmac) is the reusable part: a Signer, a Verifier, and a KeyProviderInterface that maps a key identifier to a shared secret. It signs a canonical representation of a PSR-7 request (method, URI, body hash) and emits signature/key-id headers you can verify on the other side.

Before requiring anything, run composer why-not 1ma/psr7-hmac — it was written against psr/http-message ^1.0, and if the rest of your app (Guzzle 7.9+, nyholm/psr7, most modern SDKs) has settled on PSR-7 v2, you will be fighting the solver. If it resolves, your first use case is almost always server-to-server authentication: one Laravel app calling another's internal API, or receiving signed webhooks, without OAuth or session cookies.

Minimal bootstrapping:

  1. composer require 1ma/psr7-hmac nyholm/psr7 symfony/psr-http-message-bridge
  2. Bind a KeyProviderInterface implementation in a service provider, reading client secrets from config/hmac.php or an api_clients table.
  3. Bind Signer and Verifier as singletons around that provider.
  4. Write a middleware that converts the inbound Illuminate\Http\Request to PSR-7 and calls verify().

Implementation Patterns

Key provider as the seam. The library only knows about key IDs and secrets; everything else is yours. Implement KeyProviderInterface against an Eloquent model (ApiClient with key_id, secret, active, revoked_at) so rotation and revocation are database operations, not deploys. Cache the lookup per-request; HMAC verification happens on every call.

Inbound middleware (the bundle's firewall listener, ported). The bundle's job was: read signature headers → resolve the user → populate the security token. In Laravel that becomes a middleware registered on an api route group:

$psr7 = (new PsrHttpFactory(...))->createRequest($request);
$client = $this->verifier->verify($psr7);   // throws on bad signature
$request->attributes->set('hmac_client', $client);

Then either set Auth::setUser() via a custom guard, or — simpler and less magical — attach the resolved client as a request attribute and read it in the controller/FormRequest. Do not attempt to emulate Symfony's token storage; Laravel's guard + a HmacUser implementing Authenticatable is the idiomatic substitute if you truly need auth()->user().

Outbound signing via Guzzle middleware. Wrap your Signer in a Guzzle middleware so every outgoing call is signed in one place:

$stack->push(Middleware::mapRequest(fn ($r) => $this->signer->sign($r)));

If you're using Laravel's Http client, either drop to Guzzle for signed calls or attach the signature manually with Http::withHeaders([...])->withMiddleware(...) — the fingerprint must be computed over the exact final body and query string, so build the body string before handing it to the client, not after.

Replay protection. HMAC proves integrity, not freshness. Store the signature (or a nonce header) in Cache::add($sig, true, 300) and reject duplicates; enforce a timestamp header with ±300s skew. This is not in the bundle and is the single most common thing teams forget.

Verification-first for webhooks. Signature verification must run before any JSON parsing or model writes. Register the middleware globally for the webhook route group and reject with 401 immediately.

Testing. Bind an in-memory key provider in tests, freeze time with Carbon::setTestNow() for timestamp assertions, and use Http::fake() plus assertions on the emitted X-Hmac-* headers to prove the signing middleware ran.

Gotchas and Tips

The bundle cannot be installed into Laravel. Its composer.json requires symfony/framework-bundle and symfony/security-bundle in the 2.x/3.x line; Laravel 9+ pulls Symfony 6/7. Even if Composer let it through, Uma\Psr7HmacBundle\DependencyInjection\... would fatal at boot because Laravel never calls build(). Vendor the ~200 lines you need instead of requiring it.

PSR-7 v1 pin is the real blocker. psr/http-message ^1.0 conflicts with anything demanding ^2.0. If you can't resolve it, the library's whole value — a canonical signing string — is roughly 60 lines to reimplement with hash_hmac('sha256', $canonical, $secret) + hash_equals(). Do that before you pin an old Guzzle just to keep this alive.

Body streams are consumed once. PHP's php://input is single-read; Laravel's $request->getContent() caches it, but a PSR-7 bridge may hand the verifier an empty stream if the body was already read. Capture the raw body early ($request->getContent()) and build the PSR-7 request from it explicitly. This is the #1 source of "signature valid for GET, invalid for POST" bugs.

Proxy/URL drift breaks the canonical string. Signature schemes include scheme, host, path, and sorted query. Behind a load balancer, Laravel sees http and an internal host. Configure TrustProxies correctly, or better, exclude scheme/host from your canonical form and sign only method + path + sorted query + body hash.

Query-string normalization. Sort keys, use RFC 3986 encoding (spaces as %20, not +), and normalise repeated params identically on both sides. Verify with a fixed vector in a unit test before wiring anything real.

No maintainer, 8 stars, archived. Treat it as a frozen spec. Pin an exact version, add a comment, and schedule replacement. The API surface you depend on is three classes — trivially swappable.

Extension points that survive: KeyProviderInterface (multiple secrets, rotation, chaining), and the signer/verifier pair itself. The Symfony-specific pieces — the firewall listener, the uma_psr7_hmac config tree with its key-provider service id, the security token — have no Laravel analogue and are reading material, not code you port.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin