diff --git a/README.md b/README.md index bbed3ca..d889679 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,34 @@ php artisan vendor:publish --tag="laravel-cloudfront-cookies-views" Add the middleware to set the cookies needed for AWS CloudFront +## Excluding CloudFront Cookies from Laravel Encryption + +Laravel encrypts cookies by default for security. However, CloudFront signed cookies must remain unencrypted to function properly. + +### Configuration Steps + +1. **Locate the EncryptCookies middleware** + - Path: `app/Http/Middleware/EncryptCookies.php` + +2. **Add CloudFront cookies to the exclusion list** + - Update the `$except` array to include the three CloudFront cookie names: + +```php +class EncryptCookies extends Middleware +{ + /** + * The names of the cookies that should not be encrypted. + * + * @var array + */ + protected $except = [ + 'CloudFront-Key-Pair-Id', + 'CloudFront-Policy', + 'CloudFront-Signature', + ]; +} +``` + ## Testing Generate a private key diff --git a/config/cloudfront-cookies.php b/config/cloudfront-cookies.php index fe830fd..7b6d210 100644 --- a/config/cloudfront-cookies.php +++ b/config/cloudfront-cookies.php @@ -3,17 +3,17 @@ declare(strict_types=1); return [ - 'version' => env('CLOUDFRONT_VERSION', 'latest'), + 'version' => env('CLOUDFRONT_COOKIES_VERSION', 'latest'), - 'region' => env('CLOUDFRONT_REGION', 'us-east-1'), + 'region' => env('CLOUDFRONT_COOKIES_REGION', 'us-east-1'), - 'key_pair_id' => env('CLOUDFRONT_KEY_PAIR_ID'), + 'key_pair_id' => env('CLOUDFRONT_COOKIES_KEY_PAIR_ID'), - 'private_key_path' => env('CLOUDFRONT_PRIVATE_KEY_PATH'), + 'private_key_path' => env('CLOUDFRONT_COOKIES_PRIVATE_KEY_PATH'), - 'domain' => env('CLOUDFRONT_DOMAIN'), + 'private_key_storage' => env('CLOUDFRONT_COOKIES_PRIVATE_KEY_STORAGE', 'local'), - 'resource' => env('CLOUDFRONT_RESOURCE', 'http*://localhost/*'), + 'resource' => env('CLOUDFRONT_COOKIES_RESOURCE', 'http*://localhost/*'), 'cookies_expiration' => [ 'unit' => \Carbon\CarbonInterval::PERIOD_DAYS, diff --git a/phpstan.neon.dist b/phpstan.neon.dist index a91953b..540131b 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -6,9 +6,6 @@ parameters: paths: - src - config - - database tmpDir: build/phpstan checkOctaneCompatibility: true checkModelProperties: true - checkMissingIterableValueType: false - diff --git a/src/Http/Middleware/CloudfrontSignedCookiesMiddleware.php b/src/Http/Middleware/CloudfrontSignedCookiesMiddleware.php index becf21b..56ade9e 100644 --- a/src/Http/Middleware/CloudfrontSignedCookiesMiddleware.php +++ b/src/Http/Middleware/CloudfrontSignedCookiesMiddleware.php @@ -16,9 +16,12 @@ public function handle(Request $request, Closure $next) { $cookies = collect(static::cookieNames()); - $shouldNotSign = $cookies->every(function ($cookie) use ($request) { - return $request->hasCookie($cookie); - }); + $shouldNotSign = ! config('cloudfront-cookies.resource') || + ! config('cloudfront-cookies.key_pair_id') || + ! config('cloudfront-cookies.private_key_path') || + $cookies->every(function ($cookie) use ($request) { + return $request->hasCookie($cookie); + }); if ($shouldNotSign) { return $next($request); diff --git a/src/LaravelCloudfrontCookies.php b/src/LaravelCloudfrontCookies.php index bfd6e31..b114008 100755 --- a/src/LaravelCloudfrontCookies.php +++ b/src/LaravelCloudfrontCookies.php @@ -7,6 +7,8 @@ use Aws\CloudFront\CloudFrontClient; use Exception; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Storage; class LaravelCloudfrontCookies { @@ -58,9 +60,18 @@ public function policy(?string $policy = null): static public function get(): array { + $private_key = Cache::remember('laravel_cloudfront_cookies_private_key', + 3600 * 24, + fn () => Storage::disk(config('cloudfront-cookies.private_key_storage'))->get(config('cloudfront-cookies.private_key_path')) + ); + + if (! $private_key) { + throw new Exception('private key not found'); + } + return $this->client->getSignedCookie([ 'policy' => $this->policy, - 'private_key' => config('cloudfront-cookies.private_key_path'), + 'private_key' => $private_key, 'key_pair_id' => config('cloudfront-cookies.key_pair_id'), ]); } diff --git a/tests/TestCase.php b/tests/TestCase.php index 6fb2428..b5c5a2e 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -6,6 +6,7 @@ use Creacoon\LaravelCloudfrontCookies\LaravelCloudfrontCookiesServiceProvider; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Support\Facades\Route; +use Illuminate\Support\Facades\Storage; use Orchestra\Testbench\TestCase as Orchestra; class TestCase extends Orchestra @@ -31,10 +32,41 @@ protected function getPackageProviders($app): array protected function getEnvironmentSetUp($app): void { - config()->set('services', [ - 'cloudfront' => [ - - ], + Storage::disk('local')->put('tests/test_private_key.pem', '-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZBUqdtCv8luW1 +iV3JXPq9IDjKlLvsmnYU7RgZlNfkJpqdM21gDgD2VFM9PQvmSuSsBqLS9E18zs4N +BrQyfXRqGocbhma4W1ZfnX47rELXYMncJTjdc0z6OE+zok7/wpBXZ+IA75t4rGq/ +wCQbKOnz5XQ2QvNzmZEs9WBg7fYWJGrIseJN7/lytMYhdE1HCwwdF2k67ul07rlf +s16IyT99c4OmgzkzhP99UQItmhoHU0fgPmhQ0n01M7O/auE1OM5UHGuMi6559tX6 +UNv8V8R2h/vsqlZS6k7fkNjTlft9Gn3c6MavpK5SV3hhZz+S2BQ22AGIUPmBeFbk +jc49Sr2fAgMBAAECggEABNKqrDD/+FFPoo3wOGbt02tiK6bOC+ilmbmlumiukhce +/tI4IebosNrhJMHGNj2Pp4kuAjTtz3+h9VcljTg9cXquKqsgLn9Y4ZQ2yyEXmjvU +VCDFAg9reORKRTPzn989NwQKY4bDZ7JHfkB+fG/cFj8NKQipk6/4NQQm3YDVvyzd +YYf4cikghy70xurRIBoy2kjkuqYRwKZsPUJ1FWZ22mHDiKJt9F7gfhZEIeK2XZ3X +sIOp5215404k/KecPcXw+fs0TGB4yP0q+/HFNXYa9PJe2CPk+DT9Z3dTnY1DrI0x +KtCVokaSgnYtq35mitLYk5eRQHbtQrWlH9qwo7+HQQKBgQDNIX4Kzt5xtPmE05cn +ei8+M/qPNYItu006szJBXdQAviFMLt2CzIr+v17F6K4ehk84ZmZaw5Ej/Hz/AvgA +N6kA+cPALPtizV045zSQCpbCsSqYQKh4c0hIdMrc/dkiOqOSIWqKKn/BomqowZrv +ugcxuxeC3mcI5OM9IKCo67pXawKBgQC+96RtV5xmllWiI0fTWAwFRj58YhQk+wv3 ++Jz7Rt/lm40rq0IlAHQlNUah+eks1sDGgidFFo2HhqPU6ORkMKZdU0jSF8jD29mR +PMIgnN6ATVSXUHOmb/KhhvChzIHNiI3PLUvQSus45yoRcxLZ6E8spC+7vCKJsQsb +dp0r246jnQKBgQCCMHGIdobjb9K1JH1YhsmZFvA9F97JG4kGaljI973nwsPrUAsy +SpMk31xNC0IHCYMZ7pOjo19okYTbbIztxmWywtIkE+hwappx1PudN7s7UaoQ+2hx +GemUYtulqk621LSfuCmgCx0OTgCXnlixMUYDoBRp8LFACdTXJPAShZm8hQKBgF7e +DTwYcuTvt/jFCBBww//2xmHqI1G/uVFlmy6lJeMdpELWYBSbphc54S3kRbb1tGyp +CbMjogl6lHbXf2ZaWLsx/ZIJKL5LwEiLY3DqHQql3+kPmXRMVr9xlqb6Pl2JgdEz +El+WaEYraFWk0e+YnYRyyBe+PXYjkn4BLdE00CZtAoGBAIQYURPYjEQP+5ptWvyK +eg5yoin/T9dokEOLLflsABVhE+NhRXgsZRurXdD29hbfJlIC3OP2Ry0L9eqs83AQ +XJJYb+eHfJotE3g5AundYSbK4io85p550SgJk+io4ht/Rg8c57KG3J1vqZr7qpwt +aHhk2PE8H+YXPht4mD0Xi85k +-----END PRIVATE KEY-----'); + config()->set('cloudfront-cookies', [ + 'version' => 'latest', + 'region' => 'us-east-1', + 'key_pair_id' => 'AA12345678', + 'private_key_path' => 'tests/test_private_key.pem', + 'private_key_storage' => 'local', + 'resource' => '/resource/*', ]); } }