@prairielearn/session 2.0.6 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.cjs +3 -0
- package/CHANGELOG.md +6 -0
- package/README.md +18 -3
- package/dist/before-end.js +1 -5
- package/dist/before-end.js.map +1 -1
- package/dist/before-end.test.js +11 -16
- package/dist/before-end.test.js.map +1 -1
- package/dist/cookie.d.ts +0 -4
- package/dist/cookie.js +4 -33
- package/dist/cookie.js.map +1 -1
- package/dist/index.d.ts +25 -14
- package/dist/index.js +55 -47
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +213 -210
- package/dist/index.test.js.map +1 -1
- package/dist/memory-store.d.ts +1 -1
- package/dist/memory-store.js +1 -5
- package/dist/memory-store.js.map +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +9 -20
- package/dist/session.js.map +1 -1
- package/dist/session.test.js +35 -37
- package/dist/session.test.js.map +1 -1
- package/dist/store.js +1 -2
- package/dist/store.js.map +1 -1
- package/dist/test-utils.d.ts +1 -1
- package/dist/test-utils.js +1 -5
- package/dist/test-utils.js.map +1 -1
- package/package.json +5 -4
- package/src/before-end.test.ts +1 -1
- package/src/cookie.ts +0 -23
- package/src/index.test.ts +15 -6
- package/src/index.ts +69 -46
- package/src/memory-store.ts +1 -1
- package/src/session.test.ts +2 -2
- package/src/session.ts +1 -1
package/dist/index.test.js
CHANGED
|
@@ -1,38 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const set_cookie_parser_1 = require("set-cookie-parser");
|
|
11
|
-
const express_async_handler_1 = __importDefault(require("express-async-handler"));
|
|
12
|
-
const express_test_utils_1 = require("@prairielearn/express-test-utils");
|
|
13
|
-
const index_1 = require("./index");
|
|
14
|
-
const memory_store_1 = require("./memory-store");
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { assert } from 'chai';
|
|
3
|
+
import fetch from 'node-fetch';
|
|
4
|
+
import fetchCookie from 'fetch-cookie';
|
|
5
|
+
import setCookie from 'set-cookie-parser';
|
|
6
|
+
import asyncHandler from 'express-async-handler';
|
|
7
|
+
import { withServer } from '@prairielearn/express-test-utils';
|
|
8
|
+
import { createSessionMiddleware } from './index.js';
|
|
9
|
+
import { MemoryStore } from './memory-store.js';
|
|
15
10
|
const TEST_SECRET = 'test-secret';
|
|
11
|
+
function parseSetCookie(header) {
|
|
12
|
+
return setCookie.parse(setCookie.splitCookiesString(header));
|
|
13
|
+
}
|
|
16
14
|
describe('session middleware', () => {
|
|
17
15
|
it('sets a session cookie', async () => {
|
|
18
|
-
const app = (
|
|
19
|
-
app.use(
|
|
16
|
+
const app = express();
|
|
17
|
+
app.use(createSessionMiddleware({ secret: TEST_SECRET, store: new MemoryStore() }));
|
|
20
18
|
app.get('/', (_req, res) => res.sendStatus(200));
|
|
21
|
-
await
|
|
22
|
-
const res = await (
|
|
23
|
-
|
|
19
|
+
await withServer(app, async ({ url }) => {
|
|
20
|
+
const res = await fetch(url);
|
|
21
|
+
assert.equal(res.status, 200);
|
|
24
22
|
const header = res.headers.get('set-cookie');
|
|
25
|
-
const cookies = (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
const cookies = parseSetCookie(header ?? '');
|
|
24
|
+
assert.equal(cookies.length, 1);
|
|
25
|
+
assert.equal(cookies[0].name, 'session');
|
|
26
|
+
assert.equal(cookies[0].path, '/');
|
|
29
27
|
});
|
|
30
28
|
});
|
|
31
29
|
it('sets a session cookie with options', async () => {
|
|
32
|
-
const app = (
|
|
33
|
-
app.use(
|
|
30
|
+
const app = express();
|
|
31
|
+
app.use(createSessionMiddleware({
|
|
34
32
|
secret: TEST_SECRET,
|
|
35
|
-
store: new
|
|
33
|
+
store: new MemoryStore(),
|
|
36
34
|
cookie: {
|
|
37
35
|
name: 'prairielearn_session',
|
|
38
36
|
httpOnly: true,
|
|
@@ -42,163 +40,163 @@ describe('session middleware', () => {
|
|
|
42
40
|
},
|
|
43
41
|
}));
|
|
44
42
|
app.get('/', (_req, res) => res.sendStatus(200));
|
|
45
|
-
await
|
|
46
|
-
const res = await (
|
|
47
|
-
|
|
43
|
+
await withServer(app, async ({ url }) => {
|
|
44
|
+
const res = await fetch(url);
|
|
45
|
+
assert.equal(res.status, 200);
|
|
48
46
|
const header = res.headers.get('set-cookie');
|
|
49
|
-
const cookies = (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
47
|
+
const cookies = parseSetCookie(header ?? '');
|
|
48
|
+
assert.equal(cookies.length, 1);
|
|
49
|
+
assert.equal(cookies[0].name, 'prairielearn_session');
|
|
50
|
+
assert.equal(cookies[0].path, '/');
|
|
51
|
+
assert.isTrue(cookies[0].httpOnly);
|
|
52
|
+
assert.equal(cookies[0].domain, '.localhost');
|
|
53
|
+
assert.equal(cookies[0].sameSite, 'Strict');
|
|
56
54
|
});
|
|
57
55
|
});
|
|
58
56
|
it('sets a secure cookie for proxied HTTPS request', async () => {
|
|
59
|
-
const app = (
|
|
57
|
+
const app = express();
|
|
60
58
|
app.enable('trust proxy');
|
|
61
|
-
app.use(
|
|
59
|
+
app.use(createSessionMiddleware({
|
|
62
60
|
secret: TEST_SECRET,
|
|
63
|
-
store: new
|
|
61
|
+
store: new MemoryStore(),
|
|
64
62
|
cookie: {
|
|
65
63
|
secure: true,
|
|
66
64
|
},
|
|
67
65
|
}));
|
|
68
66
|
app.get('/', (_req, res) => res.sendStatus(200));
|
|
69
|
-
await
|
|
70
|
-
const res = await (
|
|
67
|
+
await withServer(app, async ({ url }) => {
|
|
68
|
+
const res = await fetch(url, {
|
|
71
69
|
headers: {
|
|
72
70
|
'X-Forwarded-Proto': 'https',
|
|
73
71
|
},
|
|
74
72
|
});
|
|
75
|
-
|
|
73
|
+
assert.equal(res.status, 200);
|
|
76
74
|
const header = res.headers.get('set-cookie');
|
|
77
|
-
const cookies = (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
const cookies = parseSetCookie(header ?? '');
|
|
76
|
+
assert.equal(cookies.length, 1);
|
|
77
|
+
assert.equal(cookies[0].name, 'session');
|
|
78
|
+
assert.equal(cookies[0].path, '/');
|
|
79
|
+
assert.isTrue(cookies[0].secure);
|
|
82
80
|
});
|
|
83
81
|
});
|
|
84
82
|
it('does not set a secure cookie for proxied HTTP request', async () => {
|
|
85
|
-
const app = (
|
|
83
|
+
const app = express();
|
|
86
84
|
app.enable('trust proxy');
|
|
87
|
-
app.use(
|
|
85
|
+
app.use(createSessionMiddleware({
|
|
88
86
|
secret: TEST_SECRET,
|
|
89
|
-
store: new
|
|
87
|
+
store: new MemoryStore(),
|
|
90
88
|
cookie: {
|
|
91
89
|
secure: true,
|
|
92
90
|
},
|
|
93
91
|
}));
|
|
94
92
|
app.get('/', (_req, res) => res.sendStatus(200));
|
|
95
|
-
await
|
|
96
|
-
const res = await (
|
|
93
|
+
await withServer(app, async ({ url }) => {
|
|
94
|
+
const res = await fetch(url, {
|
|
97
95
|
headers: {
|
|
98
96
|
'X-Forwarded-Proto': 'http',
|
|
99
97
|
},
|
|
100
98
|
});
|
|
101
|
-
|
|
99
|
+
assert.equal(res.status, 200);
|
|
102
100
|
const header = res.headers.get('set-cookie');
|
|
103
|
-
|
|
101
|
+
assert.isNull(header);
|
|
104
102
|
});
|
|
105
103
|
});
|
|
106
104
|
it('automatically sets secure for proxied HTTPS request', async () => {
|
|
107
|
-
const app = (
|
|
105
|
+
const app = express();
|
|
108
106
|
app.enable('trust proxy');
|
|
109
|
-
app.use(
|
|
107
|
+
app.use(createSessionMiddleware({
|
|
110
108
|
secret: TEST_SECRET,
|
|
111
|
-
store: new
|
|
109
|
+
store: new MemoryStore(),
|
|
112
110
|
cookie: {
|
|
113
111
|
secure: 'auto',
|
|
114
112
|
},
|
|
115
113
|
}));
|
|
116
114
|
app.get('/', (_req, res) => res.sendStatus(200));
|
|
117
|
-
await
|
|
118
|
-
const res = await (
|
|
115
|
+
await withServer(app, async ({ url }) => {
|
|
116
|
+
const res = await fetch(url, {
|
|
119
117
|
headers: {
|
|
120
118
|
'X-Forwarded-Proto': 'https',
|
|
121
119
|
},
|
|
122
120
|
});
|
|
123
|
-
|
|
121
|
+
assert.equal(res.status, 200);
|
|
124
122
|
const header = res.headers.get('set-cookie');
|
|
125
|
-
const cookies = (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
123
|
+
const cookies = parseSetCookie(header ?? '');
|
|
124
|
+
assert.equal(cookies.length, 1);
|
|
125
|
+
assert.equal(cookies[0].name, 'session');
|
|
126
|
+
assert.equal(cookies[0].path, '/');
|
|
127
|
+
assert.isTrue(cookies[0].secure);
|
|
130
128
|
});
|
|
131
129
|
});
|
|
132
130
|
it('automatically sets secure for proxied HTTP request', async () => {
|
|
133
|
-
const app = (
|
|
131
|
+
const app = express();
|
|
134
132
|
app.enable('trust proxy');
|
|
135
|
-
app.use(
|
|
133
|
+
app.use(createSessionMiddleware({
|
|
136
134
|
secret: TEST_SECRET,
|
|
137
|
-
store: new
|
|
135
|
+
store: new MemoryStore(),
|
|
138
136
|
cookie: {
|
|
139
137
|
secure: 'auto',
|
|
140
138
|
},
|
|
141
139
|
}));
|
|
142
140
|
app.get('/', (_req, res) => res.sendStatus(200));
|
|
143
|
-
await
|
|
144
|
-
const res = await (
|
|
141
|
+
await withServer(app, async ({ url }) => {
|
|
142
|
+
const res = await fetch(url, {
|
|
145
143
|
headers: {
|
|
146
144
|
'X-Forwarded-Proto': 'http',
|
|
147
145
|
},
|
|
148
146
|
});
|
|
149
|
-
|
|
147
|
+
assert.equal(res.status, 200);
|
|
150
148
|
const header = res.headers.get('set-cookie');
|
|
151
|
-
const cookies = (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
149
|
+
const cookies = parseSetCookie(header ?? '');
|
|
150
|
+
assert.equal(cookies.length, 1);
|
|
151
|
+
assert.equal(cookies[0].name, 'session');
|
|
152
|
+
assert.equal(cookies[0].path, '/');
|
|
153
|
+
assert.isUndefined(cookies[0].secure);
|
|
156
154
|
});
|
|
157
155
|
});
|
|
158
156
|
it('sets secure cookie based on a function', async () => {
|
|
159
|
-
const app = (
|
|
157
|
+
const app = express();
|
|
160
158
|
app.enable('trust proxy');
|
|
161
|
-
app.use(
|
|
159
|
+
app.use(createSessionMiddleware({
|
|
162
160
|
secret: TEST_SECRET,
|
|
163
|
-
store: new
|
|
161
|
+
store: new MemoryStore(),
|
|
164
162
|
cookie: {
|
|
165
163
|
secure: (req) => req.hostname === 'example.com',
|
|
166
164
|
},
|
|
167
165
|
}));
|
|
168
166
|
app.get('/', (_req, res) => res.sendStatus(200));
|
|
169
|
-
await
|
|
170
|
-
const insecureRes = await (
|
|
167
|
+
await withServer(app, async ({ url }) => {
|
|
168
|
+
const insecureRes = await fetch(url, {
|
|
171
169
|
headers: {
|
|
172
170
|
'X-Forwarded-Host': 'subdomain.example.com',
|
|
173
171
|
'X-Forwarded-Proto': 'http',
|
|
174
172
|
},
|
|
175
173
|
});
|
|
176
|
-
|
|
174
|
+
assert.equal(insecureRes.status, 200);
|
|
177
175
|
const insecureHeader = insecureRes.headers.get('set-cookie');
|
|
178
|
-
const insecureCookie = (
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const secureRes = await (
|
|
176
|
+
const insecureCookie = parseSetCookie(insecureHeader ?? '');
|
|
177
|
+
assert.equal(insecureCookie.length, 1);
|
|
178
|
+
assert.equal(insecureCookie[0].name, 'session');
|
|
179
|
+
assert.equal(insecureCookie[0].path, '/');
|
|
180
|
+
assert.isUndefined(insecureCookie[0].secure);
|
|
181
|
+
const secureRes = await fetch(url, {
|
|
184
182
|
headers: {
|
|
185
183
|
'X-Forwarded-Host': 'example.com',
|
|
186
184
|
'X-Forwarded-Proto': 'https',
|
|
187
185
|
},
|
|
188
186
|
});
|
|
189
|
-
|
|
187
|
+
assert.equal(secureRes.status, 200);
|
|
190
188
|
const secureHeader = secureRes.headers.get('set-cookie');
|
|
191
|
-
const secureCookies = (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
189
|
+
const secureCookies = parseSetCookie(secureHeader ?? '');
|
|
190
|
+
assert.equal(secureCookies.length, 1);
|
|
191
|
+
assert.equal(secureCookies[0].name, 'session');
|
|
192
|
+
assert.equal(secureCookies[0].path, '/');
|
|
193
|
+
assert.isTrue(secureCookies[0].secure);
|
|
196
194
|
});
|
|
197
195
|
});
|
|
198
196
|
it('persists session data across requests', async () => {
|
|
199
|
-
const app = (
|
|
200
|
-
app.use(
|
|
201
|
-
store: new
|
|
197
|
+
const app = express();
|
|
198
|
+
app.use(createSessionMiddleware({
|
|
199
|
+
store: new MemoryStore(),
|
|
202
200
|
secret: TEST_SECRET,
|
|
203
201
|
}));
|
|
204
202
|
app.get('/', (req, res) => {
|
|
@@ -206,20 +204,20 @@ describe('session middleware', () => {
|
|
|
206
204
|
req.session.count += 1;
|
|
207
205
|
res.send(req.session.count.toString());
|
|
208
206
|
});
|
|
209
|
-
await
|
|
210
|
-
const fetchWithCookies = (
|
|
207
|
+
await withServer(app, async ({ url }) => {
|
|
208
|
+
const fetchWithCookies = fetchCookie(fetch);
|
|
211
209
|
let res = await fetchWithCookies(url);
|
|
212
|
-
|
|
213
|
-
|
|
210
|
+
assert.equal(res.status, 200);
|
|
211
|
+
assert.equal(await res.text(), '1');
|
|
214
212
|
res = await fetchWithCookies(url);
|
|
215
|
-
|
|
216
|
-
|
|
213
|
+
assert.equal(res.status, 200);
|
|
214
|
+
assert.equal(await res.text(), '2');
|
|
217
215
|
});
|
|
218
216
|
});
|
|
219
217
|
it('commits the session before sending a redirect', async () => {
|
|
220
|
-
const app = (
|
|
221
|
-
app.use(
|
|
222
|
-
store: new
|
|
218
|
+
const app = express();
|
|
219
|
+
app.use(createSessionMiddleware({
|
|
220
|
+
store: new MemoryStore(),
|
|
223
221
|
secret: TEST_SECRET,
|
|
224
222
|
}));
|
|
225
223
|
app.post('/', (req, res) => {
|
|
@@ -229,104 +227,104 @@ describe('session middleware', () => {
|
|
|
229
227
|
app.get('/', (req, res) => {
|
|
230
228
|
res.send(req.session.test ?? 'NO VALUE');
|
|
231
229
|
});
|
|
232
|
-
await
|
|
233
|
-
const res = await (
|
|
230
|
+
await withServer(app, async ({ url }) => {
|
|
231
|
+
const res = await fetchCookie(fetch)(url, {
|
|
234
232
|
method: 'POST',
|
|
235
233
|
});
|
|
236
|
-
|
|
234
|
+
assert.equal(res.status, 200);
|
|
237
235
|
const body = await res.text();
|
|
238
|
-
|
|
236
|
+
assert.equal(body, 'test');
|
|
239
237
|
});
|
|
240
238
|
});
|
|
241
239
|
it('destroys session', async () => {
|
|
242
|
-
const store = new
|
|
243
|
-
const app = (
|
|
244
|
-
app.use(
|
|
240
|
+
const store = new MemoryStore();
|
|
241
|
+
const app = express();
|
|
242
|
+
app.use(createSessionMiddleware({
|
|
245
243
|
store,
|
|
246
244
|
secret: TEST_SECRET,
|
|
247
245
|
}));
|
|
248
246
|
app.get('/', (_req, res) => res.sendStatus(200));
|
|
249
|
-
app.use('/destroy', (
|
|
247
|
+
app.use('/destroy', asyncHandler(async (req, res) => {
|
|
250
248
|
await req.session.destroy();
|
|
251
249
|
res.sendStatus(200);
|
|
252
250
|
}));
|
|
253
|
-
await
|
|
254
|
-
const fetchWithCookies = (
|
|
251
|
+
await withServer(app, async ({ url }) => {
|
|
252
|
+
const fetchWithCookies = fetchCookie(fetch);
|
|
255
253
|
// Generate a new session.
|
|
256
254
|
await fetchWithCookies(url);
|
|
257
255
|
// Destroy the session.
|
|
258
256
|
const destroyRes = await fetchWithCookies(`${url}/destroy`);
|
|
259
|
-
|
|
257
|
+
assert.equal(destroyRes.status, 200);
|
|
260
258
|
// Ensure the session cookie was cleared in the response.
|
|
261
259
|
const header = destroyRes.headers.get('set-cookie');
|
|
262
|
-
const cookies = (
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
260
|
+
const cookies = parseSetCookie(header ?? '');
|
|
261
|
+
assert.equal(cookies.length, 1);
|
|
262
|
+
assert.equal(cookies[0].name, 'session');
|
|
263
|
+
assert.equal(cookies[0].path, '/');
|
|
264
|
+
assert.equal(cookies[0].expires?.getTime(), 0);
|
|
267
265
|
// Ensure the session was destroyed in the session store.
|
|
268
266
|
const sessionId = cookies[0].value.split('.')[0];
|
|
269
|
-
|
|
267
|
+
assert.isNull(await store.get(sessionId));
|
|
270
268
|
});
|
|
271
269
|
});
|
|
272
270
|
it('regenerates session', async () => {
|
|
273
|
-
const store = new
|
|
274
|
-
const app = (
|
|
275
|
-
app.use(
|
|
271
|
+
const store = new MemoryStore();
|
|
272
|
+
const app = express();
|
|
273
|
+
app.use(createSessionMiddleware({
|
|
276
274
|
store,
|
|
277
275
|
secret: TEST_SECRET,
|
|
278
276
|
}));
|
|
279
277
|
app.get('/', (req, res) => {
|
|
280
278
|
res.send(req.session.regenerated ? 'true' : 'false');
|
|
281
279
|
});
|
|
282
|
-
app.get('/regenerate', (
|
|
280
|
+
app.get('/regenerate', asyncHandler(async (req, res) => {
|
|
283
281
|
await req.session.regenerate();
|
|
284
282
|
req.session.regenerated = true;
|
|
285
283
|
res.sendStatus(200);
|
|
286
284
|
}));
|
|
287
|
-
await
|
|
288
|
-
const fetchWithCookies = (
|
|
285
|
+
await withServer(app, async ({ url }) => {
|
|
286
|
+
const fetchWithCookies = fetchCookie(fetch);
|
|
289
287
|
// Generate a new session.
|
|
290
288
|
let res = await fetchWithCookies(url);
|
|
291
|
-
|
|
292
|
-
|
|
289
|
+
assert.equal(res.status, 200);
|
|
290
|
+
assert.equal(await res.text(), 'false');
|
|
293
291
|
// Extract the original cookie value.
|
|
294
292
|
let header = res.headers.get('set-cookie');
|
|
295
|
-
let cookies = (
|
|
296
|
-
|
|
293
|
+
let cookies = parseSetCookie(header ?? '');
|
|
294
|
+
assert.equal(cookies.length, 1);
|
|
297
295
|
const originalCookieValue = cookies[0].value;
|
|
298
296
|
// Regenerate the session.
|
|
299
297
|
res = await fetchWithCookies(`${url}/regenerate`);
|
|
300
|
-
|
|
298
|
+
assert.equal(res.status, 200);
|
|
301
299
|
// Ensure that the session cookie was changed.
|
|
302
300
|
header = res.headers.get('set-cookie');
|
|
303
|
-
cookies = (
|
|
304
|
-
|
|
301
|
+
cookies = parseSetCookie(header ?? '');
|
|
302
|
+
assert.equal(cookies.length, 1);
|
|
305
303
|
const newCookieValue = cookies[0].value;
|
|
306
|
-
|
|
304
|
+
assert.notEqual(newCookieValue, originalCookieValue);
|
|
307
305
|
// Ensure the original session is no longer present in the session store.
|
|
308
306
|
const originalSessionId = originalCookieValue.split('.')[0];
|
|
309
|
-
|
|
307
|
+
assert.isNull(await store.get(originalSessionId));
|
|
310
308
|
// Ensure that the regenerated session data was persisted.
|
|
311
309
|
res = await fetchWithCookies(url);
|
|
312
|
-
|
|
313
|
-
|
|
310
|
+
assert.equal(res.status, 200);
|
|
311
|
+
assert.equal(await res.text(), 'true');
|
|
314
312
|
});
|
|
315
313
|
});
|
|
316
314
|
it('creates a new session if signature checks fail', async () => {
|
|
317
|
-
const store = new
|
|
318
|
-
const app = (
|
|
319
|
-
app.use(
|
|
315
|
+
const store = new MemoryStore();
|
|
316
|
+
const app = express();
|
|
317
|
+
app.use(createSessionMiddleware({
|
|
320
318
|
store,
|
|
321
319
|
secret: TEST_SECRET,
|
|
322
320
|
}));
|
|
323
321
|
app.get('/', (req, res) => res.send(req.session.id));
|
|
324
|
-
await
|
|
325
|
-
const cookieJar = new
|
|
326
|
-
const fetchWithCookies = (
|
|
322
|
+
await withServer(app, async ({ url }) => {
|
|
323
|
+
const cookieJar = new fetchCookie.toughCookie.CookieJar();
|
|
324
|
+
const fetchWithCookies = fetchCookie(fetch, cookieJar);
|
|
327
325
|
// Generate a new session.
|
|
328
326
|
let res = await fetchWithCookies(url);
|
|
329
|
-
|
|
327
|
+
assert.equal(res.status, 200);
|
|
330
328
|
const originalSessionId = await res.text();
|
|
331
329
|
// Tamper with the session cookie.
|
|
332
330
|
const cookie = cookieJar.getCookiesSync(url)[0];
|
|
@@ -334,39 +332,39 @@ describe('session middleware', () => {
|
|
|
334
332
|
cookieJar.setCookieSync(cookie, url);
|
|
335
333
|
// Make sure we get a new session.
|
|
336
334
|
res = await fetchWithCookies(url);
|
|
337
|
-
|
|
335
|
+
assert.equal(res.status, 200);
|
|
338
336
|
const newSessionId = await res.text();
|
|
339
|
-
|
|
337
|
+
assert.notEqual(newSessionId, originalSessionId);
|
|
340
338
|
// Make sure the existing session is still present in the store. We don't
|
|
341
339
|
// want someone to be able to evict other sessions by submitting invalid
|
|
342
340
|
// cookies.
|
|
343
|
-
|
|
341
|
+
assert.isNotNull(await store.get(originalSessionId));
|
|
344
342
|
});
|
|
345
343
|
});
|
|
346
344
|
it('does not re-set the cookie on subsequent requests', async () => {
|
|
347
|
-
const app = (
|
|
348
|
-
app.use(
|
|
349
|
-
store: new
|
|
345
|
+
const app = express();
|
|
346
|
+
app.use(createSessionMiddleware({
|
|
347
|
+
store: new MemoryStore(),
|
|
350
348
|
secret: TEST_SECRET,
|
|
351
349
|
}));
|
|
352
350
|
app.get('/', (_req, res) => res.sendStatus(200));
|
|
353
|
-
await
|
|
354
|
-
const fetchWithCookies = (
|
|
351
|
+
await withServer(app, async ({ url }) => {
|
|
352
|
+
const fetchWithCookies = fetchCookie(fetch);
|
|
355
353
|
// Generate a new session.
|
|
356
354
|
let res = await fetchWithCookies(url);
|
|
357
|
-
|
|
355
|
+
assert.equal(res.status, 200);
|
|
358
356
|
// Make another request with the same session.
|
|
359
357
|
res = await fetchWithCookies(url);
|
|
360
|
-
|
|
358
|
+
assert.equal(res.status, 200);
|
|
361
359
|
// Ensure that the cookie wasn't set again.
|
|
362
360
|
const header = res.headers.get('set-cookie');
|
|
363
|
-
|
|
361
|
+
assert.isNull(header);
|
|
364
362
|
});
|
|
365
363
|
});
|
|
366
364
|
it('extends the expiration date of the cookie', async () => {
|
|
367
|
-
const store = new
|
|
368
|
-
const app = (
|
|
369
|
-
app.use(
|
|
365
|
+
const store = new MemoryStore();
|
|
366
|
+
const app = express();
|
|
367
|
+
app.use(createSessionMiddleware({
|
|
370
368
|
store,
|
|
371
369
|
secret: TEST_SECRET,
|
|
372
370
|
cookie: {
|
|
@@ -378,79 +376,79 @@ describe('session middleware', () => {
|
|
|
378
376
|
req.session.setExpiration(Date.now() + 10000);
|
|
379
377
|
res.sendStatus(200);
|
|
380
378
|
});
|
|
381
|
-
await
|
|
382
|
-
const fetchWithCookies = (
|
|
379
|
+
await withServer(app, async ({ url }) => {
|
|
380
|
+
const fetchWithCookies = fetchCookie(fetch);
|
|
383
381
|
// Generate a new session.
|
|
384
382
|
let res = await fetchWithCookies(url);
|
|
385
|
-
|
|
383
|
+
assert.equal(res.status, 200);
|
|
386
384
|
// Grab the original expiration date.
|
|
387
385
|
let header = res.headers.get('set-cookie');
|
|
388
|
-
let cookies = (
|
|
389
|
-
|
|
390
|
-
|
|
386
|
+
let cookies = parseSetCookie(header ?? '');
|
|
387
|
+
assert.equal(cookies.length, 1);
|
|
388
|
+
assert.isUndefined(cookies[0].maxAge);
|
|
391
389
|
const originalExpirationDate = cookies[0].expires;
|
|
392
|
-
|
|
390
|
+
assert(originalExpirationDate);
|
|
393
391
|
// Also grab the expiration date from the store.
|
|
394
392
|
const sessionId = cookies[0].value.split('.')[0];
|
|
395
393
|
const session = await store.get(sessionId);
|
|
396
|
-
|
|
394
|
+
assert(session);
|
|
397
395
|
const originalStoreExpirationDate = session.expiresAt;
|
|
398
396
|
// Ensure that the expiration dates are consistent.
|
|
399
|
-
|
|
397
|
+
assert.equal(originalExpirationDate.getTime(), originalStoreExpirationDate.getTime());
|
|
400
398
|
// Make another request with the same session.
|
|
401
399
|
res = await fetchWithCookies(`${url}/extend`);
|
|
402
|
-
|
|
400
|
+
assert.equal(res.status, 200);
|
|
403
401
|
// Ensure that the cookie was set again.
|
|
404
402
|
header = res.headers.get('set-cookie');
|
|
405
|
-
cookies = (
|
|
406
|
-
|
|
407
|
-
|
|
403
|
+
cookies = parseSetCookie(header ?? '');
|
|
404
|
+
assert.equal(cookies.length, 1);
|
|
405
|
+
assert.isUndefined(cookies[0].maxAge);
|
|
408
406
|
const newExpirationDate = cookies[0].expires;
|
|
409
|
-
|
|
407
|
+
assert(newExpirationDate);
|
|
410
408
|
// Ensure that the expiration date was extended.
|
|
411
|
-
|
|
409
|
+
assert.notEqual(newExpirationDate.getTime(), originalExpirationDate.getTime());
|
|
412
410
|
// Also grab the new expiration date from the store.
|
|
413
411
|
const newSession = await store.get(sessionId);
|
|
414
|
-
|
|
412
|
+
assert(newSession);
|
|
415
413
|
const newStoreExpirationDate = newSession.expiresAt;
|
|
416
414
|
// Ensure that the expiration dates are consistent.
|
|
417
|
-
|
|
415
|
+
assert.equal(newExpirationDate.getTime(), newStoreExpirationDate.getTime());
|
|
418
416
|
});
|
|
419
417
|
});
|
|
420
418
|
it('does not persist session data if the session did not change', async () => {
|
|
421
|
-
const store = new
|
|
419
|
+
const store = new MemoryStore();
|
|
422
420
|
let setCount = 0;
|
|
423
421
|
const originalStoreSet = store.set.bind(store);
|
|
424
422
|
store.set = async (...args) => {
|
|
425
423
|
setCount += 1;
|
|
426
424
|
await originalStoreSet(...args);
|
|
427
425
|
};
|
|
428
|
-
const app = (
|
|
429
|
-
app.use(
|
|
426
|
+
const app = express();
|
|
427
|
+
app.use(createSessionMiddleware({
|
|
430
428
|
store,
|
|
431
429
|
secret: TEST_SECRET,
|
|
432
430
|
}));
|
|
433
431
|
app.get('/', (_req, res) => res.sendStatus(200));
|
|
434
|
-
await
|
|
435
|
-
const fetchWithCookies = (
|
|
432
|
+
await withServer(app, async ({ url }) => {
|
|
433
|
+
const fetchWithCookies = fetchCookie(fetch);
|
|
436
434
|
// Generate a new session.
|
|
437
435
|
let res = await fetchWithCookies(url);
|
|
438
|
-
|
|
436
|
+
assert.equal(res.status, 200);
|
|
439
437
|
// Ensure the session was persisted.
|
|
440
|
-
|
|
438
|
+
assert.equal(setCount, 1);
|
|
441
439
|
// Make another request with the same session.
|
|
442
440
|
res = await fetchWithCookies(url);
|
|
443
|
-
|
|
441
|
+
assert.equal(res.status, 200);
|
|
444
442
|
// Ensure the session was not persisted.
|
|
445
|
-
|
|
443
|
+
assert.equal(setCount, 1);
|
|
446
444
|
});
|
|
447
445
|
});
|
|
448
446
|
it('rotates to a new cookie when needed', async () => {
|
|
449
|
-
const fetchWithCookies = (
|
|
450
|
-
const store = new
|
|
447
|
+
const fetchWithCookies = fetchCookie(fetch);
|
|
448
|
+
const store = new MemoryStore();
|
|
451
449
|
// Will create "legacy" sessions.
|
|
452
|
-
const legacyApp = (
|
|
453
|
-
legacyApp.use(
|
|
450
|
+
const legacyApp = express();
|
|
451
|
+
legacyApp.use(createSessionMiddleware({
|
|
454
452
|
store,
|
|
455
453
|
secret: TEST_SECRET,
|
|
456
454
|
cookie: {
|
|
@@ -459,51 +457,56 @@ describe('session middleware', () => {
|
|
|
459
457
|
}));
|
|
460
458
|
legacyApp.get('/', (req, res) => res.send(req.session.id.toString()));
|
|
461
459
|
// Will create "new" sessions and upgrade "legacy" sessions.
|
|
462
|
-
const app = (
|
|
463
|
-
app.use(
|
|
460
|
+
const app = express();
|
|
461
|
+
app.use(createSessionMiddleware({
|
|
464
462
|
store,
|
|
465
463
|
secret: TEST_SECRET,
|
|
466
464
|
cookie: {
|
|
467
|
-
name:
|
|
465
|
+
name: 'legacy_session',
|
|
466
|
+
writeNames: ['legacy_session', 'session'],
|
|
467
|
+
writeOverrides: [{ domain: undefined }, { domain: '.example.com' }],
|
|
468
468
|
},
|
|
469
469
|
}));
|
|
470
470
|
app.get('/', (req, res) => res.send(req.session.id.toString()));
|
|
471
471
|
let legacySessionId = null;
|
|
472
|
-
await
|
|
472
|
+
await withServer(legacyApp, async ({ url }) => {
|
|
473
473
|
// Generate a legacy session.
|
|
474
474
|
const res = await fetchWithCookies(url);
|
|
475
|
-
|
|
475
|
+
assert.equal(res.status, 200);
|
|
476
476
|
legacySessionId = await res.text();
|
|
477
477
|
});
|
|
478
|
-
await
|
|
478
|
+
await withServer(app, async ({ url }) => {
|
|
479
479
|
const res = await fetchWithCookies(url);
|
|
480
|
-
|
|
480
|
+
assert.equal(res.status, 200);
|
|
481
481
|
const newSessionId = await res.text();
|
|
482
482
|
// Ensure that the new session cookie was set.
|
|
483
483
|
const header = res.headers.get('set-cookie');
|
|
484
|
-
|
|
485
|
-
const cookies = (
|
|
486
|
-
|
|
487
|
-
|
|
484
|
+
assert.isNotNull(header);
|
|
485
|
+
const cookies = parseSetCookie(header ?? '');
|
|
486
|
+
assert.equal(cookies.length, 2);
|
|
487
|
+
assert.equal(cookies[0].name, 'legacy_session');
|
|
488
|
+
assert.isUndefined(cookies[0].domain);
|
|
489
|
+
assert.equal(cookies[1].name, 'session');
|
|
490
|
+
assert.equal(cookies[1].domain, '.example.com');
|
|
488
491
|
// Ensure that the legacy session is migrated to a new session.
|
|
489
|
-
|
|
492
|
+
assert.equal(newSessionId, legacySessionId);
|
|
490
493
|
});
|
|
491
494
|
});
|
|
492
495
|
it('persists the session immediately after creation', async () => {
|
|
493
|
-
const store = new
|
|
494
|
-
const app = (
|
|
495
|
-
app.use(
|
|
496
|
+
const store = new MemoryStore();
|
|
497
|
+
const app = express();
|
|
498
|
+
app.use(createSessionMiddleware({
|
|
496
499
|
store,
|
|
497
500
|
secret: TEST_SECRET,
|
|
498
501
|
}));
|
|
499
|
-
app.get('/', (
|
|
502
|
+
app.get('/', asyncHandler(async (req, res) => {
|
|
500
503
|
const persistedSession = await store.get(req.session.id);
|
|
501
504
|
res.status(persistedSession == null ? 500 : 200).send();
|
|
502
505
|
}));
|
|
503
|
-
await
|
|
504
|
-
const fetchWithCookies = (
|
|
506
|
+
await withServer(app, async ({ url }) => {
|
|
507
|
+
const fetchWithCookies = fetchCookie(fetch);
|
|
505
508
|
const res = await fetchWithCookies(url);
|
|
506
|
-
|
|
509
|
+
assert.equal(res.status, 200);
|
|
507
510
|
});
|
|
508
511
|
});
|
|
509
512
|
});
|