question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Persisting session cookies is not working

See original GitHub issue

I am having an issue persisting the correct cookies when trying to test an endpoint that requires authentication.

When I test an endpoint that requires a user to be logged in, I would expect to receive a http status of 201 success, but am receiving a 403 forbidden.

Here is an overview of what my test spec file is doing:

  • I create a mocha describe function with a before() and after() and inside those callbacks, I start my server and then add a dummy user and some dummy content in my test mongoose database.
  • My first it() function uses agent to make a connection to the endpoint without signing in and that test expect(403) is correct.
  • My second it() function then uses agent to attempt to sign in with the credentials for the dummy user I created earlier. I expect(200) to pass, which it does. I also console.log(res.headers[set-cookie]) and I see the output is as follows:
connect.sid=s%3Aze-bIOV3kWJc6TdjWMhxqnmcregTXBKZ.HIACLrWQwReMqj8YVLSXkjAPhBEwT0EuUJfkgeC%2BfpU; Domain=localhost; Path=/'
  • The problem arises with my third it() function, where I want to make a call to an endpoint that requires a user to be logged in. What I would expect to see here is expect(201), but instead get expect(403) suggesting to me that the user is not logged in, and the cookie is invalid. Indeed, when I log the value of the cookie as above I see the following, which is completely different:
connect.sid=s%3AVStxZsDf_RxxDj50c4kmvzebKeMGz51N.Y0qojAAqf8Uw92vGv5u941FArM57P%2FXEG10dsSsx2bc; Domain=localhost; Path=/'

This is the contents of my spec file for testing these endpoints:

'use strict';

let app         = require('../../../server/app.js'),
    testutils   = require('../testutils.js'),
    request     = require('supertest');

describe('POST /answer/:id/comment', function() {

    let answer_id,
        server,
        agent;

    before(function(done) {
        app().then(function(result) {
            server = result.server;
            agent = request.agent(server);
            testutils.addUser()
            .then(testutils.addQuestion)
            .then(testutils.addAnswer)
            .then(function(answer) {
                answer_id = answer._id;
                done();
            });
        });
    });

    after(function(done) {
        testutils.mongoClear();
        done();
    });

    it('should return a json encoded error message with a http status of 403 for a logged out user.', function(done) {
        agent
        .post('/answer/' + answer_id + '/comment')
        .expect('Content-Type', /json/)
        .expect(403)
        .expect({info: 'You need to be signed in to comment.'}, done);
    });

    it('should then log a user in and save a cookie', function(done) {
        agent
        .post('/user/login')
        .send({email: 'test@test.te', authkey: 'password1'})
        .expect('set-cookie', /connect.sid/)
        .expect(200)
        .end(function(err, res) {
            console.log({
                cookie: '/user/login ' + res.headers['set-cookie']
            });
            if(err) return done(err);
            done();
        });
    });

    it('should then be able to post a comment for an answer given a logged in user with the correct role', function(done) {
        agent
        .post('/answer/' + answer_id + '/comment')
        .expect(201)
        .end(function(err, res) {
            console.log({
                cookie: '/answer/:id/comment ' + res.headers['set-cookie']
            });
            if(err) return done(err);
            done();
        });
    });
});

A brief description of before() and after()

before(function(done) {
    app().then(function(result) {
        server = result.server;
        agent = request.agent(server);
        testutils.addUser()
        .then(testutils.addQuestion)
        .then(testutils.addAnswer)
        .then(function(answer) {
            answer_id = answer._id;
            done();
        });
    });
});

after(function(done) {
    testutils.mongoClear();
    done();
});

What I am doing inside before() is ensuring that the express() server has booted, that mongoose has started and that my dummy data has been inserted to the test database - all before any of these tests are started. This seems to be working fine and from my understanding, the before function should run to completion.

I am also assigning request.agent to a running instance of my express() server.

Finally, once everything is set up as expected, the first it test starts running and completes with the expected result, so it passes.

The second test runs as expected and passes.

The Third test fails because a 403 is returned where a 201 status would be expected. When looking closely at the cookie strings between the two tests, I see that they are completely different. This has been highlighted in this StackOverflow post.

Here is what I have tried:

  • I wanted to make sure that everything that was needed was ready before any tests ran, including making sure that Mongoose was running, the express server was running and that all the dummy data needed was inserted.
  • I place a console log message inside the /user/login endpoint to see if the session data was being set and it was. For reference, this is what the endpoint looks like.
app.post('/user/login', (req, res) => {
    user.checkUserCredentials(req.body)
    .then((result) => {
        if(!result.isactive) {
            res.status(403).json({
                info: 'Could not sign you in because your account has been deactivated.'
            });
            return;
        }
        user.setUserCookie(req, res, result);
        console.log(req.session); // This shows the user id correctly so I know it is working
        res.status(200).json({
            info: 'Login successful.',
            user: result
        });
    })
    .catch((error) => {
        res.status(403).json({
            info: 'Login unsuccessful.',
            error: error
        });
    });
 });
  • Inside the second test, I tried saving the cookies following some users suggestions, as follows:
.end(function(err, res) {
    if(err) return done(err);
    agent.saveCookies(res);
    done();
});
  • I then tried to retrieve that cookie in testing the endpoint that requires the user to be logged in, as follows:
it('should allow the user to post something', function(done) {
    let call = agent.post('/need/to/be/signed/in');
    agent.attachCookies(call);
    call
    .send({some: 'Content'})
    .expect(201)
    .end(function(err, res) {
        if(err) return done(err);
        done();
    });
});

For reference, I am also using the express-session module and inside my app.js file I have the following.

app.use(session({
    secret: 'dummysecret',
    resave: false,
    saveUninitialized: true,
    httpOnly: true,
    store: new connectMongo({
        mongooseConnection: mongoose.connection
    }),
    cookie: {
        httpOnly: false,
        maxAge: null,
        domain: app.config.cookieDomain,
        secure: app.config.httpsEnabled
    }
}));

I’m out of ideas as to what could be causing this. I have trawled through issues and discussion fora and tried many things to get it working, but to no avail. Could the code above be causing the issue?

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:11 (1 by maintainers)

github_iconTop GitHub Comments

11reactions
matfincommented, May 12, 2016

I was able to get this working in the end by doing the following:

  • First I set a global cookie variable at the top of my test
let cookie;
  • Then assigned the value of res.headers['set-cookie'] to that
it('POST answer/:id/comment - should then log a user in and save a cookie', function(done) {
        agent
        .post('/user/login')
        .send({email: 'test@test.te', authkey: 'password1'})
        .expect('set-cookie', /connect.sid/)
        .expect(200)
        .end(function(err, res) {
            cookie = res.headers['set-cookie']; //Setting the cookie
            if(err) return done(err);
            done();
        });
    });
  • Then in subsequent calls, I did something like this.
it('POST answer/:id/comment - should then be able with a logged in user.', function(done) {
        let call = agent.post('/answer/' + answer_id + '/comment');
        call.set('Cookie', cookie); //And then using it here
        call.send({content: 'A comment.'})
        .expect(201)
        .expect(function(res) {
            if(!res.body.info) throw new Error('info is missing');
            if(!res.body.comment) throw new Error('comment is missing');
        })
        .end(done);
    });

This is the only thing that seems to do the trick as a workaround.

9reactions
ReeganExEcommented, Nov 19, 2020

My root cause came from the secure configuration that I passed into express-session

It should be false when running test as supertest runs the express app on an insecure (HTTP) port.

session({
  // ...
  cookie: {
    secure: config.isDevelopment ? false : true,
  },
  // ...
});

Read more comments on GitHub >

github_iconTop Results From Across the Web

Persisting session cookies is not working · Issue #336 - GitHub
I am having an issue persisting the correct cookies when trying to test an endpoint that requires authentication.
Read more >
session not persisting when using secure cookie in NodeJS ...
You are setting cookie: {secure: true} but trying to access your server using HTTP. From the express-session documentation: cookie.secure.
Read more >
Persistent Cookie not Working - WordPress.org
2/ For cacheing plugins I have wp-rocket and perfmatters. I turned both those off and tested and saw the same 'session' for max...
Read more >
Types of Computer Cookies - AllAboutCookies.org
But not all cookies are the same. There are two types of cookies: Session cookies. Session cookies are temporary cookie files, which are...
Read more >
Cookies, document.cookie - The Modern JavaScript Tutorial
A write operation to document.cookie updates only cookies mentioned in it, but doesn't touch other cookies. For instance, this call sets a ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found