0

I'm trying to share session data between Express and SocketIO on a nodejs server. I researched online the came across how to implement it at https://socket.io/docs/v3/faq/#Usage-with-express-session

NOTE: FRONT-END AND BACK-END CODE AT BOTTOM

Anytime I visit the homepage at http://localhost:5000 the socket is successfully connected and session contains myData value that is set in socketio initial connection

SocketIO Console Output

Session {
  cookie: {
    path: '/',
    _expires: null,
    originalMaxAge: null,
    httpOnly: true,
    secure: true
  },
  myData: 'hello world'
}

However when a request is sent to http://localhost:5000/api/download the console output in the Express handler does not contain the myData value in the session. This indicates that even though socketIO is saving session data, it is not sharing it with Express.

Express Handler Console Output

Session {
  cookie: {
    path: '/',
    _expires: null,
    originalMaxAge: null,
    httpOnly: true,
    secure: true
  }
}

I have searched all over the internet and I can't seem to figure it out. The socketIO documentation is not helpful either. I also tried other solutions here on stackoverflow but none seems to work.

  1. How to share sessions with Socket.IO 1.x and Express 4.x?
  2. express-session isn't setting session cookie while using with socket.io
  3. socket.io and session?

This question is my last attempt to find help an solve the problem.

Thank You

MY CODE SET UP FOR BOTH FRONT-END AND BACK-END

FRONT-END HTML: (index.html)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1"
      crossorigin="anonymous"
    />
    <title>File Download Progress</title>
  </head>
  <body class="vh-100 w-2">
    <div class="d-flex flex-column align-items-center vh-100">
      <div>
        <div class="d-flex flex-column align-items-center mt-5">
          <h1 class="m-5">File Download Progress</h1>

          <button class="m-1 w-100 btn btn-success" id="dl-btn">
            Download
          </button>
        </div>
      </div>
    </div>

    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW"
      crossorigin="anonymous"
    ></script>
    <script src="./socket.io.min.js"></script>
    <script src="./app.js"></script>
  </body>
</html>

FRONT-END: (app.js)

const downloadBtn = document.getElementById("dl-btn");
const socket = io("http://localhost:5000");

socket.on("connected", () => {
  console.log("server connected");
});

downloadBtn.addEventListener("click", () => {
  window.location = "http://localhost:5000/api/download";
});

BACK-END:

import express from "express";
import session from "express-session";
import { createServer } from "http";
import { Server } from "socket.io";
import cors from "cors";
import bodyParser from "body-parser";
import asyncHandler from "express-async-handler";

const app = express();
const server = createServer(app);

const io = new Server(server, {
  cors: {
    origin: "http://localhost:3000",
    methods: ["GET", "POST"],
  },
});

const port = 5000;

const corsOptions = {
  origin: "http://localhost:3000",
  optionsSuccessStatus: 200,
};

const sessionMiddleware = session({
  secret: "keyboard cat",
  resave: false,
  saveUninitialized: true,
  cookie: { secure: true },
});

app.use(cors(corsOptions));
app.use(bodyParser.urlencoded({ extended: true }));

app.use(sessionMiddleware);
io.use((socket, next) => {
  sessionMiddleware(socket.request, socket.request.res || {}, next);
});

app.get("/api/", (req, res) => {
  res.send("API is running");
});

app.get(
  "/api/download",
  asyncHandler(async (req, res) => {
    console.log(req.session);
    res.send("hello");
  })
);

io.on("connection", (socket) => {
  socket.emit("connected");
  console.log("client connected");
  const session = socket.request.session;
  session.myData = "hello world";
  session.save();

  console.log(session);
});

server.listen(port, () => {
  console.log(`listening at http://localhost:${port}`);
});
claOnline
  • 665
  • 1
  • 5
  • 10
  • Have you seen the docs here https://socket.io/docs/v3/middlewares/ that talk about the `wrap` function – TommyBs Jan 15 '21 at 08:57
  • Modified the code with the example on "Compatibility with Express middleware" section at the link provided. The result is the same. If you spin up a node.js server and copy my code and then try it out you will realize that it does NOT! work. – claOnline Jan 15 '21 at 09:05
  • Do you see the same result with the full passport example provided here https://github.com/socketio/socket.io/blob/master/examples/passport-example/index.js – TommyBs Jan 15 '21 at 09:10
  • @TommyBs I haven't tried it. Since I'm not going to do any authentication I don't think I need passport.js? – claOnline Jan 15 '21 at 10:22
  • I mean it uses session in it as well, so you could remove the passport pieces and see if the session bit works – TommyBs Jan 15 '21 at 11:56
  • @TommyBs I tried with the passport setup and then removed the passport pieces. The result is still the same. Session data is saved in the socketio connection, but once you hit the Express route the session data is not present. The problem still persists just like I originally stated. I honestly appreciate your help but It seems to me that, you are just referring me to the socketio Docs without trying it yourself. Though I might be wrong. But once you try the code yourself, you will see that it does not work. – claOnline Jan 15 '21 at 19:19
  • 1
    I wasn’t in a position to try anything out at the time which is why I thought a (hopefully) working example might have been a good starting point. I’ll see if I can have a go with the example – TommyBs Jan 15 '21 at 19:29
  • @TommyBs Okay that would be great. Let me know if you come up with a fix – claOnline Jan 15 '21 at 19:33
  • Can you provide your html and view code for the index etc.? The only thing I've noticed so far is that you're listening on PORT 5000 as declared in your variable, but for socketio you're setting things up with port 3000 or if you have a git repo to easily clone – TommyBs Jan 15 '21 at 19:49
  • @TommyBs I have updated the code with index.html. Kindly check the bottom. I serve the front-end on a different port at https://localhost:3000 – claOnline Jan 15 '21 at 20:38

1 Answers1

1

After a lot of investigation I believe I've found the issue (and it was a lot simpler than I thought).

I noticed that a new session id was being generated on each request (websocket and http). The cause of this issue I believe is because you have secure:true set for your cookies, but I believe you are running this over http not https.

Clear all your cookies, set the secure option to false (You would want it true for a production environment most likely) and run your app again and see if you get the desired results.

TommyBs
  • 8,497
  • 3
  • 26
  • 58
  • I Set the secure option to false. Cleared cookies and run again. The result still persists. I even run in Incognito mode in Chrome. Tried even Firefox. The result is the same. "express": "^4.17.1" "socket.io": "^3.0.5" – claOnline Jan 15 '21 at 22:01
  • 1
    Interesting as that worked for me though I ran the front-end on the same server as the backend. You can try logging out the session ID ‘req.session.id’ and see if it’s changing on every request? It might be to do with CORs. Is your front end app also using “secure:true” when setting cookies? – TommyBs Jan 15 '21 at 22:06
  • Upon investigations I realized that req.session does not have the data but its rather in req.sessionStore.sessions. Very weird – claOnline Jan 15 '21 at 22:08
  • UPDATE: I can confirm that it works when you run the front-end on the same server as the backend. But when the servers are different session data is missing in req.session but rather in req.sessionStore.sessions – claOnline Jan 15 '21 at 22:48