Skip to content

Commit f0acf18

Browse files
committed
Expire cookie immediately upon destroying session
Fixes #241.
1 parent 3c7a35b commit f0acf18

File tree

2 files changed

+197
-8
lines changed

2 files changed

+197
-8
lines changed

Diff for: index.js

+38-8
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,7 @@ function session(options){
139139
store.generate = function(req){
140140
req.sessionID = generateId(req);
141141
req.session = new Session(req);
142-
req.session.cookie = new Cookie(cookieOptions);
143-
144-
if (cookieOptions.secure === 'auto') {
145-
req.session.cookie.secure = issecure(req, trustProxy);
146-
}
142+
req.session.cookie = createCookie(cookieOptions, req, trustProxy);
147143
};
148144

149145
var storeImplementsTouch = typeof store.touch === 'function';
@@ -184,20 +180,35 @@ function session(options){
184180

185181
// set-cookie
186182
onHeaders(res, function(){
183+
// Is this an existing session that is being destroyed?
184+
var isBeingDestroyed = !!cookieId && shouldDestroy(req);
185+
187186
if (!req.session) {
188187
debug('no session');
189-
return;
188+
if (!isBeingDestroyed) {
189+
return;
190+
}
190191
}
191192

192-
var cookie = req.session.cookie;
193+
var cookie = req.session ? req.session.cookie : undefined;
194+
195+
if (isBeingDestroyed) {
196+
if (cookie == null) {
197+
debug('creating expired cookie');
198+
cookie = createCookie(cookieOptions, req, trustProxy);
199+
}
200+
201+
// Set the cookie to immediately expire on the client
202+
cookie.maxAge = 0;
203+
}
193204

194205
// only send secure cookies via https
195206
if (cookie.secure && !issecure(req, trustProxy)) {
196207
debug('not secured');
197208
return;
198209
}
199210

200-
if (!shouldSetCookie(req)) {
211+
if (!isBeingDestroyed && !shouldSetCookie(req)) {
201212
return;
202213
}
203214

@@ -570,6 +581,25 @@ function issecure(req, trustProxy) {
570581
return proto === 'https';
571582
}
572583

584+
/**
585+
* Create a new Cookie for this request.
586+
*
587+
* @param {Object} cookieOptions
588+
* @param {Object} req
589+
* @param {Boolean} [trustProxy]
590+
* @return {Cookie}
591+
* @private
592+
*/
593+
function createCookie(cookieOptions, req, trustProxy) {
594+
cookieOptions = cookieOptions || {};
595+
596+
var cookie = new Cookie(cookieOptions);
597+
if (cookieOptions.secure === 'auto') {
598+
cookie.secure = issecure(req, trustProxy);
599+
}
600+
return cookie;
601+
}
602+
573603
/**
574604
* Set cookie on response.
575605
*

Diff for: test/session.js

+159
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,165 @@ describe('session()', function(){
12101210
});
12111211
});
12121212

1213+
it('should set expired cookie to override persistent cookie on req.session = null', function(done){
1214+
var store = new session.MemoryStore();
1215+
var app = express()
1216+
.use(session({ store: store, unset: 'destroy', secret: 'keyboard cat', cookie: { maxAge: min } }))
1217+
.use(function(req, res, next){
1218+
req.session.count = req.session.count || 0;
1219+
req.session.count++;
1220+
if (req.session.count === 2) req.session = null;
1221+
res.end();
1222+
});
1223+
1224+
request(app)
1225+
.get('/')
1226+
.expect(shouldSetCookie('connect.sid'))
1227+
.expect(200, function(err, res){
1228+
if (err) return done(err);
1229+
1230+
var now = new Date();
1231+
1232+
var expiry = expires(res);
1233+
assert.ok(expiry);
1234+
1235+
var a = new Date(expiry);
1236+
1237+
var delta = a.valueOf() - now.valueOf();
1238+
assert.ok(delta > (min - 1000) && delta <= min);
1239+
1240+
request(app)
1241+
.get('/')
1242+
.set('Cookie', cookie(res))
1243+
.expect(shouldSetCookie('connect.sid'))
1244+
.expect(200, function(err, res){
1245+
if (err) return done(err);
1246+
1247+
now = new Date();
1248+
1249+
expiry = expires(res);
1250+
assert.ok(expiry);
1251+
1252+
var b = new Date(expiry);
1253+
1254+
delta = b.valueOf() - a.valueOf();
1255+
assert.ok(delta <= 0);
1256+
1257+
delta = b.valueOf() - now.valueOf();
1258+
assert.ok(delta <= 0);
1259+
1260+
done();
1261+
});
1262+
});
1263+
});
1264+
1265+
it('should set expired cookie to override session cookie on req.session = null', function(done){
1266+
var store = new session.MemoryStore();
1267+
var app = express()
1268+
.use(session({ store: store, unset: 'destroy', secret: 'keyboard cat' }))
1269+
.use(function(req, res, next){
1270+
req.session.count = req.session.count || 0;
1271+
req.session.count++;
1272+
if (req.session.count === 2) req.session = null;
1273+
res.end();
1274+
});
1275+
1276+
request(app)
1277+
.get('/')
1278+
.expect(shouldSetCookie('connect.sid'))
1279+
.expect(200, function(err, res){
1280+
if (err) return done(err);
1281+
1282+
assert.ok(!expires(res));
1283+
1284+
request(app)
1285+
.get('/')
1286+
.set('Cookie', cookie(res))
1287+
.expect(shouldSetCookie('connect.sid'))
1288+
.expect(200, function(err, res){
1289+
if (err) return done(err);
1290+
1291+
var now = new Date();
1292+
1293+
var expiry = expires(res);
1294+
assert.ok(expiry);
1295+
1296+
var b = new Date(expiry);
1297+
1298+
var delta = b.valueOf() - now.valueOf();
1299+
assert.ok(delta <= 0);
1300+
1301+
done();
1302+
});
1303+
});
1304+
});
1305+
1306+
it('should not set an expired cookie on destroyed session if session is regenerated', function(done){
1307+
var store = new session.MemoryStore();
1308+
var app = express()
1309+
.use(session({ store: store, unset: 'destroy', secret: 'keyboard cat', cookie: { maxAge: min } }))
1310+
.use(function(req, res, next){
1311+
req.session.count = req.session.count || 0;
1312+
req.session.count++;
1313+
if (req.session.count === 2) {
1314+
req.session.destroy(function(err) {
1315+
if (err) return done(err);
1316+
1317+
req.sessionStore.regenerate(req, function(err) {
1318+
if (err) return done(err);
1319+
1320+
req.session.now = Date.now();
1321+
res.end();
1322+
});
1323+
});
1324+
}
1325+
else {
1326+
res.end();
1327+
}
1328+
});
1329+
1330+
request(app)
1331+
.get('/')
1332+
.expect(shouldSetCookie('connect.sid'))
1333+
.expect(200, function(err, res){
1334+
if (err) return done(err);
1335+
1336+
var now = new Date();
1337+
1338+
var expiry = expires(res);
1339+
assert.ok(expiry);
1340+
1341+
var a = new Date(expiry);
1342+
1343+
var delta = a.valueOf() - now.valueOf();
1344+
assert.ok(delta > (min - 1000) && delta <= min);
1345+
1346+
request(app)
1347+
.get('/')
1348+
.set('Cookie', cookie(res))
1349+
.expect(shouldSetCookie('connect.sid'))
1350+
.expect(200, function(err, res){
1351+
if (err) return done(err);
1352+
1353+
now = new Date();
1354+
1355+
expiry = expires(res);
1356+
assert.ok(expiry);
1357+
1358+
var b = new Date(expiry);
1359+
1360+
// The session regenerated so the expiration date should be the same as the original
1361+
delta = b.valueOf() - a.valueOf();
1362+
assert.equal(delta, 0);
1363+
1364+
delta = b.valueOf() - now.valueOf();
1365+
assert.ok(delta > 0);
1366+
1367+
done();
1368+
});
1369+
});
1370+
});
1371+
12131372
it('should not set cookie if initial session destroyed', function(done){
12141373
var store = new session.MemoryStore();
12151374
var app = express()

0 commit comments

Comments
 (0)