arthem/request-signer-bundle
Install the package:
composer require arthem/request-signer-bundle
For JWT or AWS S3 support, install the required adapters:
composer require arthem/jwt-request-signer aws/aws-sdk-php
Configure the bundle in config/packages/arthem_request_signer.yaml:
arthem_request_signer:
signers:
my_jwt_signer:
jwt:
ttl: 3600
signing_key: '%env(resolve:MY_SIGNING_KEY)%'
Define environment variables in .env:
MY_SIGNING_KEY=your_secure_key_here
First use case: Sign a URL in a controller or normalizer:
use Arthem\RequestSignerBundle\RequestSigner;
public function generateSignedUrl(RequestSigner $signer): string
{
return $signer->signUri(
'https://example.com/asset.jpg',
$request,
['signer' => 'my_jwt_signer']
);
}
$signedUrl = $signer->signUri(
$this->urlGenerator->generate('asset_download', ['id' => $assetId]),
$request,
['signer' => 'aws_images', 'ResponseContentDisposition' => 'attachment']
);
public function secureAction(Request $request, RequestSigner $signer)
{
try {
$signer->validateRequest($request);
// Proceed if valid
} catch (InvalidSignatureException $e) {
throw new AccessDeniedHttpException();
}
}
signer option:
$signer->signUri($url, $request, ['signer' => 'my_jwt_signer']);
kernel.request).<a href="{{ signedUrl('asset_download', asset.id) }}">Download</a>
(Requires a Twig extension or helper.)ResponseContentDisposition to force downloads:
$signer->signUri($url, $request, ['ResponseContentDisposition' => 'attachment']);
serialize() in entity normalizers to inject signed URLs:
public function serialize($object, $format, array $context)
{
$data = parent::serialize($object, $format, $context);
$data['downloadUrl'] = $this->signer->signUri(
$this->urlGenerator->generate('api_asset_download', ['id' => $object->getId()])
);
return $data;
}
public function __invoke(Request $request, RequestSigner $signer)
{
$signer->validateRequest($request);
return $next($request);
}
Register in config/routes/protected.yaml:
_protected:
path: /
controller: App\Middleware\ValidateSignedRequestMiddleware
$client = new Client();
$signedUrl = $signer->signUri('https://api.example.com/data');
$response = $client->get($signedUrl);
ttl seconds). Use NTP or a time-sync service.ttl (e.g., 300 seconds) or implement clock drift handling.%env() and restrict IAM permissions.$signer->validateRequest($request) in controllers/middleware.UrlGeneratorInterface::ABSOLUTE_URL and let the bundle handle encoding.league/url-signatures.%env(resolve:MY_SIGNING_KEY)% matches the key used to sign the URL.GET). Mismatches cause failures.error_log($signer->signUri($url, $request));
s3:GetObject permissions.aws s3 presign s3://my_bucket/file.jpg --expires-in 3600
signer is specified in signUri(), the bundle uses the first configured signer (alphabetical order).ttl in the config is in seconds, not minutes/hours.3600 for 1 hour, not 60.%env(resolve:VAR)% to resolve nested env vars (e.g., MY_SIGNING_KEY from .env.local)..env.local is loaded (Symfony’s default behavior).Arthem\RequestSignerBundle\Signer\SignerInterface:
class CustomSigner implements SignerInterface
{
public function sign(string $uri, Request $request, array $options): string
{
// Custom logic
}
public function validate(string $uri, Request $request, array $options): bool
{
// Custom validation
}
}
config/packages/arthem_request_signer.yaml:
services:
app.custom_signer:
class: App\Signer\CustomSigner
tags: ['arthem_request_signer.signer']
arthem_request_signer:
signers:
custom_signer: ~
// Hypothetical event (check bundle source for actual events)
$eventDispatcher->addListener(
RequestSignerEvents::SIGNING,
function (SigningEvent $event) {
// Modify $event->getOptions() or $event->setUri()
}
);
$cacheKey = md5($url . $request->getClientIp());
$signedUrl = $cache->get($cacheKey, function() use ($signer, $url, $request) {
return $signer->signUri($url, $request);
});
How can I help you explore Laravel packages today?