Why sessionId get changed in every request?

I am developing a student request management system. I have used node, mysql and express. When a user logged-in he should be redirected to a dashboard.

I am using express-session to manage user sessions.

When a user successfully logged-in, the browser will send a GET request to the dashboard. But before handling the GET request there is a middleware called authChecker to check the session created on a successful login. Although the req.session.user is set inside the /login route, req.session.user is undefined inside the authChecker. Therefore user gets redirected back to login page even he logged-in correctly.

When debugging this issue in VS Code, I found that the req.session.id is changed from /login to authChecker. I think that means req.session object is changed. That’s why req.session.user is undefined inside the authChecker.

OS: Linux Mint 20 Cinnamon
Node version: 14.15.0
npm version: 6.14.9
Browser: Firefox 86.0.1 (64 bit) and Google Chrome 89.0.4389.90 (64 bit)
(Both Firefox and Chrome shows the same error)

N.B: Several friends of mine on Windows and Macs have tried this app on their machines without any problem. So could be this is because of some setting of my computer ?

Server.js

import cookieParser from "cookie-parser";
import express from "express";
import expressMySqlSession from "express-mysql-session";
import expressSession from "express-session";
import createError from "http-errors";
import logger from "morgan";
import mysql from "mysql";
import path from "path";
import socketio from "socket.io";
import {
    MYSQL_DATABASE,
    MYSQL_HOST,
    MYSQL_PASSWORD,
    MYSQL_USER,
    NODE_ENV,
    PORT,
    SESS_LIFETIME,
    SESS_NAME,
    SESS_SECRET,
} from "./config/constants";
import router from "./routes/index";

// MySQL database
const MySQLOptions = {
    host: MYSQL_HOST,
    user: MYSQL_USER,
    password: MYSQL_PASSWORD,
    database: MYSQL_DATABASE,
};
const pool = mysql.createPool({ connectionLimit: 10, ...MySQLOptions });
pool.getConnection((err, conn) => {
    if (err) throw err;

    // Check connection
    console.log("Connected to MySQL database");
    conn.release();
});

// Express app
const app = express();

// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");

// Options
app.disable("x-powered-by");
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(express.static("public"));
app.use(logger("dev"));

// Session store
const MySQLStore = expressMySqlSession(expressSession);
const sessionStore = new MySQLStore(
    { clearExpired: true, checkExpirationInterval: 600000, ...MySQLOptions },
    pool
);
const session = expressSession({
    name: SESS_NAME,
    secret: SESS_SECRET,
    saveUninitialized: false,
    resave: false,
    store: sessionStore,
    cookie: {
        sameSite: true,
        secure: NODE_ENV === "production",
        maxAge: parseInt(SESS_LIFETIME),
    },
});
app.use(session);

// Check session
app.use((req, res, next) => {
    if (!req.session.user) {
        res.clearCookie(SESS_NAME);
    }
    next();
});

// Start server
const io = socketio(
    app.listen(PORT, () =>
        console.log(`App running at http://localhost:${PORT}`)
    )
);

io.use((socket, next) => {
    let req = socket.handshake;
    let res = socket.request.res || {};
    cookieParser()(req, res, (err) => {
        if (err) return next(err);
        session(req, res, next);
    });
});

// Routing
const {
    loginRouter,
    logoutRouter,
    dashboardRouter,
    requestRouter,
    replyRouter,
} = router(pool, io);

app.use("/login", loginRouter);
app.use("/logout", logoutRouter);
app.use("/dashboard", dashboardRouter);
app.use("/request", requestRouter);
app.use("/reply", replyRouter);
app.use("*", (req, res) => {
    // Default route
    res.redirect("/login");
});

// catch 404 and forward to error handler
app.use(function (req, res, next) {
    next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
    // set locals, only providing error in development
    res.locals.message = err.message;
    res.locals.error = req.app.get("env") === "development" ? err : {};

    // render the error page
    res.status(err.status || 500);
    res.render("error");
});

login.js

import express from "express";
import { sessionChecker } from "../util/middleware";
import { sessionizeUser } from "../util/helpers";
import { getUserById } from "../util/database";

const handler = (pool) => {
    const loginRouter = express.Router();

    // Get request for login page
    loginRouter.get("", sessionChecker, (req, res) => {
        // Show login page
        res.render("login");
    });

    // Post request for login
    loginRouter.post("", sessionChecker, async (req, res) => {
        const { userId, password } = req.body;

        const user = await getUserById(pool, userId);

        if (!user) {
            res.redirect("/login");
        } else if (!user.validPassword(password)) {
            res.redirect("/login");
        } else {
            req.session.user = sessionizeUser(user);
            res.redirect("/dashboard");
        }
    });

    return loginRouter;
};

export default handler;

authChecker & sessionChecker

// Used for login route
    const sessionChecker = (req, res, next) => {
    if (req.session.user) {
        res.redirect("/dashboard");
    } else {
        next();
    }
};

// If session unavailable, redirect to login
// Used for all protected routes
    const authChecker = (req, res, next) => {
    if (req.session.user) {
        next();
    } else {
        res.redirect("/login");
    }
};

}))
});

dashboard.js

dashboardRouter.get("", authChecker, async (req, res) => {
//code
}

Github repo: GitHub - nimanthadilz/srms
(Use the develop branch for debugging)

Works on my machine:
Ubuntu 20.04.2 LTS
Node: 14.11.0
npm: 6.14.8

In the future you should create at least a basic README and sample .env file (and ideally a db migration script) so the people who are willing to help won’t have to spend too much time on figuring out the setup.

1 Like

Sorry about the inconvenience. Then There is a problem with my machine. What could go wrong ? Thank you for your valuable time.

Inside the login route handler req.session.user is available.

But inside the authChecker middleware it is not there.

Even the session.id s are different.

What could be the reason ?

I think I managed to reproduce the error - looks like only the first user that logged in is able to restore its session. That’s why it works for your friends - they have an empty sessions table.
Unfortunately I’m not familiar with the packages you’re using to handle sessions, so maybe someone else will have an idea.

1 Like

I also noticed something. Even a user has a session in the database a new session entry will be created without using the previously stored session.
I think the problem is with the sessionStore. I just used the default memoryStore for the store. Then the app worked just fine.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.