twogether/laravel-url-signer

Signs and validates URLs with public/private keys

2.5.1 2023-12-14 20:07 UTC

This package is auto-updated.

Last update: 2024-04-14 21:12:35 UTC


README

Signs and validates URLs with public/private keys. This is used so that a Source application can make one time calls to a Target application and the Target can verify they're legit.

This package is designed for Laravel, but can be used by other things with some additional configuration.

Installing

First install the package. If you're using Laravel, it adds an artisan command which you can use to generate a key pair if you need one.

php artisan twogether:generate-key-pair

You'll want to install the package into both the Source and Target.

If you're not using Laravel, you will want to generate an RSA256 Public/Private Key Pair.

Configuring the Factory

If you are using Laravel, the Service Provider will automatically register a factory that uses a config file that publishes to config/signed_urls to store keys, and uses the Redis facade. app('Twogether\URLSigner') will return an instance of this.

If you are configuring manually, then you need to create a Factory.

new Twogether\LaravelURLSigner\SignedUrlFactory(
    string $appName, 
    CacheBroker $cacheBroker, 
    KeyProvider $keyProvider = null
)

App Name is simply a short string identifying this application. Avoid using spaces, and keep it simple. The Target will use this to verify that requests from this source are unique, so you should not duplicate app_names and use them with the same target.

The CacheBroker is used to validate one-time nonce codes to prevent replay attacks. It expects to use Redis for this, and a PredisCacheBroker class is available for you to use by passing in an instance of Predis\Client. If you are not using Redis, or if you do not use Predis, then you can check this class to see what it does and implement your own by implementing the interface in Contracts\CacheBroker.

Lastly the KeyProvider is optional. If you do not choose to use it then you will have to explicitly set the key every time you sign or validate a URL. We have provided an array implementation that you can configure:

new Twogether\LaravelURLSigner\KeyProviders\ArrayKeyProvider([
   'default' => [
       'public' => '', // Public key string
       'private' => '', // Private key string
   ]
]);

You do not need to specify both public and private keys if you do not need them. A Target application that only receives requests and does not make them does not need a private key for example. And vice versa.

If you want to sign or validate URLs with multiple services, you can add additional keyName => [pair] entries to this array.

Signing

Now when you want to make a request from the Source, generate your target URL, and pass it through:

$factory->sign(string $url, string $keyName = 'default')

This will return your signed URL, or throw exceptions if your configuration is off.

You can also call make which returns a SignedUrl object that supports additional fluent configuration if you want to specify additional options. e.g:

$factory->make('https://example.com')
    ->withKey(EXPLICIT PRIVATE KEY HERE)
    ->withExpiry(time()+300) // 5 minute expiry
    ->get() // returns the URL as a string

Here we can explicitly set the private key if we did not configure a provider. We can also specify that this URL will be valid for 5 minutes (default is 2).

Validating

$factory->validate($url,string $keyName = 'default', string $publicKey = '')

will validate the signed URL and return true if it passes. You can either specify a keyName for your KeyProvider to fetch the public key, or pass the key as a string if you prefer.

If it fails, it will throw an InvalidSignedUrl exception which has one method:

errors(): array An array of validation errors, keyed by field to work with a Laravel Form Request. You may prefer to use Middleware. Note that the list of errors is not exhaustive, if something's wrong it will error immediately and only include the last error unless everything was missing.

Get started with Laravel

To get started quickly, publish the config file and then set up a public or private key in keys.default in config/signed_urls.

Now just add:

Twogether\LaravelURLSigner\Middleware\SignedURL:class

to a Middleware stack.

The middleware also supports a keyName if you want to set up your routes. Alias the middleware in your kernel to signed_urls and then specify something like signed_urls:reporting to use the public key assigned to 'reporting' in your config.

Note on keys

Your public and private keys can either be a one line string, or the proper 64-character per-line versions with -----BEGIN----- and -----END-----. The library will handle either.