0

Hello Stackoverflow Community.

So I am encountering a very weird problem when hosting my nextjs powered by express with openlitespeed. Everything works great in production, except one thing - the authentification of sessions. The user is saved in the cookies correctly and it works if you are not idle for more than a minute on the page you are on, but if you are idle for more than a minute, then the request is not authenticated anymore even though the cookie is still there.

I am using redis for my cookie store, and everything works in local testing, where openlitespeed is not present. The authentification I am using is passportjs with express-session. Have any of you encountered this problem, and if so, how did you solve it? I have tried disabling the cache module, set all timeouts to a higher value or disabling them, use different memorystores and more, but no luck. Here is the server.js file, however, I do not believe it has something to do with the code itself, but rather the config of openlitespeed:

const express = require('express')
const next = require('next')
const passport = require('passport');
const redis = require('redis')
const session = require('express-session')
const {v4: uuidv4} = require('uuid');
const path = require('path');
const log = require('./logger')
let RedisStore = require('connect-redis')(session)
let redisClient = redis.createClient()

const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const server = express()

  //Json parsing
  server.use(express.json());
  server.use(express.urlencoded({extended: true}));


  if (dev){
    //Express session
    server.use(session({
      store: new RedisStore({ client: redisClient }),
      genid: function() {
        return uuidv4()},
      secret: uuidv4(),
      resave: false,
      saveUninitialized: false,
      cookie: {
        secure: false,
        maxAge: 86400000 
      }
  }))
  }
  else{
      //Express session
    server.use(session({
      store: new RedisStore({ client: redisClient }),
      genid: function() {
        return uuidv4()},
      secret: uuidv4(),
      proxy: true,
      resave: false,
      saveUninitialized: false,
      cookie: {
        secure: true,
        maxAge: 86400000
      }
  }))
  }


  //Passport auth
  server.use(passport.initialize());
  server.use(passport.session());

  //Import of the passport config 
const initializePassport = require('./passport-config');
initializePassport(passport);

//Login route
server.post('/login', passport.authenticate('login'), (req, res) => {
    res.send({message: 'Successful login', login: true})
});

const passportLogout = function (req, res, next) {
  req.logout()
  next()
}

//Logout route
server.get('/logout', passportLogout, (req, res) => {
    req.session.destroy();
    res.redirect('/login');
});

//Import registrerings route. Pga. brugen af route i stedet for app kan vi bruge denne middleware med en anden underside, hvis vi f.eks. ville gøre så admins også kunne lave brugere.
const registerRoute = require('./routes/register-user');
server.use('/register', registerRoute);

  //User routes hvor login er required. Rendering. Skal stå under called til initializepassport, ellers kan den ikke finde ud af at den er authenticated via passport, og auth.js returnerer dig derfor til login
  const usersRoutes =  require('./routes/user/user-routes');
  server.use(usersRoutes);

  //Admin routes til rendering
  const adminRoutes = require('./routes/admin/admin-routes');
  server.use(adminRoutes);

  const indexRoutes = require('./routes/index-routes');
  server.use(indexRoutes);


  server.all('*', (req, res) => {
    return handle(req, res)
  })

  server.listen(port, (err) => {
    if (err) throw err
    log.logger.log({
      level: "info",
      message: `Server was started on ${port}`,
      additional: "properties",
      are: "passed along",
    });
    console.log(`> Ready on http://localhost:${port}`)
  })
})
muggel
  • 11
  • 4
  • Just curious how you set up the OpenLiteSpeed, as a reverse proxy? – Eric Jan 19 '21 at 06:02
  • Hi Eric. Sorry for the late reply. Honestly, I just used a community setup from linode and customised it for my needs – muggel Feb 15 '21 at 12:50

1 Answers1

1

All right, so I figured it out finally. The configuration for Openlitespeed was set, so that it could create as many httpd workers as it wants. Therefore, when a new was created and the requests went over to that one, it seems the authentification did not stick. I have fixed this by setting the "Number of Workers" to 1 under Server Configuration -> Server Process -> Number of Workers.

As for my server.js file I used to setup nextjs and openlitespeed:

const express = require("express");
const next = require("next");
const passport = require("passport");
const redis = require("redis");
const session = require("express-session");
const { v4: uuidv4 } = require("uuid");
const path = require("path");
const log = require("./logger");
let RedisStore = require("connect-redis")(session);
let redisClient = redis.createClient({ auth_pass: process.env.DB_PASSWORD });

const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();

app.prepare().then(() => {
  const server = express();

  //Json parsing
  server.use(express.json());
  server.use(express.urlencoded({ extended: true }));

  if (dev) {
    //Express session
    server.use(
      session({
        store: new RedisStore({ client: redisClient }),
        genid: function () {
          return uuidv4();
        },
        secret: uuidv4(),
        resave: false,
        saveUninitialized: false,
        cookie: {
          secure: false,
          maxAge: 86400000,
        },
      })
    );
  } else {
    //Express session
    server.use(
      session({
        store: new RedisStore({ client: redisClient }),
        genid: function () {
          return uuidv4();
        },
        secret: uuidv4(),
        proxy: true,
        resave: false,
        saveUninitialized: false,
        cookie: {
          secure: true,
          maxAge: 86400000,
        },
      })
    );
  }

  //Passport auth
  server.use(passport.initialize());
  server.use(passport.session());

  //Import of the passport config
  const initializePassport = require("./passport-config");
  initializePassport(passport);

  //Login route
  server.post("/login", passport.authenticate("login"), (req, res) => {
    res.send({ message: "Successful login", login: true });
  });

  const passportLogout = function (req, res, next) {
    req.logout();
    next();
  };

  //Logout route
  server.get("/logout", passportLogout, (req, res) => {
    req.session.destroy();
    res.redirect("/login");
  });

  //Import registrerings route. Pga. brugen af route i stedet for app kan vi bruge denne middleware med en anden underside, hvis vi f.eks. ville gøre så admins også kunne lave brugere.
  const registerRoute = require("./routes/register-user");
  server.use("/register", registerRoute);

  //User routes hvor login er required. Rendering. Skal stå under called til initializepassport, ellers kan den ikke finde ud af at den er authenticated via passport, og auth.js returnerer dig derfor til login
  const usersRoutes = require("./routes/user/user-routes");
  server.use(usersRoutes);

  //Admin routes til rendering
  const adminRoutes = require("./routes/admin/admin-routes");
  server.use(adminRoutes);

  const indexRoutes = require("./routes/index-routes");
  server.use(indexRoutes);

  server.all("*", (req, res) => {
    return handle(req, res);
  });

  server.listen(port, (err) => {
    if (err) throw err;
    console.log(`> Ready on ${port}`);
  });
});
muggel
  • 11
  • 4
  • HI @muggel, thanks for sharing. I saw some other people also want to set up the NextJS on OLS but don't know how to. Is it possible for you to share some basic knowledge or steps about how to do it, and I will help to write it on the LiteSpeedtech document. – Eric May 25 '21 at 05:48
  • Hello @Eric. Sorry for the late reply, but I would be happy to help. As mentioned, I used a linode stackscript to setup the basic nodejs + openlitespeed setup. Is there anything in particular that you would like me to explain or? – muggel Jul 01 '21 at 21:59
  • I see, you launch the server from https://docs.litespeedtech.com/cloud/images/nodejs/ . May I know if you are willing to share an example for the NextJS app code and how you put it on the server to make it works in detail? then I can help to share the method on the document. – Eric Jul 02 '21 at 01:23
  • 1
    Hi @Eric. I started off with this stackscript: https://cloud.linode.com/stackscripts/458633. That installs nodejs + openlitespeed. As for the server, I am using express together with nextjs. EDIT: I cannot seem to be able to insert the server.js file I use here, but I have edited it into the answer I gave – muggel Jul 03 '21 at 20:13