From 47480efb0c6eedba43d0d7a33097e29c27b456ca Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Wed, 15 Jul 2020 08:30:27 +0100 Subject: [PATCH] Add some tests for session duration behaviour (#114) --- test/appSession.tests.js | 113 +++++++++++++++++++++++-- test/config.tests.js | 38 ++++++++- test/{auth.tests.js => login.tests.js} | 0 test/setup.js | 4 +- 4 files changed, 143 insertions(+), 12 deletions(-) rename test/{auth.tests.js => login.tests.js} (100%) diff --git a/test/appSession.tests.js b/test/appSession.tests.js index d41b8e34..390e5f7e 100644 --- a/test/appSession.tests.js +++ b/test/appSession.tests.js @@ -7,7 +7,8 @@ const request = require('request-promise-native').defaults({ const sinon = require('sinon'); const appSession = require('../lib/appSession'); -const sessionEncryption = require('./fixture/sessionEncryption'); +const { encrypted } = require('./fixture/sessionEncryption'); +const { makeIdToken } = require('./fixture/cert'); const { get: getConfig } = require('../lib/config'); const { create: createServer } = require('./fixture/server'); @@ -17,10 +18,25 @@ const defaultConfig = { issuerBaseURL: 'https://op.example.com', baseURL: 'https://example.org', secret: '__test_secret__', + errorOnRequiredAuth: true, +}; + +const login = async (claims) => { + const jar = request.jar(); + await request.post('/session', { + baseUrl, + jar, + json: { + id_token: makeIdToken(claims), + }, + }); + return jar; }; const baseUrl = 'http://localhost:3000'; +const HR_MS = 60 * 60 * 1000; + describe('appSession', () => { let server; @@ -62,7 +78,7 @@ describe('appSession', () => { baseUrl, json: true, headers: { - cookie: `appSession=${sessionEncryption.encrypted}`, + cookie: `appSession=${encrypted}`, }, }); assert.equal(res.statusCode, 200); @@ -75,7 +91,7 @@ describe('appSession', () => { baseUrl, json: true, headers: { - cookie: `appSession=${sessionEncryption.encrypted}`, + cookie: `appSession=${encrypted}`, }, }); assert.equal(res.statusCode, 200); @@ -158,7 +174,7 @@ describe('appSession', () => { json: true, jar, headers: { - cookie: `appSession=${sessionEncryption.encrypted}`, + cookie: `appSession=${encrypted}`, }, }); const [cookie] = jar.getCookies(baseUrl); @@ -194,7 +210,7 @@ describe('appSession', () => { json: true, jar, headers: { - cookie: `appSession=${sessionEncryption.encrypted}`, + cookie: `appSession=${encrypted}`, }, }); const [cookie] = jar.getCookies(baseUrl); @@ -220,7 +236,7 @@ describe('appSession', () => { json: true, jar, headers: { - cookie: `customName=${sessionEncryption.encrypted}`, + cookie: `customName=${encrypted}`, }, }); const [cookie] = jar.getCookies(baseUrl); @@ -243,7 +259,7 @@ describe('appSession', () => { json: true, jar, headers: { - cookie: `appSession=${sessionEncryption.encrypted}`, + cookie: `appSession=${encrypted}`, }, }); const [cookie] = jar.getCookies(baseUrl); @@ -265,7 +281,7 @@ describe('appSession', () => { json: true, jar, headers: { - cookie: `appSession=${sessionEncryption.encrypted}`, + cookie: `appSession=${encrypted}`, }, }); assert.equal(res.statusCode, 200); @@ -308,4 +324,85 @@ describe('appSession', () => { const res = await request.get('/session', { baseUrl, json: true }); assert.equal(res.statusCode, 200); }); + + it('should expire after 24hrs of inactivity by default', async () => { + const clock = sinon.useFakeTimers({ toFake: ['Date'] }); + server = await createServer(appSession(getConfig(defaultConfig))); + const jar = await login({ sub: '__test_sub__' }); + let res = await request.get('/session', { baseUrl, jar, json: true }); + assert.isNotEmpty(res.body); + clock.tick(23 * HR_MS); + res = await request.get('/session', { baseUrl, jar, json: true }); + assert.isNotEmpty(res.body); + clock.tick(25 * HR_MS); + res = await request.get('/session', { baseUrl, jar, json: true }); + assert.isEmpty(res.body); + clock.restore(); + }); + + it('should expire after 7days regardless of activity by default', async () => { + const clock = sinon.useFakeTimers({ toFake: ['Date'] }); + server = await createServer(appSession(getConfig(defaultConfig))); + const jar = await login({ sub: '__test_sub__' }); + let days = 7; + while (days--) { + clock.tick(23 * HR_MS); + let res = await request.get('/session', { baseUrl, jar, json: true }); + assert.isNotEmpty(res.body); + } + clock.tick(8 * HR_MS); + let res = await request.get('/session', { baseUrl, jar, json: true }); + assert.isEmpty(res.body); + clock.restore(); + }); + + it('should expire only after defined absoluteDuration', async () => { + const clock = sinon.useFakeTimers({ toFake: ['Date'] }); + server = await createServer( + appSession( + getConfig({ + ...defaultConfig, + session: { + rolling: false, + absoluteDuration: 10 * 60 * 60, + }, + }) + ) + ); + const jar = await login({ sub: '__test_sub__' }); + clock.tick(9 * HR_MS); + let res = await request.get('/session', { baseUrl, jar, json: true }); + assert.isNotEmpty(res.body); + clock.tick(2 * HR_MS); + res = await request.get('/session', { baseUrl, jar, json: true }); + assert.isEmpty(res.body); + clock.restore(); + }); + + it('should expire only after defined rollingDuration period of inactivty', async () => { + const clock = sinon.useFakeTimers({ toFake: ['Date'] }); + server = await createServer( + appSession( + getConfig({ + ...defaultConfig, + session: { + rolling: true, + rollingDuration: 24 * 60 * 60, + absoluteDuration: false, + }, + }) + ) + ); + const jar = await login({ sub: '__test_sub__' }); + let days = 30; + while (days--) { + clock.tick(23 * HR_MS); + let res = await request.get('/session', { baseUrl, jar, json: true }); + assert.isNotEmpty(res.body); + } + clock.tick(25 * HR_MS); + let res = await request.get('/session', { baseUrl, jar, json: true }); + assert.isEmpty(res.body); + clock.restore(); + }); }); diff --git a/test/config.tests.js b/test/config.tests.js index 8d5cc25e..1e251a0c 100644 --- a/test/config.tests.js +++ b/test/config.tests.js @@ -217,7 +217,6 @@ describe('get config', () => { assert.throws(() => { getConfig({ ...defaultConfig, - secret: '__test_session_secret__', session: { rollingDuration: 3.14159, }, @@ -225,6 +224,42 @@ describe('get config', () => { }, '"session.rollingDuration" must be an integer'); }); + it('should fail when rollingDuration is defined and rolling is false', function () { + assert.throws(() => { + getConfig({ + ...defaultConfig, + session: { + rolling: false, + rollingDuration: 100, + }, + }); + }, '"session.rollingDuration" must be false when "session.rolling" is disabled'); + }); + + it('should fail when rollingDuration is not defined and rolling is true', function () { + assert.throws(() => { + getConfig({ + ...defaultConfig, + session: { + rolling: true, + rollingDuration: false, + }, + }); + }, '"session.rollingDuration" must be provided an integer value when "session.rolling" is true'); + }); + + it('should fail when absoluteDuration is not defined and rolling is false', function () { + assert.throws(() => { + getConfig({ + ...defaultConfig, + session: { + rolling: false, + absoluteDuration: false, + }, + }); + }, '"session.absoluteDuration" must be provided an integer value when "session.rolling" is false'); + }); + it('should fail when app session secret is invalid', function () { assert.throws(() => { getConfig({ @@ -238,7 +273,6 @@ describe('get config', () => { assert.throws(() => { getConfig({ ...defaultConfig, - secret: '__test_session_secret__', session: { cookie: { httpOnly: '__invalid_httponly__', diff --git a/test/auth.tests.js b/test/login.tests.js similarity index 100% rename from test/auth.tests.js rename to test/login.tests.js diff --git a/test/setup.js b/test/setup.js index 33d6e503..a06e18b9 100644 --- a/test/setup.js +++ b/test/setup.js @@ -2,7 +2,7 @@ const nock = require('nock'); const wellKnown = require('./fixture/well-known.json'); const certs = require('./fixture/cert'); -before(function () { +beforeEach(function () { nock('https://op.example.com', { allowUnmocked: true }) .persist() .get('/.well-known/openid-configuration') @@ -24,6 +24,6 @@ before(function () { .reply(200, certs.jwks); }); -after(function () { +afterEach(function () { nock.cleanAll(); });