1

I am having difficulty getting my react app to work with cookie-session. When I look at my network and signup I do send a Set-Cookie. These are my response headers:

 Connection:keep-alive
Content-Type:application/json
Date:Fri, 09 Feb 2018 22:32:02 GMT
Set-Cookie:session=eyJ1c2VySWQiOjEwOX0=; path=/; expires=Mon, 19 Feb 2018 22:32:02 GMT; httponly
Set-Cookie:session.sig=o975QwcyzldDFzSPzbp2S4Qe_M4; path=/; expires=Mon, 19 Feb 2018 22:32:02 GMT; httponly
Transfer-Encoding:chunked
X-Powered-By:Express

request headers:

Accept:application/json
Accept-Encoding:gzip, deflate, br
Accept-Language:en-US,en;q=0.9
Connection:keep-alive
Content-Length:59
Content-Type:application/json
Host:localhost:5000
Origin:http://localhost:5000
Referer:http://localhost:5000/signup
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36

However, I noticed that my cookies were not stored in the browser. I believe the cookies are supposed to be stored automatically by the browser? Heres my code:

const express = require('express')
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')
const cookieSession = require('cookie-session')
const bcrypt = require('bcrypt')
const port = process.env.PORT || 5000
const app = express()
const pg = require('pg')
const conString = "postgres://postgres:postgres@localhost:5432/test"
const db = new pg.Client(conString)
let User


app.use(bodyParser.urlencoded({ extended: true }))
app.use(cookieParser())
app.use(cookieSession({
    name: 'session',
    secret: "booboo",
    httpOnly: true,
    // Cookie Options
    maxAge: 24 * 60 * 60 * 1000 * 10 // 24 hours * 10 days
  }))

app.use(express.json())
app.use(express.static(__dirname + '/client/build'))

app.use(function(req,res ,next){
console.log(req.session)
if(req.method !== 'POST')
    res.sendFile(__dirname + '/client/build/index.html')

if(req.headers.accept === 'application/json')
    next() 
})


app.route('/signup')
    .post((req, res) => {
        const sendError = (msg) => {
            res.status(400).send({error: msg})
        }

        if(!User.validateBeforeCreate({email: req.body.user.email, password: req.body.user.password}, sendError))
            return

        const salt = bcrypt.genSaltSync();
        const password = bcrypt.hashSync(req.body.user.password, salt);
        User.create({email: req.body.user.email, password: password}, (err, user) => {
            if(err !== null){
                if(err.code === '23505')
                    sendError('Account with that email already exists.')
            }
            else{
                req.session.userId = user.user_id
                res.writeHead(201, {"Content-Type": "application/json"})
                res.end()
                console.log(req.session)
                console.log(req.session.userId)
            }
        })
    })

My console.log in the signup prints this: Session {userId: 103} 103

Meanwhile my console.log after I sign up and visit a page to receive my react app i get this print out(probably because the browser doesn't have the cookies): Session {} , I have tried setting the httpOnly to true and false. Same results.

What am I doing wrong?

frogtoprincejs
  • 141
  • 2
  • 12

2 Answers2

0

It looks like you're not setting a domain in your cookie object, therefore the browser is discarding the cookie. I'd also recommend opening the developer tools in your browser and checking the headers sent by the server to see what the transmitted cookie looks like.

Try:

app.use(cookieSession({
  name: 'session',
  secret: "booboo",
  httpOnly: true,
  domain: "example.com",
  // Cookie Options
  maxAge: 24 * 60 * 60 * 1000 * 10 // 24 hours * 10 days
}))
Taylor Swanson
  • 613
  • 7
  • 11
  • i put in path: "/", and I tried 4 different values for the domain property, "localhost", "localhost:5000", "127.0.0.1", and "127.0.0.1:5000" still doesn't work. – frogtoprincejs Feb 09 '18 at 22:51
  • This can be implementation-dependent, unfortunately. Historically domains in cookies have needed a leading dot e.g., ".example.com" but nowadays it looks like the domain needs to have at least one dot. This doesn't work for localhost, however. Try setting it to an empty string or using a different browser. Each browser handles it differently. What headers are being sent by your server? – Taylor Swanson Feb 09 '18 at 23:00
  • Connection:keep-alive Content-Type:application/json Date:Fri, 09 Feb 2018 23:03:30 GMT Set-Cookie:session=eyJ1c2VySWQiOjEyMX0=; path=/; expires=Mon, 19 Feb 2018 23:03:30 GMT; domain=127.0.0.1; httponly Set-Cookie:session.sig=7ZAaj8_BTYKHRBWuX3XJt0hm2fo; path=/; expires=Mon, 19 Feb 2018 23:03:30 GMT; domain=127.0.0.1; httponly Transfer-Encoding:chunked X-Powered-By:Express – frogtoprincejs Feb 09 '18 at 23:05
  • empty string didn't work, i will download firefox and try it out on that. – frogtoprincejs Feb 09 '18 at 23:07
  • i tried it on all browsers. no go. However, I had a rails app working on localhost on authentication and authorization. I checked the cookie that rails was setting and it is with the domain property set to 'localhost' and is working. It's server response is different from mine: – frogtoprincejs Feb 09 '18 at 23:27
  • Cache-Control:max-age=0, private, must-revalidate Connection:close Content-Type:text/html; charset=utf-8 ETag:W/"6250ad4276cccc6a4f49e61235bc4fed" Server:thin Set-Cookie:_favmovie_session=NEZQS1Fmc1hKaEJhaDBpMm1wbm9aNkpMb2tFTjZIZjZNZ1JxTUtBakZ4ekdhZG5EbkU2S0FsUVF5S1BzVFpPZEx4S0xyWDdna2JJVFFCSWVPblFGcGk4TDdYTWpJZXMwZzFLMzJmN3UyaUlkR1dBT0FWdjRXazdGK0NMczVkZFVwYkRnM2Y2MDJTNDJHRnJBVkhsdVdBPT0tLUdLZVJraml6YU1BZkZ2ajEvT3duMVE9PQ%3D%3D--90a595065f2a589c5fe5d67767d811a5b101f310; path=/; HttpOnly – frogtoprincejs Feb 09 '18 at 23:28
  • X-Content-Type-Options:nosniff X-Frame-Options:SAMEORIGIN X-Request-Id:b80c0f37-5892-46f8-9c38-9230aa59018e X-Runtime:0.364805 X-XSS-Protection:1; mode=block – frogtoprincejs Feb 09 '18 at 23:28
  • Interesting. Perhaps this is a front-end issue then? I see you tagged your question with `react`, is there any chance that the application itself is removing the cookie (either through some sort of validation or a third-party library or otherwise)? I notice that the rails app is sending an X-Frame-Options header, perhaps look to see if there's any CORS check in your react app. – Taylor Swanson Feb 09 '18 at 23:32
  • might be, probably the next step that i will look into. – frogtoprincejs Feb 09 '18 at 23:33
  • i built a simple app without react using pretty much the same code, and the browser did save the cookies. so must be something with react. – frogtoprincejs Feb 10 '18 at 00:08
  • it actually might not be. On a get request it will save the cookie. But on a post request it does not on my simple app. – frogtoprincejs Feb 10 '18 at 00:43
  • figured it out. it was something minor. thanks for the help! – frogtoprincejs Feb 10 '18 at 03:39
  • I hope I was useful; for future archaeologists and the curious, what was the issue? – Taylor Swanson Feb 10 '18 at 09:52
  • I had a few. I misplaced a next() that was giving inconsistent results. And on my client side when I was fetching I needed to set the crendentials property of the request to 'same-origin'. credentials: 'same-origin', – frogtoprincejs Feb 10 '18 at 16:35
0

After hours of banging my head against the keyboard I figured it out. For some reason on post it would not set the cookies. The get was working but it took me a while to get there. I spent a while figuring out if its react. But nope, if I set it on the get action it would work. So then I figured it was a security issue. And I found out I needed to add a credentials property with 'same-origin' on my client's fetch. And I placed a next() in the wrong spot. Now everything works. So all in all the changes I made, 4 lines. Thanks to Taylor Swanson and this post: Why is the browser not setting cookies after an AJAX request returns?

name: 'session',
secret: "booboo",
httpOnly: true,
path: "/",
domain: "localhost"

handle submit is a client callback:

handleSubmit = (event) => {
        event.preventDefault()
        fetch('/signup',{
            headers:{
              'Accept': 'application/json',
              'Content-Type': 'application/json'
            },
            credentials: 'same-origin',
            method: 'post',
            body: JSON.stringify({user: {...this.state}})
          })
    }

And my new server code looks like this:

app.use(bodyParser.urlencoded({ extended: true }))
app.use(cookieParser())
app.use(cookieSession({
    name: 'session',
    secret: "booboo",
    httpOnly: true,
    path: "/",
    domain: "localhost"
    // Cookie Options
  }))

app.use(express.json())
app.use(function(req, res, next){
    if(req.method === 'GET' && req.cookies.session === undefined)
        req.session.userId = null
    else
        console.log(req.session.userId)

    next()
})
app.use(express.static(__dirname + '/client/build/'))
app.use(function(req,res ,next){
    if(req.method === 'GET')
        res.sendFile(__dirname + '/client/build/index.html')

    if(req.headers.accept === 'application/json')
        next()
})

app.route('/signup')
    .post((req, res, next) => {
        const sendError = (msg) => {
            res.status(400).send({error: msg})
        }

        if(!User.validateBeforeCreate({email: req.body.user.email, password: req.body.user.password}, sendError))
            return

        const salt = bcrypt.genSaltSync();
        const password = bcrypt.hashSync(req.body.user.password, salt);
        User.create({email: req.body.user.email, password: password}, (err, user) => {
            if(err !== null){
                if(err.code === '23505')
                    sendError('Account with that email already exists.')
            }
            else{
                req.session.userId = user.user_id
                res.writeHead(201, {"Content-Type": "application/json"})
                res.end()
                console.log(req.session)
                console.log(req.session.userId)
            }
            next()
        })
    })
frogtoprincejs
  • 141
  • 2
  • 12