Last active
September 15, 2023 11:55
-
-
Save mscdex/9507b0d8df42e0aec825 to your computer and use it in GitHub Desktop.
sharing sessions between node.js and php using redis
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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>"; | |
?> |
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
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 forsession_id()
returns empty since both these functions should be called aftersession_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.
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*).Explanation :
Since the expectation for calling
is to generate a new ID which the script modify to match express-session format, calling
SessionHandler::create_sid()
can be used. Yesthe 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).