@prairielearn/session 1.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.
Files changed (44) hide show
  1. package/.turbo/turbo-build.log +0 -0
  2. package/README.md +39 -0
  3. package/dist/before-end.d.ts +25 -0
  4. package/dist/before-end.js +81 -0
  5. package/dist/before-end.js.map +1 -0
  6. package/dist/before-end.test.d.ts +1 -0
  7. package/dist/before-end.test.js +33 -0
  8. package/dist/before-end.test.js.map +1 -0
  9. package/dist/cookie.d.ts +4 -0
  10. package/dist/cookie.js +34 -0
  11. package/dist/cookie.js.map +1 -0
  12. package/dist/index.d.ts +24 -0
  13. package/dist/index.js +89 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/index.test.d.ts +1 -0
  16. package/dist/index.test.js +506 -0
  17. package/dist/index.test.js.map +1 -0
  18. package/dist/memory-store.d.ts +7 -0
  19. package/dist/memory-store.js +28 -0
  20. package/dist/memory-store.js.map +1 -0
  21. package/dist/session.d.ts +14 -0
  22. package/dist/session.js +78 -0
  23. package/dist/session.js.map +1 -0
  24. package/dist/session.test.d.ts +1 -0
  25. package/dist/session.test.js +92 -0
  26. package/dist/session.test.js.map +1 -0
  27. package/dist/store.d.ts +9 -0
  28. package/dist/store.js +3 -0
  29. package/dist/store.js.map +1 -0
  30. package/dist/test-utils.d.ts +10 -0
  31. package/dist/test-utils.js +32 -0
  32. package/dist/test-utils.js.map +1 -0
  33. package/package.json +44 -0
  34. package/src/before-end.test.ts +34 -0
  35. package/src/before-end.ts +96 -0
  36. package/src/cookie.ts +38 -0
  37. package/src/index.test.ts +628 -0
  38. package/src/index.ts +132 -0
  39. package/src/memory-store.ts +25 -0
  40. package/src/session.test.ts +122 -0
  41. package/src/session.ts +106 -0
  42. package/src/store.ts +10 -0
  43. package/src/test-utils.ts +42 -0
  44. package/tsconfig.json +11 -0
File without changes
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # `@prairielearn/session`
2
+
3
+ The implementation borrows heavily from prior art such as [`express-session`](https://github.com/expressjs/session) and [`fastify-session`](https://github.com/fastify/session). However, the semantics and functionality have been changed to better suit PrairieLearn's needs. Specifically:
4
+
5
+ - We need to have more precise control over when the session is written back to the session store. `express-session` will try to write the session on every request, which produces an undesirable amount of load on the database.
6
+ - We need to have more precise control over when new/updated cookies are sent back to the client. In the near future, we'll need to avoid writing these cookies when requests are served from subdomains.
7
+
8
+ ## Usage
9
+
10
+ ```ts
11
+ import express from 'express';
12
+ import { createSessionMiddleware, MemoryStore } from '@prairielearn/session';
13
+
14
+ const app = express();
15
+
16
+ app.use(
17
+ createSessionMiddleware({
18
+ store: new MemoryStore(),
19
+ secret: 'top_secret',
20
+ }),
21
+ );
22
+ ```
23
+
24
+ ### Controlling when cookies are set
25
+
26
+ You can pass a `canSetCookie` function to `createSessionMiddleware` to provide control over when session cookies will be returned to the client.
27
+
28
+ ```ts
29
+ createSessionMiddleware({
30
+ canSetCookie: (req) => {
31
+ return req.hostname === 'us.prairielearn.com';
32
+ },
33
+ });
34
+ ```
35
+
36
+ This can be useful to enforce that a session cookie is only ever set from a root domain and not subdomains.
37
+
38
+ - If a request is received that does not have a valid session cookie, a temporary session will be created at `req.session`, but it won't be persisted since the client won't know about the session ID.
39
+ - If a request is received that already has a valid session cookie, modifications to the session will be persisted, but the cookie itself won't be updated.
@@ -0,0 +1,25 @@
1
+ import type { NextFunction } from 'express';
2
+ /**
3
+ * The following function is based on code from `express-session`:
4
+ *
5
+ * https://github.com/expressjs/session/blob/1010fadc2f071ddf2add94235d72224cf65159c6/index.js#L246-L360
6
+ *
7
+ * This code is used to work around the fact that Express doesn't have a good
8
+ * hook to allow us to perform some asynchronous operation before the response
9
+ * is written to the client.
10
+ *
11
+ * Note that this is truly only necessary for Express. Other Node frameworks
12
+ * like Fastify and Adonis have hooks that allow us to do this without any
13
+ * hacks. It's also probably only useful in the context of Express, as it
14
+ * seems to rely on the fact that Express and its ecosystem generally don't
15
+ * call `end()` without an additional chunk of data. If it instead called
16
+ * `write()` with the final data and then `end()` with no data, this code
17
+ * wouldn't function as intended. It's possible that `stream.pipe(res)` does
18
+ * in fact behave this way, so it's probably not completely safe to use this
19
+ * code when streaming responses back to the client.
20
+ *
21
+ * One could probably make this safer by *also* hooking into `response.write()`
22
+ * and buffering the data. My understanding of Node streams isn't good enough
23
+ * to implement that, though.
24
+ */
25
+ export declare function beforeEnd(res: any, next: NextFunction, fn: () => Promise<void>): void;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.beforeEnd = void 0;
4
+ /**
5
+ * The following function is based on code from `express-session`:
6
+ *
7
+ * https://github.com/expressjs/session/blob/1010fadc2f071ddf2add94235d72224cf65159c6/index.js#L246-L360
8
+ *
9
+ * This code is used to work around the fact that Express doesn't have a good
10
+ * hook to allow us to perform some asynchronous operation before the response
11
+ * is written to the client.
12
+ *
13
+ * Note that this is truly only necessary for Express. Other Node frameworks
14
+ * like Fastify and Adonis have hooks that allow us to do this without any
15
+ * hacks. It's also probably only useful in the context of Express, as it
16
+ * seems to rely on the fact that Express and its ecosystem generally don't
17
+ * call `end()` without an additional chunk of data. If it instead called
18
+ * `write()` with the final data and then `end()` with no data, this code
19
+ * wouldn't function as intended. It's possible that `stream.pipe(res)` does
20
+ * in fact behave this way, so it's probably not completely safe to use this
21
+ * code when streaming responses back to the client.
22
+ *
23
+ * One could probably make this safer by *also* hooking into `response.write()`
24
+ * and buffering the data. My understanding of Node streams isn't good enough
25
+ * to implement that, though.
26
+ */
27
+ function beforeEnd(res, next, fn) {
28
+ const _end = res.end;
29
+ const _write = res.write;
30
+ let ended = false;
31
+ res.end = function end(chunk, encoding) {
32
+ if (ended) {
33
+ return false;
34
+ }
35
+ ended = true;
36
+ let ret;
37
+ let sync = true;
38
+ function writeend() {
39
+ if (sync) {
40
+ ret = _end.call(res, chunk, encoding);
41
+ sync = false;
42
+ return;
43
+ }
44
+ _end.call(res);
45
+ }
46
+ function writetop() {
47
+ if (!sync) {
48
+ return ret;
49
+ }
50
+ if (!res._header) {
51
+ res._implicitHeader();
52
+ }
53
+ if (chunk == null) {
54
+ ret = true;
55
+ return ret;
56
+ }
57
+ const contentLength = Number(res.getHeader('Content-Length'));
58
+ if (!isNaN(contentLength) && contentLength > 0) {
59
+ chunk = !Buffer.isBuffer(chunk) ? Buffer.from(chunk, encoding) : chunk;
60
+ encoding = undefined;
61
+ if (chunk.length !== 0) {
62
+ ret = _write.call(res, chunk.slice(0, chunk.length - 1));
63
+ chunk = chunk.slice(chunk.length - 1, chunk.length);
64
+ return ret;
65
+ }
66
+ }
67
+ ret = _write.call(res, chunk, encoding);
68
+ sync = false;
69
+ return ret;
70
+ }
71
+ fn().then(() => {
72
+ writeend();
73
+ }, (err) => {
74
+ setImmediate(next, err);
75
+ writeend();
76
+ });
77
+ return writetop();
78
+ };
79
+ }
80
+ exports.beforeEnd = beforeEnd;
81
+ //# sourceMappingURL=before-end.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-end.js","sourceRoot":"","sources":["../src/before-end.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,SAAgB,SAAS,CAAC,GAAQ,EAAE,IAAkB,EAAE,EAAuB;IAC7E,MAAM,IAAI,GAAG,GAAG,CAAC,GAAU,CAAC;IAC5B,MAAM,MAAM,GAAG,GAAG,CAAC,KAAY,CAAC;IAChC,IAAI,KAAK,GAAG,KAAK,CAAC;IAElB,GAAG,CAAC,GAAG,GAAG,SAAS,GAAG,CAAC,KAAU,EAAE,QAAa;QAC9C,IAAI,KAAK,EAAE;YACT,OAAO,KAAK,CAAC;SACd;QAED,KAAK,GAAG,IAAI,CAAC;QAEb,IAAI,GAAQ,CAAC;QACb,IAAI,IAAI,GAAG,IAAI,CAAC;QAEhB,SAAS,QAAQ;YACf,IAAI,IAAI,EAAE;gBACR,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;gBACtC,IAAI,GAAG,KAAK,CAAC;gBACb,OAAO;aACR;YAED,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC;QAED,SAAS,QAAQ;YACf,IAAI,CAAC,IAAI,EAAE;gBACT,OAAO,GAAG,CAAC;aACZ;YAED,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE;gBAChB,GAAG,CAAC,eAAe,EAAE,CAAC;aACvB;YAED,IAAI,KAAK,IAAI,IAAI,EAAE;gBACjB,GAAG,GAAG,IAAI,CAAC;gBACX,OAAO,GAAG,CAAC;aACZ;YAED,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC;YAE9D,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,aAAa,GAAG,CAAC,EAAE;gBAC9C,KAAK,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;gBACvE,QAAQ,GAAG,SAAS,CAAC;gBAErB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;oBACtB,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;oBACzD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;oBACpD,OAAO,GAAG,CAAC;iBACZ;aACF;YAED,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;YACxC,IAAI,GAAG,KAAK,CAAC;YAEb,OAAO,GAAG,CAAC;QACb,CAAC;QAED,EAAE,EAAE,CAAC,IAAI,CACP,GAAG,EAAE;YACH,QAAQ,EAAE,CAAC;QACb,CAAC,EACD,CAAC,GAAG,EAAE,EAAE;YACN,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACxB,QAAQ,EAAE,CAAC;QACb,CAAC,CACF,CAAC;QAEF,OAAO,QAAQ,EAAE,CAAC;IACpB,CAAC,CAAC;AACJ,CAAC;AAtED,8BAsEC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const express_1 = __importDefault(require("express"));
7
+ const node_fetch_1 = __importDefault(require("node-fetch"));
8
+ const chai_1 = require("chai");
9
+ const before_end_1 = require("./before-end");
10
+ const test_utils_1 = require("./test-utils");
11
+ describe('beforeEnd', () => {
12
+ it('handles errors correctly', async () => {
13
+ const app = (0, express_1.default)();
14
+ app.use((_req, res, next) => {
15
+ (0, before_end_1.beforeEnd)(res, next, async () => {
16
+ throw new Error('oops');
17
+ });
18
+ next();
19
+ });
20
+ app.get('/', (_req, res) => res.sendStatus(200));
21
+ let error = null;
22
+ app.use((err, _req, _res, next) => {
23
+ error = err;
24
+ next();
25
+ });
26
+ await (0, test_utils_1.withServer)(app, async ({ url }) => {
27
+ const res = await (0, node_fetch_1.default)(url);
28
+ chai_1.assert.equal(res.status, 200);
29
+ chai_1.assert.equal(error?.message, 'oops');
30
+ });
31
+ });
32
+ });
33
+ //# sourceMappingURL=before-end.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-end.test.js","sourceRoot":"","sources":["../src/before-end.test.ts"],"names":[],"mappings":";;;;;AAAA,sDAAkF;AAClF,4DAA+B;AAC/B,+BAA8B;AAE9B,6CAAyC;AACzC,6CAA0C;AAE1C,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,GAAG,GAAG,IAAA,iBAAO,GAAE,CAAC;QACtB,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YAC1B,IAAA,sBAAS,EAAC,GAAG,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;gBAC9B,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;YAC1B,CAAC,CAAC,CAAC;YAEH,IAAI,EAAE,CAAC;QACT,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;QAEjD,IAAI,KAAK,GAAiB,IAAI,CAAC;QAC/B,GAAG,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,IAAa,EAAE,IAAc,EAAE,IAAkB,EAAE,EAAE;YACtE,KAAK,GAAG,GAAG,CAAC;YACZ,IAAI,EAAE,CAAC;QACT,CAAC,CAAC,CAAC;QAEH,MAAM,IAAA,uBAAU,EAAC,GAAG,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;YACtC,MAAM,GAAG,GAAG,MAAM,IAAA,oBAAK,EAAC,GAAG,CAAC,CAAC;YAE7B,aAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YAC9B,aAAM,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Request } from 'express';
2
+ export type CookieSecure = boolean | 'auto' | ((req: Request) => boolean);
3
+ export declare function shouldSecureCookie(req: Request, secure: CookieSecure): boolean;
4
+ export declare function getSessionIdFromCookie(req: Request, cookieName: string, secrets: string[]): string | null;
package/dist/cookie.js ADDED
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getSessionIdFromCookie = exports.shouldSecureCookie = void 0;
7
+ const cookie_1 = __importDefault(require("cookie"));
8
+ const cookie_signature_1 = __importDefault(require("cookie-signature"));
9
+ function shouldSecureCookie(req, secure) {
10
+ if (typeof secure === 'function') {
11
+ return secure(req);
12
+ }
13
+ if (secure === 'auto') {
14
+ return req.protocol === 'https';
15
+ }
16
+ return secure;
17
+ }
18
+ exports.shouldSecureCookie = shouldSecureCookie;
19
+ function getSessionIdFromCookie(req, cookieName, secrets) {
20
+ const cookies = cookie_1.default.parse(req.headers.cookie ?? '');
21
+ const sessionCookie = cookies[cookieName];
22
+ if (!sessionCookie)
23
+ return null;
24
+ // Try each secret until we find one that works.
25
+ for (const secret of secrets) {
26
+ const value = cookie_signature_1.default.unsign(sessionCookie, secret);
27
+ if (value !== false) {
28
+ return value;
29
+ }
30
+ }
31
+ return null;
32
+ }
33
+ exports.getSessionIdFromCookie = getSessionIdFromCookie;
34
+ //# sourceMappingURL=cookie.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookie.js","sourceRoot":"","sources":["../src/cookie.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAC5B,wEAAyC;AAKzC,SAAgB,kBAAkB,CAAC,GAAY,EAAE,MAAoB;IACnE,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE;QAChC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;KACpB;IAED,IAAI,MAAM,KAAK,MAAM,EAAE;QACrB,OAAO,GAAG,CAAC,QAAQ,KAAK,OAAO,CAAC;KACjC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAVD,gDAUC;AAED,SAAgB,sBAAsB,CACpC,GAAY,EACZ,UAAkB,EAClB,OAAiB;IAEjB,MAAM,OAAO,GAAG,gBAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IACvD,MAAM,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAE1C,IAAI,CAAC,aAAa;QAAE,OAAO,IAAI,CAAC;IAEhC,gDAAgD;IAChD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE;QAC5B,MAAM,KAAK,GAAG,0BAAS,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QACtD,IAAI,KAAK,KAAK,KAAK,EAAE;YACnB,OAAO,KAAK,CAAC;SACd;KACF;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAnBD,wDAmBC"}
@@ -0,0 +1,24 @@
1
+ import type { Request } from 'express';
2
+ import { SessionStore } from './store';
3
+ import { type CookieSecure } from './cookie';
4
+ import { type Session } from './session';
5
+ declare global {
6
+ namespace Express {
7
+ interface Request {
8
+ session: Session;
9
+ }
10
+ }
11
+ }
12
+ export interface SessionOptions {
13
+ secret: string | string[];
14
+ store: SessionStore;
15
+ canSetCookie?: (req: Request) => boolean;
16
+ cookie?: {
17
+ name?: string;
18
+ secure?: CookieSecure;
19
+ httpOnly?: boolean;
20
+ sameSite?: boolean | 'none' | 'lax' | 'strict';
21
+ maxAge?: number;
22
+ };
23
+ }
24
+ export declare function createSessionMiddleware(options: SessionOptions): import("express").RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
package/dist/index.js ADDED
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createSessionMiddleware = void 0;
7
+ const on_headers_1 = __importDefault(require("on-headers"));
8
+ const cookie_signature_1 = __importDefault(require("cookie-signature"));
9
+ const express_async_handler_1 = __importDefault(require("express-async-handler"));
10
+ const before_end_1 = require("./before-end");
11
+ const cookie_1 = require("./cookie");
12
+ const session_1 = require("./session");
13
+ const DEFAULT_COOKIE_NAME = 'session';
14
+ const DEFAULT_COOKIE_MAX_AGE = 86400000; // 1 day
15
+ function createSessionMiddleware(options) {
16
+ const secrets = Array.isArray(options.secret) ? options.secret : [options.secret];
17
+ const cookieName = options.cookie?.name ?? DEFAULT_COOKIE_NAME;
18
+ const cookieMaxAge = options.cookie?.maxAge ?? DEFAULT_COOKIE_MAX_AGE;
19
+ const store = options.store;
20
+ return (0, express_async_handler_1.default)(async function sessionMiddleware(req, res, next) {
21
+ const cookieSessionId = (0, cookie_1.getSessionIdFromCookie)(req, cookieName, secrets);
22
+ const sessionId = cookieSessionId ?? (await (0, session_1.generateSessionId)());
23
+ req.session = await (0, session_1.loadSession)(sessionId, req, store, cookieMaxAge);
24
+ const originalHash = (0, session_1.hashSession)(req.session);
25
+ const originalExpirationDate = req.session.getExpirationDate();
26
+ const canSetCookie = options.canSetCookie?.(req) ?? true;
27
+ (0, on_headers_1.default)(res, () => {
28
+ if (!req.session) {
29
+ if (cookieSessionId) {
30
+ // If the request arrived with a session cookie but the session was
31
+ // destroyed, clear the cookie.
32
+ res.clearCookie(cookieName);
33
+ return;
34
+ }
35
+ // There is no session to do anything with.
36
+ return;
37
+ }
38
+ const secureCookie = (0, cookie_1.shouldSecureCookie)(req, options.cookie?.secure ?? 'auto');
39
+ if (secureCookie && req.protocol !== 'https') {
40
+ // Avoid sending cookie over insecure connection.
41
+ return;
42
+ }
43
+ const isNewSession = !cookieSessionId || cookieSessionId !== req.session.id;
44
+ const didExpirationChange = originalExpirationDate.getTime() !== req.session.getExpirationDate().getTime();
45
+ if (canSetCookie && (isNewSession || didExpirationChange)) {
46
+ const signedSessionId = signSessionId(req.session.id, secrets[0]);
47
+ res.cookie(cookieName, signedSessionId, {
48
+ secure: secureCookie,
49
+ httpOnly: options.cookie?.httpOnly ?? true,
50
+ sameSite: options.cookie?.sameSite ?? false,
51
+ expires: req.session.getExpirationDate(),
52
+ });
53
+ }
54
+ });
55
+ (0, before_end_1.beforeEnd)(res, next, async () => {
56
+ if (!req.session) {
57
+ // There is no session to do anything with.
58
+ return;
59
+ }
60
+ const isExistingSession = cookieSessionId && cookieSessionId === req.session.id;
61
+ const hashChanged = (0, session_1.hashSession)(req.session) !== originalHash;
62
+ const didExpirationChange = originalExpirationDate.getTime() !== req.session.getExpirationDate().getTime();
63
+ if ((hashChanged && isExistingSession) ||
64
+ (canSetCookie && (!isExistingSession || didExpirationChange))) {
65
+ // Only update the expiration date in the store if we were actually
66
+ // able to update the cookie too.
67
+ const expirationDate = canSetCookie
68
+ ? req.session.getExpirationDate()
69
+ : originalExpirationDate;
70
+ await store.set(req.session.id, req.session,
71
+ // Cookies only support second-level resolution. To ensure consistency
72
+ // between the cookie and the store, truncate the expiration date to
73
+ // the nearest second.
74
+ truncateExpirationDate(expirationDate));
75
+ }
76
+ });
77
+ next();
78
+ });
79
+ }
80
+ exports.createSessionMiddleware = createSessionMiddleware;
81
+ function signSessionId(sessionId, secret) {
82
+ return cookie_signature_1.default.sign(sessionId, secret);
83
+ }
84
+ function truncateExpirationDate(date) {
85
+ const time = date.getTime();
86
+ const truncatedTime = Math.floor(time / 1000) * 1000;
87
+ return new Date(truncatedTime);
88
+ }
89
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};