19

I currently have a React App (via create-react-app) and an Express server for my API calls. They are both running separately and have been working fine until I ran into this problem:

I'm also using express-session and passport for user authentication. I'm able to authenticate users but the session doesn't persist between API calls. If I successfully login, the next time I make an API call, req.sessionID will be different, and req.isAuthenticated() will return false.

Here is my server code:

'use strict'

var express         = require('express');
var path                = require('path');
var bodyParser      = require('body-parser');
var cookieParser    = require('cookie-parser');
var mongoose      = require('mongoose');
var passport      = require('passport');
var session       = require('express-session');
var cors          = require('cors');
var flash         = require('connect-flash');

var store         = require('./store');
var database      = require('./database');

var app     = express();
var port    = process.env.port || 3001;
var router  = express.Router();

var promise = mongoose.connect('mongodb://localhost:27017/lifeless-db', {useMongoClient: true});

//app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cookieParser('lifeless-secret-01890'));
app.use(session({
  secret: 'lifeless-secret-01890',
  saveUninitialized: true,
  cookie: {
    secure: false,
  }
}));
app.use(flash());

app.use(passport.initialize());
app.use(passport.session());
// Initialize Passport
var authinit = require('./auth/init');
authinit(passport);

// For debugging:
app.use(function(req, res, next) {
  console.log("SessionID: " + req.sessionID);
  console.log(req.isAuthenticated() ? "This user is logged in" : "This user is NOT logged in");
  next();
});



// GET

app.get('/items', function(req, res) {
  store.getAllItems((items) => {
    if(!items) return res.send({error: true, message: 'Error loading store :/', items: null});
    else return res.send({error: false, message: 'Success', items: items});
  });
});

app.get('/login', function(req, res) {
  console.log(req.flash());
  console.log("Login fail");
  res.send({error: true, message: 'Unknown error'});
});


// POST

app.post('/message', function(req, res) {
  database.submitMessage(req.body.email, req.body.message, (success) => {
    if (success) return res.send({error: false, message: 'Success'});
    else return res.send({error: true, message: 'Error sending message'});
  });
});


app.post('/login', passport.authenticate('local', {failureRedirect: '/login', failureFlash: true}), function(req, res) {
  console.log(req.flash());
  console.log("Login success");
  return res.send({error: false, message: 'Success'});
});



// SERVER

app.listen(port, function(){
  console.log('Server started.');
  console.log('Listening on port ' + port);
});

And here is an example of an API call from the React App (using axios):

login(e) {
    e.preventDefault();

    axios({
      method: 'post',
      url: 'http://localhost:3001/login',
      data: {
        username: this.state.username,
        password: this.state.password,
      }
    })
    .then((response) => {
      console.log(response.data);
      if (response.data.error) {
        this.setState({error: true, errmessage: response.data.message});
      } else {
        this.setState({redirect: true});
      }
    })
    .catch((error) => {
      this.setState({error: true, errmessage: 'Error logging in'});
    });
  }

I figure there must be some way to have React store the session somehow (?) but I'm fairly new to React and don't really know how to use it with an express backend.

drewpel
  • 405
  • 3
  • 14
  • You need to configure `app.session()` with a data store to store cookies so that sessions will persist. See this answer: https://stackoverflow.com/questions/16061811/persistent-sessions-with-passport-mongodb-and-express and https://stackoverflow.com/questions/10164312/node-js-express-js-passport-js-stay-authenticated-between-server-restart (there are also many others that address this question) – Tom Oakley Mar 28 '18 at 09:25
  • I'm having this issue now. Two years later, no one has posted an answer. @drewpel did you come up with a solution. I'm guessing it has to do with headers. – M312V Aug 20 '20 at 07:53

2 Answers2

3

Your axios request from your React client needs to be sent withCredentials. To fix it, either do axios.defaults.withCredentials = true; or do axios.get('url', {withCredentials: true})...Also in your expressjs backend, set cors options credentials: true

M312V
  • 195
  • 1
  • 14
0

Here is an example of setting up express-session using connect-redis. First, setup both express-session and the Redis store.

var session = require('express-session);
var RedisStore = require('connect-redis')(session);  

Then, where you're declaring middleware for your app;

app.use(session({
  store: new RedisStore({
    url: process.env.REDIS_URL
  }),
  secret: process.env.REDISSECRET,
  resave: false,
  saveUninitialized: false
}));

now when you use req.session.{etc} this writes to your Redis DB.

adam.sellers
  • 151
  • 1
  • 15