Search code examples
expressexpress-session

Express Session Not Persisting Between Requests


I'm having problems with express-session. Session data is not persisting between requests.

As you can see in the code below the /join route sets some session properties, but when the /surveys route is hit the session data is not available. req.session.loggedIn returns undefined in the /surveys route.

require('dotenv').config()

const cors = require('cors')
const express = require('express')
const open = require('amqplib').connect(process.env.RABBIT_SERVER)
const session = require('express-session')
const subscribeValidator = require('./src/validators/subscribe')
const { validationResult } = require('express-validator/check')

const app = express()

app.set('trust proxy', 1)
app.use(session({
  name: 'giro',
  secret: process.env.SESSION_SECRET,
  saveUninitialized: false,
  cookie: { secure: false },
  maxAge: process.env.SESSION_EXPIRY,
  resave: false
}))
app.use(express.json())
app.use(cors())

app.post('/join', subscribeValidator, function (req, res) {
  /*
  if(session.loggedIn) {
    return res.send(JSON.stringify({
      redirect: '/surveys'
    }))
  }
  */

  const data = {
    firstname: req.body.firstname,
    surname: req.body.surname,
    email: req.body.email,
    birthday: req.body.birthday,
    gender: req.body.gender,
    isSubscribed: req.body.isSubscribed
  }

  const errors = validationResult(req.body)
  if (!errors.isEmpty()) {
    return res.status(422).json({ errors: errors.array() })
  }

  open
    .then(conn => conn.createChannel())
    .then(ch => ch
      .assertQueue('subscribe')
      .then(ok => {
        ch.sendToQueue('subscribe', Buffer.from(JSON.stringify(data)))

        req.session.firstname = data.firstname
        req.session.email = data.email
        req.session.isSubscribed = data.isSubscribed
        req.session.confirmed = false
        req.session.loggedIn = true

        req.session.save()
        res.send(JSON.stringify({
          redirect: '/surveys',
          firstname: data.firstname,
          email: data.email
        }))
      })
    )
    .catch(console.warn)
})

app.post('/surveys', (req, res) => {
  console.log(req.session.loggedIn)
  if (!req.session.loggedIn) {
    return res.send(JSON.stringify({
      error: {
        type: 'auth',
        message: 'You must be logged in to view and complete surveys. Have you signed up?'
      }
    }))
  }

  res.send(JSON.stringify({
    surveys: [
      'one',
      'two',
      'three'
    ]
  }))
})

// Start the server :)
app.listen(3000, () => 
  console.log('Server running at: http://localhost:3000')
)

I've checked many SO posts which don't relate to my problem, read the docs over and over, but I still seem to be missing something.

Thanks


Solution

  • With the help of @jfriend00 I was able to solve the problem. Because the API was on a different port from the CDN that was making the request it falls under a cross origin request when making an xhr request from the CDN to the API. Although I had cors enabled in order for cookies to work cross origin you have to make a few tweaks.

    First of all I had to configure express-cors like so:

    app.use(cors({
      origin: 'http://localhost:1313',
      credentials: true
    }))
    

    This sets the Access-Control-Allow-Origin header value to http://localhost:1313. Wildcards can not be used otherwise it will fail the pre-flight check.

    The crednetials property sets the Access-Control-Allow-Credentials header value to true. Again, pre flight will fail without this.

    When making requests on the host I also had to use withCredentials. I'm using superagent so I did it like so:

    componentDidMount () {
        request
          .post('http://localhost:3000/surveys')
          .withCredentials()
          .set('accept', 'json')
          .end((err, res) => {
            this.setState({ isLoading: false })
            ...
          })
    }
    

    And now it works :)