1

I am writing a client/server application where the frontend is using React and Axios while the backend is using Express, MySQL and PassportJS. I have a running MySQL database with a users-table, and in the app I can register users successfully with their passwords being hashed and stored in the database. I have also implemented PassportJS to successfully authorize logins. After logging in with a correct username/password combination, a cookie with the following form is saved in the browser:

userId: 
s:"qAxPZ8u77YA7_NRSk2sfLxltZI3D5klX.5okMTprFTBBq4RFwyCd2ptkAfv9dfL9Z7IViSK5bGpg"

I don't know if anything seems incorrect about the above cookie. It has some other properties of course like expires etc., but no obvious "user"-object or similar except for the string saved to userId.

Main problem is when I try to check whether a user is logged in to the app on the server side. I use axios on the client side, setup like this:

import axios from 'axios';

axios.defaults.baseURL = 'http://localhost:3000/api/v1';
axios.defaults.withCredentials = true;

And from the login-module (could be anyone but I used this to test) of my React app I call on a method I have saved which looks like this:

login() {
    return axios.get("/login").then((response) => {
       console.log(response);
     });
  }

This sends the request to the backend which is set up like this: index.ts

import express from 'express';
import router from '../src/router';
import path from 'path';
import db from '../src/mysql-pool';
require('dotenv').config()

const app = express();
const passport = require('passport');
const flash = require('express-flash');
const session = require('express-session');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');

app.use(express.static(path.join(__dirname, '/../../client/public')));

app.use(express.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser(process.env.SESSION_SECRET));

app.use(flash());
app.use(session({
  key: "userId",
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    expires: 86400000 // ms, aka 24 hours
  }
}));

app.use(passport.initialize());
app.use(passport.session());

const initializePassport = require('./passport-config');
initializePassport(
  passport, 
  (username: string) => {  
    return new Promise<{}>((resolve, reject) => {
      db.query('SELECT * FROM users WHERE username = ?', [username], (error, results) => {
        if (error) return reject(error);
        if (!(results.length > 0)) {
          return reject({ message: "User doesn't exist." });
        }

        resolve(results[0]);
      });
    });
  },
  (id: number) => { 
    return new Promise<{}>((resolve, reject) => {
      db.query('SELECT * FROM users WHERE user_id = ?', [id], (error, results) => {
        if (error) return reject(error);
        if (!(results.length > 0)) {
          return reject({ message: "User doesn't exist." });
        }

        resolve(results[0]);
      });
    });
  }
);

// the method being called from the client side
router.get('/login', (request, response) => {
  console.log(request.user);
  if (request.user) {
    response.send({ loggedIn: true, user: request.user });
  } else {
    response.send({ loggedIn: false })
  }
});

router.post('/login', passport.authenticate('local', {
  successRedirect: '/',
  failureRedirect: '/login',
  failureFlash : true
}));

app.use('/api/v1', router);

const port = 3000;
app.listen(port, () => {
  console.info(`Server running on port ${port}`);
});

The router is declared in another file called router.ts:

import express from 'express';
import { registerService, loginService } from './services'; // file with some MySQL database queries

require('dotenv').config()

const bcrypt = require('bcrypt');
const saltRounds = 10;

const router = express.Router();

// [.. some unrelated and unproblematic api-calls and routes omitted here ..]

router.post('/register', (request, response) => {
  const username = request.body.username;
  const password = request.body.password;

  if (username && username.length != 0) {
    bcrypt.hash(password, saltRounds, (error: Error, hash: string) => {
      if (error) response.status(500).send(error);
      
      registerService.register(username, hash)
      .then(() => response.status(201).send('New user registered'))
      .catch((error) => response.status(500).send(error));
    });
  } else response.status(400).send("Can't register user: Missing username og password.");
});

export default router;

And passport is configured in a file called passport-config.ts:

// @ts-nocheck

const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcrypt');

function initialize(passport, getUserByUsername, getUserById) {
  const authenticateUser = async (username, password, done) => {
    const user = await getUserByUsername(username);

    if (user == null) {
      return done(null, false, { message: 'No user exists that matches the given username.' })
    }
    
    try {
      if (await bcrypt.compare(password, user.passwd)) {
        return done(null, user)
      } else {
        return done(null, false, { message: 'Wrong username or password.' })
      }
    } catch (error) {
      return done(error)
    }
  }

  passport.use(new LocalStrategy({ usernameField : 'username', passwordField : 'password'}, authenticateUser));
  passport.serializeUser((user, done) => done(null, user.user_id));
  passport.deserializeUser((id, done) => {
    return done(null, getUserById(id));
  });
};

module.exports = initialize; 

Now: The PassportJS docs and most guides I have tried to follow say that user data after authenticating is stored as req.user. But this route on the backend:

router.get('/login', (request, response) => {
  console.log(request.user);
  if (request.user) {
    response.send({ loggedIn: true, user: request.user });
  } else {
    response.send({ loggedIn: false })
  }
});

returns undefined.

If I instead call:

router.get('/login', (request, response) => {
  console.log(request.session); // console log the session instead of req.user
  if (request.user) {
    response.send({ loggedIn: true, user: request.user });
  } else {
    response.send({ loggedIn: false })
  }
});

It logs:

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

So something is definetly happening.

It just doesn't seem like Passport ever saved the user data in the session after authenticating, or if it did, I can't find it.

Any help would be greatly appreciated, I can include more info as well if needed.

EDIT: For future reference, the solution ended up having something to do with the order of the commands in index.ts. If I remember correctly I think the app.use(passport.xxx)-commands had to be above alot of the other commands. I am afraid I can't recall exactly what order fixed the issue.

whalebeach
  • 21
  • 5
  • your login function is only console.logging the information. You aren't storing it anywhere, so of course when you try to find it in the back nothing is there – Dan Oct 25 '21 at 20:17
  • @Dan I'm sorry, I'm not quite sure I get what you mean. Are you talking about my "router.get("/login" [...])" function? I am console logging in this function to verify that I can retrieve data from the user session. This is not the function that is used when the user logs in, that would be the "router.post("login" [...]" function, which uses PassportJS for authentication. PassportJS should, per the documentation, store user information somewhere along the authentication process. I see that my router.get("/login")-function could be misleading as I use the /login-route. – whalebeach Oct 26 '21 at 10:23
  • Did you ever figure this out? – AndrewLeonardi Sep 02 '22 at 16:49
  • @AndrewLeonardi Yes, in the end it had something to do with the order of the commands in index.ts. If I remember correctly I think the app.use(passport.xxx)-commands had to be above alot of the other commands. – whalebeach Sep 03 '22 at 17:43
  • Thanks for the update @whalebeach! For me it ended up being a third party cookie issue: https://stackoverflow.com/questions/59384430/cookies-only-set-in-chrome-not-set-in-safari-mobile-chrome-or-mobile-safari – AndrewLeonardi Sep 03 '22 at 17:55

0 Answers0