rjz / supertest-session

Persistent sessions for supertest
Other
88 stars 21 forks source link

Cannot retrieve session data stored in prior request #32

Closed YukiThornton closed 6 years ago

YukiThornton commented 6 years ago

Hi. My team is struggling to implement csrf tokens to our express APIs and run tests correctly. Our app works well on real browsers, but the tests related with sessions fail. We added csrf secret to session in GET request, but we cannot retrieve the secret in subsequent POST request. We're trying to figure out whether we're using supertest-session in a wrong way, or something unexpected is happening (ex. some other Express middlewares interfere the test code). We also want to know how to access session objects in test code to debug more closely. We'd appreciate any help!

router.post('/login', function(req, res, next) { var secret = req.session._csrf; var token = req.cookies._csrf; console.log(secret); // undefined console.log(token);

if (tokens.verify(secret, token) === false) { return next(new Error("Invalid Token")); }

// authenticate user });

rjz commented 6 years ago

Hey, @okunoyuki!

Regarding test state, any cookies held by the session should be available from testSession.cookies (see e.g. this test). supertest-session is meant to handle cookie-based sessions transparently; if that's broken, there's a bug.

As far as the problem here, I'm not sure exactly what's up. When you have a moment, could you provide a few more details about the implementation and behavior you're seeing?

  1. what middleware is the app using for session management?
  2. if you log out res.headers after calling GET /login in the test setup, the _csrf cookie should be present. Is a session cookie (if expected) present as well? And if so, does it (or its backing datastore) contain the expected values?
  3. finally, if the cookies sent from the app look correct, are they also present in testSession.cookies?

Thanks in advance!

YukiThornton commented 6 years ago

Thank you for your quick and kind reply! @rjz I didn't know about testSession.cookies but that sounds convenient!

Looks like we use multiple session-related frameworks(express-session and passport). Maybe we shouldn't use both of them at a time?

var passport = require('passport');
app.use(require('express-session')({ secret: 'keyboard cat', resave: false, saveUninitialized: false }));
app.use(passport.session());

For 2, the _csrf cookie exists in res.headers after calling GET /login (The following is the output that I got). I assume that I can access 'session cookie' using supertest-session(I apologize if it's a totally wrong assumption), but I don't know how. Could you show me how to access session?

{ 'cache-control': 'private,no-store,no-cache,must-revalidate,proxy-revalidate',
  pragma: 'no-cache',
  'set-cookie': 
   [ '_csrf=XXXXXXXXXXXXXXXXXXXXXXXXXX; Path=/',
     'connect.sid=YYYYYYYYYYYYYYYYYYYYYYY; Path=/; HttpOnly' ],
  'content-type': 'text/html; charset=utf-8',
  'content-length': '1394',
  etag: 'W/"572-LEgv480pG9D7SI5tj94/Zg"',
  date: 'Mon, 29 Jan 2018 23:36:59 GMT',
  connection: 'close' }

For 3, testSession.cookies had _csrf cookie with expected values.

rjz commented 6 years ago

Got it—thanks, @okunoyuki!

Depending on what backing store you are using with express-session, it may not be possible (or desirable) to peek into the session from outside the app. But I think we may be able to make this work from the outside.

First thought: since supertest-session will echo any cookies it receives, try dropping .send('cookie', ...) from the test setup:

testSession.get('/login')
  .end(function(err, res) {
    if (err) {
      return done(err);
    }

    testSession.post('/login')
      .type('form')
      .send({
        username: 'xxxxxxxx',
        password: 'xxxxxxxx',
      })
      .end(done);
  });

It looks like the session cookie is being attached correctly (you can see it in the HTTP requests, and it should appear in testSession.cookies['connect.sid'] after the initial request). The next thought, then, would be to make sure the session is being restored correctly inside the app. If you log out req.cookies in the post routes, the connect.sid cookie should match the cookie seen inside the tests. If it doesn't, it's possible that something is missing in the express-session configuration.

YukiThornton commented 6 years ago

Thank you! @rjz I'm sorry I was pretty busy this week and haven't tried you ideas yet. I'll try them and get back to you next week!

YukiThornton commented 6 years ago

@rjz Thank you for your advice! It worked without .send('cookie', ...). And I found out that there is something wrong with express-session configuration since the connect.sid cookie in the post routes didn't match the one inside the tests. Since my problem doesn't seem relevant to supertest-session for now, you can close this issue. Thank you so much!

rjz commented 6 years ago

Awesome, @okunoyuki, I'm glad to hear that it's working!

Please do be in touch if you run into any other issues.

ahmadbingulzar commented 1 year ago

having the same issue
getting XSRF-TOKEN successfully but when i try to login it shows XSRF-TOKEN mismatch also it is working preety fine on browsers and postman

`const app = require('../../../app.js'); const expect = require('chai').expect; const request = require('supertest'); var csrfToken;

describe('GET /api/csrf-token', () => { it('OK, Getting CSRF Token', (done) => { request(app).get('/api/csrf-token') .expect(200) .then((res) => { const body = res.body; csrfToken=body.csrfToken; expect(body).to.contain.property('csrfToken'); done(); }) .catch((err) => done(err)); }); });

describe('POST /api/users/login', () => { it('OK, Logging in with super admin crediantials', (done) => { request(app).post('/api/users/login') .set('Content-Type', 'application/json') .set("Accept","/") .set("XSRF-TOKEN",csrfToken) .set("Cookie","XSRF-TOKEN",csrfToken) .set("withCredentials",true) .expect(200) .send({"email":"super@abc.com","password":"abc"}) .expect((res) => { const body = res.body; console.log(body); expect(body.message).to.contain.property('Auth Successful'); expect(body).to.contain.property('token'); done(); }) .catch((err) => done(err)); }); }); `