-1

I am working on a simple Todo web application, and I am stuck in authorisation. I want to protect all my routes by having an active user session.

When doing an API call to a POST route, I keep getting undefined for req.session.userId within the checkAuth middleware. When using the login route, the session is being set up just fine, but when testing out the addTask route I get undefined.

To clarify, I am using Thunder Client in VSCode to do the API calls, and I set up within headers the Cookie option along with the session id.

I have been testing the following route with Thunder Client in VSCode:

router.post('/todos', checkAuth, todoController.addTask);

exports.addTask = (req, res) => {
    const {task} = req.body;
    const userId = req.session.userId;

    const stmt = db.prepare(`INSERT INTO todos (user_id, task) VALUES (?, ?)`);
    stmt.run(userId, task, (err) => {
        if(err) {
            console.error(err.message);
            res.status(500).send({error: 'Failed to add task.'});
        } else {
            res.sendStatus(200);
        }
    });
    stmt.finalize();
};

checkAuth is a simple custom middleware I have defined in another file and import it:

const checkAuth = (req, res, next) => {
    console.log(req.session, req.session.user);
    if(!(req.session && req.session.user)) {
        return res.status(401).json({error: 'Unauthorized access.'});
    }
    next();
}

Also the route and controller for the login are the following:

router.post('/login', userController.login);

exports.login = (req, res) => {
    const {login, password} = req.body;

    db.get(`SELECT * FROM users WHERE email = ? OR username = ?`, [login, login], 
    (err, user) => {
        if(err) {
            console.error(err.message);
            return res.status(500).json({error: 'Authentication failed. Wrong email or username.'});
        }

        if(!user) {
            return res.status(401).json({error: 'Invalid login or password.'}); 
        }

        const passMatch = bcrypt.compareSync(password, user.password);
        if(!passMatch) {
            res.status(401).json({error: 'Invalid password.'});
        }

        req.session.user = user;
        console.log(user);
        res.json({message: 'User authenticated successfully.'})
    });
};

And the set up of the session middleware itself:

app.use(
  session({
    name: 'sid',
    secret: process.env.SESSION_KEY, 
    resave: true, 
    saveUninitialized: false, 
    rolling: true, 
    unset: 'destroy', 
    proxy: true, 
    cookie: {
      path: '/',
      maxAge: 600000, // in ms
      httpOnly: false,  
      secure: false
    },
    store: new SQLiteStore({db: 'sessions.db', concurrentDB: false})
);
Nežumi
  • 61
  • 1
  • 7

2 Answers2

1

In your exports.login you set a property of the session object req.session.user = user;. But in your exports.addTask you are trying to get a userId value directory from the session object. Try changing it to const userId = req.session.user.userId

jQueeny
  • 714
  • 2
  • 13
  • The `addTask` route is not accessible as the program returns during the `checkAuth` middleware. I have also tried returning both `user` and `userId` but still I get unathorized in `checkAuth`. – Nežumi Jul 01 '23 at 22:00
  • what is the output of your `console.log(req.session, req.session.user);` statement in your `checkAuth`? – jQueeny Jul 01 '23 at 22:11
  • for `req.session` it's ```Session { cookie: { path: '/', _expires: 2023-07-01T22:09:36.139Z, originalMaxAge: 600000, httpOnly: false, secure: false } }``` and for `req.session.user` it's `undefined` – Nežumi Jul 01 '23 at 22:27
  • So on your line `if(!(req.session && req.session.user)) {` it will return because `req.session.user` is `undefined` which is a `falsey` value. – jQueeny Jul 01 '23 at 22:29
  • In your `exports.login` what is the output of your `console.log(user);` statement? – jQueeny Jul 01 '23 at 22:42
  • this is working, however is the session object connected to a specific user? When accessing the `addTask` route, again I get undefined. – Nežumi Jul 01 '23 at 22:45
  • The output of the `exports.login` is an object with the user credentials from the database – Nežumi Jul 01 '23 at 22:49
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/254331/discussion-between-nezumi-and-jqueeny). – Nežumi Jul 01 '23 at 22:49
  • The session object is just a way for tracking a browser session. HTTP is stateless so by using the `express-session` middleware a session id is stored in a cookie on the client and passed with each request. Your server _knows_ who it is communicating with as the session id in the cookie matches session object server-side. – jQueeny Jul 01 '23 at 22:53
0

You can try like this:

req.session.userId = user.id;

instead of:

req.session.user = user;

Then, you should make sure that you include the cookie header with the correct session id value in your API call.

Pluto
  • 4,177
  • 1
  • 3
  • 25
  • I have tried this before, in the `login` I would set both `req.session.user = user;` and `req.session.userId = user.id;` but unfortunately both give unauthorized. – Nežumi Jul 01 '23 at 21:58
  • Do you have "sid" in your cookie? – Pluto Jul 01 '23 at 22:33
  • Yes, I set it within the headers. – Nežumi Jul 01 '23 at 22:46
  • Can you show me your request on the frontend side? – Pluto Jul 01 '23 at 22:48
  • Did you set app.use(cookieParser(process.env.SESSION_KEY)); ? – Pluto Jul 01 '23 at 22:52
  • I've set the session key for cookie parser. And I'm using Thunder Client for the request, The request is: `POST http://localhost:5000/login` to create the session, which is done successfully, then for the `addTask` it's `POST http://localhost:5000/todos` with the Body Form set as `login: Test` and `password: test`, and in the headers I have added `Cookie: sid=...` – Nežumi Jul 01 '23 at 22:56
  • Are you using the router middleware before the session middleware? – Pluto Jul 01 '23 at 23:00