Code Snippet: Managing Laravel Session over a NodeJS App

As any application grows, it transtions over time and teams. It not only needs to keep up with its users but also with the people developing it. I got a chance to work on an application which is going over such a transition. The application was acquired by a new company and its developers were more proficient in NodeJS than in PHP. They decided it was better to build any new features in something they knew and not to risk breaking an app more than than building it.

Everything new would be coded in NodeJS but the NodeJS app needed to be aware of the session. The PHP app is written in Laravel and it encrypts the session identifier in the Cookie value. To use the same session across both the applications we need the following:

  1. Both PHP and NodeJS app should have access to the same session store. In this case it was stored in Redis.
  2. NodeJS app should be able to decrypt the cookie value so as to extract the session information from the session store. It should also be able to encrypt data just in case it needs to set some data in the session.

This post shares a code snippet that helps to encrypt/decrypt a Laravel session from a NodeJS app. The Laravel application is at version 5.4 of Laravel.

Here is a code snippet of the decryption/encryption on NodeJS

crypto = require('crypto');

unserialize = require('locutus/php/var/unserialize');
serialize = require('locutus/php/var/serialize');
redis = require('redis');

// this the appkey from your laravel application, you would usually have it in your .env/config file
// it is stored here for simple testing
appKey = 'base64:the_appkey';

if (appKey != null ? appKey.startsWith("base64:") : void 0) {
  appKey = appKey.substring(7);
  appKey = Buffer.from(appKey, 'base64');
}

decodeLaravel = function(payload) {
  var alg, decoded, decodedStr, decoder, iv;
  alg = `aes-${appKey.length * 8}-cbc`;
  payload = JSON.parse(Buffer.from(payload, 'base64'));
  iv = Buffer.from(payload.iv, 'base64');
  decoder = crypto.createDecipheriv(alg, appKey, iv);
  decoded = decoder.update(payload.value, 'base64');
  decoded += decoder.final();
  decodedStr = decoded.toString();
  return unserialize(decodedStr);
};

encodeLaravel = function(payload) {
  var Hmac, alg, cipher, encoded_data, encrypted, encrypted_value, iv, json;

  alg = `aes-${appKey.length * 8}-cbc`;
  iv = crypto.randomBytes(16);
  payload = serialize(payload);
  cipher = crypto.createCipheriv(alg, appKey, iv);
  encrypted = cipher.update(payload, 'utf8', 'base64');
  encrypted += cipher.final('base64');
  Hmac = crypto.createHmac('sha256', appKey);
  encoded_data = Hmac.update(iv.toString('base64') + encrypted);
  encoded_data = Hmac.digest('hex');
  json = JSON.stringify({
    iv: iv.toString('base64'),
    value: encrypted,
    mac: encoded_data
  });
  encrypted_value = Buffer.from(json);
  return encrypted_value.toString('base64');
};

str = 'this is a string to be encrypted';

encoded = encodeLaravel(str);

console.log(encoded); // outputs:eyJpdiI6InBDajlQcmJ1ay9HVzFQelRIUVdmaGc9PSIsInZhbHVlIjoiRktuZ255WEJvaDRUaWl2R1BNQ0pFMHhudThKdzE2dElIOXFPQ1B2d0NBdWpuTHpsQXZjaFlTQ3kyVzRwUlVZUSIsIm1hYyI6ImZkMDE2YWZlYzIxZjRjMWZlZWQxYmM0YjJiNDhjNGU2ZDE4NDBjNjhlNDM4NWQ2M2I0OWE1YTMyOTc2YzE0YWEifQ==


console.log(decodeLaravel(encoded)); // outputs: this is a string to be encrypted

In your NodeJS app you will do something like

decodeLaravel(req.cookies['laravel_session']);

To get the session ID from the cookie and then make a call to your Redis server to fetch the session data.

To tinker further, you can find the PHP code here and if a newer version of the Laravel framework is used make sure to check out the latest version and adjust the code on NodeJS side accordingly.

Leave a Reply

Your email address will not be published. Required fields are marked *