Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

t0berius's avatar

laravel share cookie / session between two subdomains

I'm running a laravel app as some kind of main application on domain.com, and on a subdomain app2.domain.com I run another (not laravel based) PHP application, how can I share cookie / session data between two domains?

I would need to share some kind of the user model property of the logged in user from the laravel application to my app2.domain.com application.

Any suggestions / previous experiences how to do so?

0 likes
28 replies
rodrigo.pedra's avatar

To share cookies between subdomains, just add a dot (.) in front your domain add this env variable:

SESSION_DOMAIN=.domain.com

Note the dot (.) in front of it.

But, Laravel session sends an encrypted, cookie so you would need to replicate the encryption/decryption Laravel uses in the other project.

You could add your session cookie name into the $except property in your EncryptCookies middleware, but I don't recommend it as it would send the session id unencrypted to the browser, which is unsecure.

For replicating the session cookie encryption and decryption you can look at the base EncryptCookies middleware implementation, at your ./vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php file. (do not change files inside the ./vendor folder)

Once you manage to decrypt it, then you have a Session ID, how to retrieve session data?

You need to check within the session store code, from the store you chose in your ./config/session.php (or .env file) on how this store uses the Session ID to retrieve its content.

And of course, this store mechanism should be accessible from the non-Laravel project.

A bit of work, but doable.

t0berius's avatar

Maybe someone has implemented this already in the past / any examples on how to share session / cookie data between laravel and a another subdomain based PHP app?

Should I use Cookies for this / any suggestions?

t0berius's avatar

@rodrigo.pedra do you know if this package will support redis (since laravel stores all session data inside redis in our main application)?

t0berius's avatar

@rodrigo.pedra

Do you know what cipher laravel is using per default? Inside the .env of my laravel application I can see something like this:

APP_KEY=base64:iOJEEfZaDqxEbFzMGGNmHTicOSWSPYdDZfYcg+02tnY=

I'm trying to use this library, since it seems the easiest way for me, so using the library to extract the session ID, then request the related data from redis and here we go. Library: https://github.com/illuminate/encryption

At the moment I get:

PHP Fatal error:  Uncaught RuntimeException: Unsupported cipher or incorrect key length. Supported ciphers are: aes-128-cbc, aes-256-cbc, aes-128-gcm, aes-256-gcm. in /var/www/html/cookietest/vendor/illuminate/encryption/Encrypter.php:55\nStack trace:\n#0 /var/www/html/cookietest/index.php(4): Illuminate\Encryption\Encrypter->__construct()\n#1 {main}\n  thrown in /var/www/html/cookietest/vendor/illuminate/encryption/Encrypter.php on line 55

I already tried to base64 encode the string iOJEEfZaDqxEbFzMGGNmHTicOSWSPYdDZfYcg+02tnY=, no success too. Do you know what cipher is used by laravel / how to extract the key in the required format?

rodrigo.pedra's avatar

One more tip that just crossed my mind that might help you.

In your ./config/app.php there is a providers key where you list all Service Providers used to boot up your Laravel app.

One of them is the Illuminate\Encryption\EncryptionServiceProvider::class

So you can refer to this class to see how Laravel constructs the Encrypter class and try to mimic the same parameters on your non-Laravel app.

More precisely take a look here:

https://github.com/laravel/framework/blob/fef02606d1760d0ab6f9287b03c671fa4392998c/src/Illuminate/Encryption/EncryptionServiceProvider.php#L27-L34

t0berius's avatar

@rodrigo.pedra Thank you, should have known this my own. I'm still unable to run the Encrypter, since the key seems to be in some kind of wrong format.

APP_KEY=base64:95jv9aonvqQgLbwKVIFhALjSw/XaQmu+EB+RlaSp5/4=

I'm unable to decode it from base64 (just shows strange signs), but the laravel app itself is running fine. Any idea how I can get a valid key to run the Encrypter library?

rodrigo.pedra's avatar
<?php

$key = 'base64:95jv9aonvqQgLbwKVIFhALjSw/XaQmu+EB+RlaSp5/4=';

$decoded = base64_decode(substr($key, 7));

It will produce strange strings, but for the encrypter it does not matter, as long as it when encrypts or decrypts the same data from your Laravel app, it produces the same results.

t0berius's avatar

@rodrigo.pedra Thank you, seems like the version of laravel was updated and it's using this Encrypter.php

https://pastebin.com/F5YdcjH2

I've included the code from above with the base64_decode part and no I get bool(false)

<?php
require __DIR__ . '/vendor/autoload.php';

$key = 'base64:95jv9aonvqQgLbwKVIFhALjSw/XaQmu+EB+RlaSp5/4=';
$decoded = base64_decode(substr($key, 7));

$encrypter = new Illuminate\Encryption\Encrypter($decoded, 'AES-256-CBC');
$session_filename = $encrypter->decrypt($_COOKIE['laravel_session']);
die(var_dump($session_filename));

I already included this into .env file:

SESSION_DOMAIN=.some.testdomain

Using the chrome debugbar I'm able to verify the domain of the cookie is set to: .

.some.testdomain

too.

rodrigo.pedra's avatar

@t0berius

Well, in your non-PHP project you should include the same version of illuminate/encryption you are using on your Laravel project.

I made this sample artisan command so you can use as an example, but now that I see your last comment it is not much different from your code sample:

Artisan::command('test-encryption', function () {
    // This will use the Encrypter built by in your laravel App
    $laravelEncrypter = app(\Illuminate\Encryption\Encrypter::class);

    $key = config('app.key');
    $cipher = config('app.cipher');

    // substr here is assuming your key is prefixed with "base64:"
    // which has 7 characters. If you are not using a "base64:"
    // key you can remove it
    $decodedKey = base64_decode(substr($key, 7));

    // This will new up a fresh Encrypter as you would \
    // on your non-Laravel project
    $builtUpEncrypter = new \Illuminate\Encryption\Encrypter($decodedKey, $cipher);

    $data = 'Hello world!';

    // encryption produces different results in each run
    $laravelEncrypted = $laravelEncrypter->encrypt($data);
    $builtUpEncrypted = $builtUpEncrypter->encrypt($data);

    // but decrypting using the other Encrypter output
    // should result back in the same data
    $laravelDecrypted = $laravelEncrypter->decrypt($builtUpEncrypted);
    $builtUpDecrypted = $builtUpEncrypter->decrypt($laravelEncrypted);

    if (hash_equals($laravelDecrypted, $builtUpDecrypted)) {
        $this->info('match');
    } else {
        $this->error('fail');
    }
});
1 like
t0berius's avatar

@rodrigo.pedra Got it.

Using WITH the false parameter ( $unserialize = false):

$session_filename = $encrypter->decrypt($_COOKIE['laravel_session'], false);

returns a string like:

string(81) "753a4492b2fdff03d082beb78c95ca00c8a717ae|NdzvTT9pZxEUJFyEWniBzJCUW2lSXJfIpvxR4VoI"

Using the | delimiter I can find the session inside redis. Do you know if there's a smarter way to get this "string" I need to search for inside redis? Can you explain me the "front" part of the string (753a4492b2fdff03d082beb78c95ca00c8a717ae)?

rodrigo.pedra's avatar
Level 56

@t0berius

The front part is HMAC signature to check if the cookie has not been tampered.

I got so intrigued on how it works, I got this test script:

<?php

require __DIR__ . '/vendor/autoload.php';

use Illuminate\Cookie\CookieValuePrefix;
use Illuminate\Encryption\Encrypter;

$key = 'base64:95jv9aonvqQgLbwKVIFhALjSw/XaQmu+EB+RlaSp5/4=';
$decodedKey = base64_decode(substr($key, 7));

$encrypter = new Encrypter($decodedKey, 'AES-256-CBC');

$cookieName = 'laravel_session';
$cookieValue = $_COOKIE[$cookieName];

// check your EncryptCookies::$serialize value
// if this property is not overriden
// the default on the base Middleware is false
$payload = $encrypter->decrypt($cookieValue, false);

$hasValidPrefix = strpos(
    $payload,
    CookieValuePrefix::create($cookieName, $encrypter->getKey())
) === 0;

if (! $hasValidPrefix) {
    die('Fail');
}

$sessionFilename = CookieValuePrefix::remove($payload);

// I am using the file driver as an example here
// As you are using Redis you will need
// to use a different method
$sessionPayload = file_get_contents(
    __DIR__ . '/storage/framework/sessions/' . $sessionFilename
);
$sessionContent = unserialize($sessionPayload);

die(var_export($sessionContent, true));

I checked these files:

  • Illuminate\Cookie\Middleware\EncryptCookies
  • Illuminate\Encryption\EncryptionServiceProvider

To get to the final result. Hope this helps.

1 like
rodrigo.pedra's avatar

Forgot the output:

$ php test.php
array (
  '_token' => 'ppOcY2xTAgQt4BE4mMBioeVfL92JU8b6ufo9o285',
  'password_hash_web' => 'y$X5FFcYvf9xc71xDt0V3gKOzEC7cnxCmiskpQu8Uoz1e.6BIRmNzwa',
  '_previous' =>
  array (
    'url' => 'http://127.0.0.1/dashboard',
  ),
  '_flash' =>
  array (
    'old' =>
    array (
    ),
    'new' =>
    array (
    ),
  ),
  'login_web_59ba36addc2b2f9401580f014c7f58ea4e30989d' => 1,
)
t0berius's avatar

Thank you @rodrigo.pedra reading the data from redis is no problem, I already checked the raw data of a session from redis-cli.

Thank you soo much! Have a good evening.

1 like
rodrigo.pedra's avatar

@t0berius you're welcome =)

It was fun, and I ended up learning one more thing or two.

Have a nice day

1 like
t0berius's avatar

@rodrigo.pedra Any hint / trick how to parse the session data (never seen this format before). This is the raw string I get from redis:

//query redis for payload
$value = $client->get($redis_prefix . ':' . $sessionFilename);
//result
string(188) "s:179:"a:3:{s:6:"_token";s:40:"CHA0lTm8FXJTjP3bq4pjmnKgcjsBup8u1DUiHdqf";s:9:"_previous";a:1:{s:3:"url";s:21:"http://localdomain.testdomain";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}}";" 
rodrigo.pedra's avatar

@t0berius on my last snippet there is this line:

$sessionContent = unserialize($sessionPayload);

The format is a serialized PHP notation. The unserialize(…) call parses it and brings it back to an array.

1 like
t0berius's avatar

@rodrigo.pedra Shame on me.

Without unserialize() (plain from redis:

string(276) "s:267:"a:5:{s:6:"_token";s:40:"hoMTU9DTbJLTy4P80OUjmKpHNxEnCDNtL526u4Yr";s:9:"_previous";a:1:{s:3:"url";s:30:"http://local.domain/test";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}s:50:"login_web_59ba36addc2b2f9401580f014c7f58ea4e30989d";i:2;s:6:"some_data";i:1;}";"

Without unserialize():

string(267) "a:5:{s:6:"_token";s:40:"hoMTU9DTbJLTy4P80OUjmKpHNxEnCDNtL526u4Yr";s:9:"_previous";a:1:{s:3:"url";s:30:"http://local.domain/test";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}s:50:"login_web_59ba36addc2b2f9401580f014c7f58ea4e30989d";i:2;s:6:"some_data";i:1;}"

But it seems to be pretty messy, using array notation, like $unserializedData[0] doesn't work too. I would like to access f.e. some_data field.

rodrigo.pedra's avatar

@t0berius for some reason it seems it is double serialized... maybe Laravel does it on the Redis driver...

Try this:

$content = unserialize(unserialize($rawFromRedis));

and see if it works.

The format, although not easy to parse manually, is actually not that hard to grasp.

  • In your first snippet it starts with s:267:"..."; which translates to: a string with 267 characters
  • Then In your second snippet it starts with a:5:{...} which translates to: an array with 5 elements
  • then it has a pair of two strings for each array element, first key is s:6:"_token"; which translates to _token and first value is s:40:"hoMTU9DTbJLTy4P80OUjmKpHNxEnCDNtL526u4Yr"; which translates to hoMTU9DTbJLTy4P80OUjmKpHNxEnCDNtL526u4Yr

and so on.

rodrigo.pedra's avatar

Fixing some counts you might have changed to obfuscate your real data:

<?php

$rawData = 's:264:"a:5:{s:6:"_token";s:40:"hoMTU9DTbJLTy4P80OUjmKpHNxEnCDNtL526u4Yr";s:9:"_previous";a:1:{s:3:"url";s:24:"http://local.domain/test";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}s:50:"login_web_59ba36addc2b2f9401580f014c7f58ea4e30989d";i:2;s:9:"some_data";i:1;}";';

$unserialized = unserialize(unserialize($rawData));

var_dump($unserialized);
$ php test.php 
array(5) {
  ["_token"]=>
  string(40) "hoMTU9DTbJLTy4P80OUjmKpHNxEnCDNtL526u4Yr"
  ["_previous"]=>
  array(1) {
    ["url"]=>
    string(24) "http://local.domain/test"
  }
  ["_flash"]=>
  array(2) {
    ["old"]=>
    array(0) {
    }
    ["new"]=>
    array(0) {
    }
  }
  ["login_web_59ba36addc2b2f9401580f014c7f58ea4e30989d"]=>
  int(2)
  ["some_data"]=>
  int(1)
}
rodrigo.pedra's avatar

I am wondering if you are using the predis/predis package in your Laravel project, if it is who serializes the second time.

You should try to use the same Redis driver on your non-Laravel project

1 like
t0berius's avatar

@rodrigo.pedra Yes, using unserialize() two times works fine for me, yes I've used predis / Predis\Client(), but on the doc I wasn't able to find any docs with a command to prevent the driver from doing so (serialize).

1 like

Please or to participate in this conversation.