Installation:
composer require code4nix/uri-signer
Ensure your Laravel project uses Symfony’s HttpFoundation (already included in Laravel via illuminate/http).
Service Registration:
Add the service to Laravel’s container in config/app.php under providers:
Code4Nix\UriSigner\UriSignerBundle::class,
Publish the config (if needed) via:
php artisan vendor:publish --provider="Code4Nix\UriSigner\UriSignerBundle"
First Use Case: Sign a URL in a controller:
use Code4Nix\UriSigner\UriSigner;
public function generateSignedUrl(UriSigner $uriSigner)
{
$unsignedUrl = url('download/file.pdf');
$signedUrl = $uriSigner->sign($unsignedUrl, 3600); // Expires in 1 hour
return response()->json(['url' => $signedUrl]);
}
Signing URLs:
sign() for one-off URL generation (e.g., download links, API endpoints).$uriSigner->sign('https://example.com/file', 900); // 15 minutes
Validation:
$isValid = $uriSigner->check($signedUrl); // Returns bool
$isValid = $uriSigner->checkRequest($request);
try {
$uriSigner->check($signedUrl, true); // Throws exceptions on failure
} catch (ExpiredLinkException $e) {
abort(403, 'Link expired');
}
Integration with Laravel Middleware: Protect routes requiring signed URLs:
namespace App\Http\Middleware;
use Closure;
use Code4Nix\UriSigner\UriSigner;
class VerifySignedUrl
{
public function __construct(private UriSigner $uriSigner) {}
public function handle($request, Closure $next)
{
if (!$this->uriSigner->checkRequest($request)) {
abort(403);
}
return $next($request);
}
}
Register in app/Http/Kernel.php:
protected $routeMiddleware = [
'signed.url' => \App\Http\Middleware\VerifySignedUrl::class,
];
Use in routes:
Route::get('/protected', function () { ... })->middleware('signed.url');
Dynamic Expiration Logic:
$expiry = $user->isPremium ? 86400 : 3600; // 1 day vs. 1 hour
$signedUrl = $uriSigner->sign($url, $expiry);
URI Encoding Issues:
url() or Str::of() helper:
$url = url('path?query=value'); // Automatically encodes
$decodedUrl = urldecode($signedUrl);
$isValid = $uriSigner->check($decodedUrl);
Time Synchronization:
UriSigner class extending the base to override time logic.Exception Handling:
check() method with true throws exceptions, but these are not documented in the README. Catch:
MalformedUriException: Invalid URL format.InvalidSignatureException: Tampered or malformed signature.ExpiredLinkException: URL expired.Secret Key Management:
SYMFONY__SECRET). Override it in Laravel’s .env:
SYMFONY__SECRET=your_custom_secret_here
Query String Order Sensitivity:
UriSigner sorts query parameters alphabetically before signing. Ensure consistency when reconstructing URLs:
// Bad: Order may differ
$url = 'http://example.com?b=2&a=1';
// Good: Use Laravel's URL helpers
$url = url()->full() . '?a=1&b=2';
Log Signed URLs:
\Log::debug('Signed URL:', ['url' => $signedUrl]);
UriSigner directly).Test Expiration:
$uriSigner = $this->app->makeWith(UriSigner::class, [
'time' => fn() => strtotime('+2 hours'), // Simulate future time
]);
Check for Hidden Characters:
trim() or Str::of($url)->trim() to remove accidental whitespace/newlines in URLs.Custom Expiration Logic:
UriSigner class to add dynamic rules:
namespace App\Services;
use Code4Nix\UriSigner\UriSigner as BaseUriSigner;
class CustomUriSigner extends BaseUriSigner
{
public function signWithRole($url, string $role, int $expiry = null)
{
$expiry = $expiry ?? $this->getRoleExpiry($role);
return parent::sign($url, $expiry);
}
private function getRoleExpiry(string $role): int
{
return match ($role) {
'admin' => 2592000, // 30 days
default => 3600, // 1 hour
};
}
}
AppServiceProvider:
$this->app->bind(UriSigner::class, CustomUriSigner::class);
Add Metadata to Signatures:
$uriSigner->sign($url, 3600, ['user_id' => auth()->id()]);
Rate Limiting:
throttle middleware to limit signed URL usage:
Route::get('/download', function () { ... })
->middleware(['signed.url', 'throttle:5,1']);
How can I help you explore Laravel packages today?