Skip to content

Instantly share code, notes, and snippets.

@mscdex
Last active September 15, 2023 11:55
Show Gist options
  • Star 36 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save mscdex/9507b0d8df42e0aec825 to your computer and use it in GitHub Desktop.
Save mscdex/9507b0d8df42e0aec825 to your computer and use it in GitHub Desktop.
sharing sessions between node.js and php using redis
var express = require('express'),
app = express(),
cookieParser = require('cookie-parser'),
session = require('express-session'),
RedisStore = require('connect-redis')(session);
app.use(express.static(__dirname + '/public'));
app.use(function(req, res, next) {
if (~req.url.indexOf('favicon'))
return res.send(404);
next();
});
app.use(cookieParser());
app.use(session({
store: new RedisStore({
// this is the default prefix used by redis-session-php
prefix: 'session:php:'
}),
// use the default PHP session cookie name
name: 'PHPSESSID',
secret: 'node.js rules',
resave: false,
saveUninitialized: false
}));
app.use(function(req, res, next) {
req.session.nodejs = 'Hello from node.js!';
res.send('<pre>' + JSON.stringify(req.session, null, ' ') + '</pre>');
});
app.listen(8080);
<?php
// this must match the express-session `secret` in your Express app
define('EXPRESS_SECRET', 'node.js rules');
// this id mutator function helps ensure we look up
// the session using the right id
define('REDIS_SESSION_ID_MUTATOR', 'express_mutator');
function express_mutator($id) {
if (substr($id, 0, 2) === "s:")
$id = substr($id, 2);
$dot_pos = strpos($id, ".");
if ($dot_pos !== false) {
$hmac_in = substr($id, $dot_pos + 1);
$id = substr($id, 0, $dot_pos);
}
return $id;
}
// check for existing express-session cookie ...
$sess_name = session_name();
if (isset($_COOKIE[$sess_name])) {
// here we have to manipulate the cookie data in order for
// the lookup in redis to work correctly
// since express-session forces signed cookies now, we have
// to deal with that here ...
if (substr($_COOKIE[$sess_name], 0, 2) === "s:")
$_COOKIE[$sess_name] = substr($_COOKIE[$sess_name], 2);
$dot_pos = strpos($_COOKIE[$sess_name], ".");
if ($dot_pos !== false) {
$hmac_in = substr($_COOKIE[$sess_name], $dot_pos + 1);
$_COOKIE[$sess_name] = substr($_COOKIE[$sess_name], 0, $dot_pos);
// https://github.com/tj/node-cookie-signature/blob/0aa4ec2fffa29753efe7661ef9fe7f8e5f0f4843/index.js#L20-L23
$hmac_calc = str_replace("=", "", base64_encode(hash_hmac('sha256', $_COOKIE[$sess_name], EXPRESS_SECRET, true)));
if ($hmac_calc !== $hmac_in) {
// the cookie data has been tampered with, you can decide
// how you want to handle this. for this example we will
// just ignore the cookie and generate a new session ...
unset($_COOKIE[$sess_name]);
}
}
} else {
// let PHP generate us a new id
session_regenerate_id();
$sess_id = session_id();
$hmac = str_replace("=", "", base64_encode(hash_hmac('sha256', $sess_id, EXPRESS_SECRET, true)));
// format it according to the express-session signed cookie format
session_id("s:$sess_id.$hmac");
}
require('redis-session-php/redis-session.php');
RedisSession::start();
$_SESSION["php"] = "Hello from PHP";
if (!isset($_SESSION["cookie"]))
$_SESSION["cookie"] = array();
echo "<pre>";
echo json_encode($_COOKIE, JSON_PRETTY_PRINT);
echo json_encode($_SESSION, JSON_PRETTY_PRINT);
echo "</pre>";
?>
@pharsi
Copy link

pharsi commented Jul 4, 2015

Hi Brian, thanks for sharing this with us 😄 I want to see this code in action, I'll be grateful if you could help me run this code and see for myself :)

@bensontung
Copy link

谢谢,现在我知道怎么弄了

@josuedor
Copy link

Beautiful! Thank you.

@ZeSoft
Copy link

ZeSoft commented Aug 23, 2015

hello brian , i have a problem when i try to set session id to be compatible with express session format i got this error in php :Warning: session_start() [function.session-start]: The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' it's understandable because the session id we set contains : and . and other caracters that are not alphanumeric, second question is how can i get the phpsessid from redis in node ,
i will really appreciate it if u help me ,thanks in advance

@ramin-git
Copy link

I have different problem. When first execute "php file" then execute "node js server page" - "node js server page" have not seen session creation from "php file". When vice versa (first execute "node js server page" then execute "php file") session variables have seen in both page

@tuxbotix
Copy link

Just as @ramin-git mentioned, I also experienced the issue of express-session not accepting cookie by PHP but other way works.

The issue happens when a new session start, the calls for session_regenerate_id(); (line 45) to (re)generate a new ID for the new session (and cookie). But the next call for session_id() returns empty since both these functions should be called after session_start().

Fix:

if you are 100% sure that regenerate_id() will not work before session_start() (I'm not completely sure :P), then just replace line 45 and 46 with the following, that way executing regenerate and asking for a session id is avoided. This method should work definitely but I have to do further testing.

$sessHandlerInst=new SessionHandler();
$sess_id=$sessHandlerInst->create_sid();

or append the following instead, between line 46 and line 47 (after $sess_id = session_id();, before $hmac = str_replace.... - this will generate a new ID if $sess_id turns out empty (which will be*).

if(!$sess_id){
            $sessHandlerInst=new SessionHandler();
            $sess_id=$sessHandlerInst->create_sid();
}

Explanation :

Since the expectation for calling

 session_regenerate_id();
 $sess_id = session_id();

is to generate a new ID which the script modify to match express-session format, calling SessionHandler::create_sid() can be used. Yes the session_regenerate() internally calls create_sid() but will only work if the session has been started (which is not* otherwise setting session ID is not possible).

@rustigano
Copy link

what is the best approach for using this with multiple domains?
Example of the domains that need to be aware of the session:

  • domain.com
  • sub1.domain.com
  • bla.domain.com

I have tried adding the domain to the cookie (prefixed with and without a dot) but no success yet.

Same problem is in this thread: https://github.com/expressjs/session/issues/633

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment