Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ function session(options) {
// req.secret is passed from the cookie parser middleware
var secrets = secret || [req.secret];

var originalCookieHash;
var originalHash;
var originalId;
var savedHash;
Expand Down Expand Up @@ -383,6 +384,7 @@ function session(options) {
store.generate(req);
originalId = req.sessionID;
originalHash = hash(req.session);
originalCookieHash = cookieHash(req.session.cookie);
wrapmethods(req.session);
}

Expand All @@ -391,6 +393,7 @@ function session(options) {
store.createSession(req, sess)
originalId = req.sessionID
originalHash = hash(sess)
originalCookieHash = cookieHash(req.session.cookie)

if (!resaveSession) {
savedHash = originalHash
Expand Down Expand Up @@ -450,6 +453,11 @@ function session(options) {
return originalId === sess.id && savedHash === hash(sess);
}

// check if session cookie has been modified
function isCookieModified(sess) {
return originalCookieHash !== cookieHash(sess.cookie);
}

// determine if session should be destroyed
function shouldDestroy(req) {
return req.sessionID && unsetDestroy && req.session == null;
Expand Down Expand Up @@ -488,7 +496,7 @@ function session(options) {

return cookieId !== req.sessionID
? saveUninitializedSession || isModified(req.session)
: rollingSessions || req.session.cookie.expires != null && isModified(req.session);
: rollingSessions || req.session.cookie.expires != null && isModified(req.session) || isCookieModified(req.session);
}

// generate a session if the browser doesn't send a sessionID
Expand Down Expand Up @@ -666,6 +674,35 @@ function hash(sess) {
.digest('hex')
}

/**
* Hash the given session `cookie` object omitting changes to `.expires`.
*
* The expiry is refreshed every time the session is touched, so it
* cannot take part in detecting changes to the cookie.
*
* @param {Object} cookie
* @return {String}
* @private
*/

function cookieHash(cookie) {
// serialize
var str = JSON.stringify(cookie.data, function (key, val) {
// ignore expires property
if (key === 'expires') {
return
}

return val
})

// hash
return crypto
.createHash('sha1')
.update(str, 'utf8')
.digest('hex')
}

/**
* Determine if request is secure.
*
Expand Down
43 changes: 43 additions & 0 deletions test/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -2445,6 +2445,49 @@ describe('session()', function(){
})
})

describe('.maxAge changed without modifying the session', function () {
before(function (done) {
var ctx = this

ctx.cookie = ''
ctx.server = createServer({ cookie: { maxAge: 2000 } }, function (req, res) {
if (req.url === '/set') {
req.session.count = 1
res.end('set')
} else if (req.url === '/extend') {
req.session.cookie.maxAge = 5000
req.session.touch()
req.session.save(function () { res.end('extended') })
} else {
res.end('plain')
}
})

request(ctx.server)
.get('/set')
.end(function (err, res) {
ctx.cookie = res && cookie(res)
done(err)
})
})

it('should not set-cookie when nothing changed', function (done) {
request(this.server)
.get('/')
.set('Cookie', this.cookie)
.expect(shouldNotHaveHeader('Set-Cookie'))
.expect(200, 'plain', done)
})

it('should set-cookie when only the cookie is extended and saved', function (done) {
request(this.server)
.get('/extend')
.set('Cookie', this.cookie)
.expect(shouldSetCookieToExpireIn('connect.sid', 5000))
.expect(200, 'extended', done)
})
})

describe('.expires', function(){
describe('when given a Date', function(){
it('should set absolute', function(done){
Expand Down