rjz / supertest-session Goto Github PK
View Code? Open in Web Editor NEWPersistent sessions for supertest
License: Other
Persistent sessions for supertest
License: Other
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!
Problem
req.session._csrf
in router.post('/login')
is undefined
_csrf
key is added to req.session
in router.get('/login')
.Test code
var request = require('supertest'),
var app = require('../../app');
var session;
var testSession = null;
before(function(done) {
session = require('supertest-session');
testSession = session(app);
testSession.get('/login')
.end(function(err, res) {
if (err) {
console.log(err);
}
cookie = res.headers['set-cookie'];
testSession.post('/login')
.type('form')
.set('cookie', cookie)
.send({username: 'xxxxxxx'})
.send({password: 'xxxxxxx'})
.end(function(err, res) {
if (err) {
console.log(err);
}
});
});
});
router.get('/login',
function(req, res, next) {
var secret = tokens.secretSync();
var token = tokens.create(secret);
req.session._csrf = secret;
res.cookie("_csrf", token);
res.render('login', {error: req.flash('error')});
}
);
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
});
supertest-session has two deviations from the supertest API:
Session
class with a specific configSession
s are created using the new
keyword, but may not adjust their configuration at this timeBoth decisions may be convenient in certain cases, but they limit runtime configuration (see #12) and add unnecessary complexity to the public API. An easy fix? Create Session
instances via a supertest-esque factory function.
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":"[email protected]","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));
});
});
`
Originally posted by @ahmadbingulzar in #32 (comment)
I have the followin test routes, the first sets a cookie, the second retrieves it.
server.get('/t2/:z', (req, res) => {
req.session = {v: req.params.z}
res.send(req.session)
})
server.get('/t3', (req, res) => {
logger.debug("cookie header: %s", req.headers.cookie)
res.send(req.session)
})
I am using cookie-session: https://github.com/expressjs/cookie-session
to handle sessions
When I test the above routes with a browser, it works as expected, and I can see that cookie-session sets two cookies, one for the session object, and one for the signature (session.sig), as the log shows:
cookie header: _ga=GA1.1.984665860.1553382018; session=eyJ2IjoicXEifQ==; session.sig=kJH4iEj4NkYR4HwYyo0QI42fF9A
When I use supertest-session, only one cookie gets set, as show by the log :
cookie header: session=eyJ2IjoicXdlcnR5In0=
Which causes cookie-session to not recognize the session, since it has no signature cookie.
The jest / supertest-session code is below.
Is there a way to tell supertest-session to persist all cookies set during a request ?
import cookieSession from 'cookie-session'
import session from 'supertest-session'
import express from 'express'
const server = createRoutes(
express()
.use(express.json())
.use(cookieSession({
name: 'session',
signed: true,
secret: process.env.COOKIE_SIGNING_SECRET,
maxAge: 30 * 60 * 1000
}))
.use((req, res, next) => {
req.session.now = Date.now()
next()
})
)
test.only("test session-cookie", async done => {
const testSession = session(server)
const res1 = await testSession.get('/t2/qwerty').send()
expect(res1.status).toEqual(200)
expect(res1.body.v).toEqual("qwerty")
const res2 = await testSession.get('/t3').send()
expect(res2.status).toEqual(200)
expect(res2.body.v).toEqual("qwerty")
})
Upgrade to use supertest 1.x
the following documentation is missleading, but obvious passing envs to the app ist not possible, when the app is already initialized.
var Session = require('supertest-session')({
app: require('../../path/to/app'),
envs: { NODE_ENV: 'development' }
});
what you have to do work with envs consistently:
process.env['NODE_ENV'] = "development";
var Session = require('supertest-session')({
app: require('../../path/to/app'),
});
I would suggest to depreciate the use of your envs, cause the status of envs changes over runtime.
Hi,
In my use-case I have cookies with the option "Expires", so I send headers like that :
Set-Cookie : connect.sid=s%3AlMtQUjJP9PkhsoIlWwFCMjm-rawiAuS4.aIvx9Vi%2BEisilzFza9SO6U7Ko8doDy4QF5ypVWTCz%2BI; Path=/; Expires=Mon, 17 Nov 2014 13:53:31 GMT; HttpOnly
But with this lib when I make a request you remove the parameter "Path" but keep "Expires".
Here is an example :
Cookies : connect.sid=s:QGr6asW74gtqnC-PQ_bsP2wXHG3por9U.EejvZh2m2TU6i3te4LIcmBNCUsTYqDHDBTvkc5OkLA8,Expires=Mon, 17 Nov 2014 13:59:54 GMT
If I'm not mistaken, it would remove "Expires".
I am new to TypeScript and have noticed that there are no TypeScript type definitions available for this package. Could you please guide me on how to use it in a TypeScript project?
When running supertest-session in a project that uses Yarn PnP / Berry, the following two errors are reported:
supertest-session tried to access methods, but it isn't declared in its dependencies; this makes the require call ambiguous and unsound.
Required package: methods (via "methods")
Required by: supertest-session@virtual:640d59121dc50aef8c4e2e9c0fc24c425951c20d64b04f5476f7080bfeeaf953e8d8f92366ff62026e8f3b035a2de8fa49aa2900c07271fe8ad998892fded072#npm:4.0.0 (via .yarn/$$virtual/supertest-session-virtual-c7c934f209/0/cache/supertest-session-npm-4.0.0-30aab972e9-4b62b93e4e.zip/node_modules/supertest-session/)
supertest-session tried to access cookiejar, but it isn't declared in its dependencies; this makes the require call ambiguous and unsound.
Required package: cookiejar (via "cookiejar")
Required by: supertest-session@virtual:640d59121dc50aef8c4e2e9c0fc24c425951c20d64b04f5476f7080bfeeaf953e8d8f92366ff62026e8f3b035a2de8fa49aa2900c07271fe8ad998892fded072#npm:4.0.0 (via .yarn/$$virtual/supertest-session-virtual-c7c934f209/0/cache/supertest-session-npm-4.0.0-30aab972e9-4b62b93e4e.zip/node_modules/supertest-session/)
This is using version [email protected]
.
Workaround
Add the following to .yarnrc.yml
:
packageExtensions:
supertest-session@*:
dependencies:
methods: '*'
cookiejar: '*
After updating to 1.0.0 from 0.0.7, I get the following error
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory
I noticed this error when then the HTTP status was set to 400 or 500 in some rather complex tests.
Supertest lets you call done inside of an expect call instead of calling .end(done). When you use supertest-session to do this, you cannot chain methods. For example, when you try something like this:
var request = require('supertest-session');
var user = request(app)
.post('/login')
.type('form')
.send(credentials)
.expect(302, done);
It does not chain correctly, and you need to instead use:
var request = require('supertest-session');
var user = request(app);
user
.post('/login')
.type('form')
.send(credentials)
.expect(302, done);
The documented example does not work as expected, because the before
does not call done
.
It should look like:
before(function (done) {
this.sess = new Session();
done();
});
after(function (done) {
this.sess.destroy();
done();
});
The fix in #29 broke tests that create sessions around an existing instance of an app.
app, in my case, is an Object and not a Function. I already have a full working server, but I still need to extract the url from the app object itself.
HT @XVincentX
Would you be open to a PR that adds a mongoose-style exec
method?
Usage would look like this:
supertest( app )
.get( '/someRoute' )
.expect( 200 )
// wraps supertest.end in a promise
.exec()
.then( function() {
// success
})
.catch( function( error ) {
// failure
});
This is the how to do, hoping that will be putted into documentation:
var session = new Session();
// here we are setting the session value
session.app.request.session = {
user: {
account: {
username: USERNAME
}
}
};
session
.post(util.format(urlAnsw, _id, 'FAKEUSER'))
.expect(400)
.expect('Content-Type', /json/)
.end(function(err, res) {
res.body.should.have.property('ok', false);
res.body.err.should.be.exactly('Username is not the one online from this IP.');
session.destroy();
done();
})
Hi @rjz,
We just bumped our stack to Node 10 and NPM6 and I got this report from npm audit
from this lib.
│ Low │ Large gzip Denial of Service
│ Package │ superagent
│ Patched in │ >=3.7.0
│ Dependency of │ supertest-session [dev]
│ Path │ supertest-session > supertest > superagent
│ More info │ https://nodesecurity.io/advisories/479
I have tried updating supertest
to the 3.1.0
on my local, tests are passing, our projects haven't got issues and the security issue is resolved.
Would you considering update this dependency to remove the warning? I can send a PR if you prefer it, just let me know.
Thanks!
Hello guys, actually i'm trying to test with supertest-session when I initialize the login I set the informations of the user on req.session.infoUser but jest is returning the error : Cannot set properties of undefined (setting 'infoUser')
I don't understand why i have this problem bc the function is working well and i can connect on my server req.session is working well
👋 @rjz thanks for creating this module. It looks like just what we need for testing the electron website.
I have the following test:
test('language query param for one-off viewing in other languages', async () => {
const frenchContent = 'fenêtres'
const sesh = session(app)
let res = await sesh.get('/docs/api/browser-window?lang=fr-FR')
let $ = cheerio.load(res.text)
$('blockquote').text().should.include(frenchContent)
// verify that the query param does not persist as a cookie
res = await sesh.get('/docs/api/browser-window')
$ = cheerio.load(res.text)
$('blockquote').text().should.not.include(frenchContent)
})
The test itself passes, but it causes mocha
to hang forever after the suite has run. If I disable this single test, the problem goes away. I thought maybe this was an issue with async/await. I change the code to use Promises, then
, and done
, but it still hangs.
Any ideas?
Failing PR: electron/electronjs.org-old#1034
I want to use supertest-session to test a non-Express API. In my original code, I'd have this line:
request('http://www.google.com').get('/asdf').expect(404).end(done);
I get an error along the lines of "undefined is not a function." Through a little debugging, I found that it thought request("...").get
is undefined. So I opened up the module and found this in the module.exports
function:
if (typeof app != 'function') {
return deprecatedLegacySession(app);
}
deprecatedLegacySession calls:
function legacySession (config) {
if (!config) config = {};
// Bind session to `config`
function LegacySession () {
Session.call(this, config.app, config);
}
util.inherits(LegacySession, Session);
assign(LegacySession.prototype, {}, config.helpers);
return LegacySession;
}
And there's a lot of issues with this. First, config
is the app string I passed in and is not used properly as the string. The function returns the inner LegacySession function, it should create a new instance of the LegacySession instead of returning the function. That doesn't matter because the LegacySession is messed up. It tries to get config.app
but config is a string. The whole legacySession function should be:
function legacySession (app, config) {
if (!config) config = {};
// Bind session to `config`
function LegacySession () {
Session.call(this, app, config);
}
util.inherits(LegacySession, Session);
assign(LegacySession.prototype, {}, config.helpers);
return new LegacySession();
}
Or, an even easier solution, completely remove the if (typeof app != 'function') {...}
check out of your module.exports
function. Doing so means I can properly use request("http://www.google.com")
as advertised.
supertest
instances now expose a superagent .agent
that includes cookie attachment/handling. Leaning on this implementation will simplify supertest-session
and allow its users to take advantage of any features and options supported by superagent.
Hey RJ,
I'm updating express for my company and in the process updating supertest-session
. There's a piece of the README I'm a bit confused on, maybe you could help me and then clarify the README if it's accurate and fix it if it isn't.
On L51 it looks like we can go through with a privileged request after a previous signin. But, our testSession
from the example is repopulated in a beforeEach
callback. If testSession
is trampled each time, wouldn't the signed in session cookies get nuked for each one? If so, looks like the README should employ a before
function instead of a beforeEach
. If not, color me confused, and maybe the README could use a note on it, depending on from how deep a well my confusion comes.
Cheers,
-- Aaron
const session = require('supertest-session');
const myApp = require('../../app');
describe('Test sample', () => {
let testSession;
let authenticatedSession;
beforeEach( () => {
testSession = session(myApp);
});
it('/member/login', (done) => {
testSession.post('/member/login')
.send({ userid: '[email protected]', password: 'test' })
.expect(302)
.end(function (err) {
if (err) { return done(err); }
authenticatedSession = testSession;
return done();
});
});
// if there is this test, test freezes after all test suite finished
it('/member/profile', (done) => {
authenticatedSession.get('/member/profile')
.expect(200)
.end(done);
});
});
Written by Typescript, but executed as javascript with jest
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.