I am working on Laravel 5.4.30.
Imagine that we have a domain example.com
and a subdomain of dev.example.com
. The main domain is for master branch and the dev subdomain is for develop branch. We have cookie notice system that will be hidden after clicking on Hide Cookie Notice
button. This works by setting a cookie forever.
We have set the SESSION_DOMAIN
configs to each domain for each environment.
For main domain:
SESSION_DOMAIN=example.com
For dev subdomain:
SESSION_DOMAIN=dev.example.com
Now the issue comes from here. If we go to the example.com
and click on hiding the cookie notice, a cookie will be set forever for main domain. After that we go to the dev.example.com
and do the same. So a cookie will be set for subdomain as well. But this cookie has been set after previous one. (The order is important)
Now if we refresh the subdomain, we will see that notice again! (not hidden) The browser has read the main cookie because of .example.com
set in domain
parameter of cookie in the browser, so every subdomain will be affected. But the view still shows the notice because it cannot read any cookie for hiding.
Anyway I don't want to share that cookie across all subdomains. How can I achieve that? I think I should add a prefix for cookie name. But I don't know how to do it, that laravel automatically adds prefix to cookie name.
Any solutions?
You need to implement your own "retrieving" and "setting" a cookie.
Create yourself new class (anywhere you like, but I would do app/Foundation/Facades/) with name Cookie.
use \Illuminate\Support\Facades\Cookie as CookieStock;
class Cookie extends CookieStock {
//implement your own has(...);
public static function has($key)
{
return ! is_null(static::$app['request']->cookie(PREFIX . $key, null)); //get the prefix from .env file for your case APP_ENV
}
//implement your own get(...);
public static function get($key = null, $default = null) {...}
}
Now open up config/app.php and change corresponding alias (cookie).
Create yourself new provider (use artisan), and copy-paste code from Illuminate\Cookie\CookieServiceProvider.php and change namespaces. Again open up config/app.php and change corresponding service provider with the new one.
Create yourself new class (anywhere you like, but I would do app/Foundation/Cookie/) with name CookieJar.
use \Illuminate\Cookie\CookieJar as CookieJarStock;
class CookieJar extends CookieJarStock {
//Override any method you think is relevant (my guess is make(), I am not sure at the moment about queue related methods)
public function make($name, $value, $minutes = 0, $path = null, $domain = null, $secure = false, $httpOnly = true)
{
// check before applying the PREFIX
if (!empty($name)) {
$name = PREFIX . $name; // get the PREFIX same way as before
}
return parent::make($name, $value, $minutes, $path, $domain, $secure, $httpOnly);
}
}
Update the code in your own cookie service provider to use your implementation of CookieJar (line 19).
Run $ composer dump-autoload
, and you should be done.
Since BorisD.Teoharov brought up, that if framework changes signature of CookieJarStock
s make()
(or any other cookie related function) in between the major versions, I made a example repository, that includes a test that can be used as is and it will fail if signature change happens.
It is as simple as this:
public function test_custom_cookie_jar_can_be_resolved()
{
resolve(\App\Foundation\Cookie\CookieJar::class);
$this->assertTrue(true);
}
Detailed how to can be inspected in the corresponding commit diff.
I've setup test environments to make sure, I'm not missing any details.
As in my former answer, I thought invalidating cookies will be sufficient for that case, but as @BorisD suggested it is not, and I've confirmed that on my tests.
So there are a few important notes, coming from my experiences...
Don't mix Laravel versions in subdomains - If using SESSION_DOMAIN you need to make sure your Laravel version matches (between root and subdomains), cause I've experimented with 5.4 under example.com domain and 5.6 under dev.example.com. This showed me some inconsistency in dealing with Cookies, so some important changes have been done, between these versions, and you can be sure it will not work correctly if you mix versions. I finally ended up with Laravel 5.6 on both domains, so I'm not 100% sure if that works on Laravel 5.4, but I think it should.
Make sure all your subdomains use the same APP_KEY - otherwise, Laravel will be unable to decrypt the Cookie, returning null
value, cause all encryption/decryption in Laravel uses this app key...
SESSION_DOMAIN. In SESSION_DOMAIN I've pointed the same root domain like example.com
for both domains. With this setting, I can create a cookie on root domain, and retrieve it correctly on both domains. After that setting, creating a cookie on subdomain forces root domain to receive new value from subdomains cookie also, and they are overridden. So I guess everything works here as requested in the original question.
Cookie make parameters - In case you want to use a subdomain in SESSION_DOMAIN, you can safely do that also. However, you need to make sure, important let's call them global cookies are defined in a bit different way. Cookie make syntax:
Cookie make(string $name, string $value, int $minutes, string $path = null, string $domain = null, bool $secure = false, bool $httpOnly = true)
So what's important here, you need to put your root domain for this particular cookie on creation like this for example:
return response($content)->cookie('name','value',10,null,'example.com')
Conclusions:
You could set a prefix for the cookie name depending on the environment.
First, add COOKIE_PREFIX
to your env file.
COOKIE_PREFIX=dev
Then, use it when setting your cookie
$cookie = cookie(env('COOKIE_PREFIX', 'prod') . '_name', 'value', $minutes);
Then, retrieve it like so
$value = $request->cookie(env('COOKIE_PREFIX', 'prod') . '_name');
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With