Persisting session cookies is not working
See original GitHub issueI 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 abefore()
andafter()
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 usesagent
to make a connection to the endpoint without signing in and that test expect(403) is correct. - My second
it()
function then usesagent
to attempt to sign in with the credentials for the dummy user I created earlier. Iexpect(200)
to pass, which it does. I alsoconsole.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 isexpect(201)
, but instead getexpect(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:
- Created 7 years ago
- Comments:11 (1 by maintainers)
Top GitHub Comments
I was able to get this working in the end by doing the following:
res.headers['set-cookie']
to thatThis is the only thing that seems to do the trick as a workaround.
My root cause came from the
secure
configuration that I passed intoexpress-session
It should be
false
when running test as supertest runs the express app on an insecure (HTTP) port.