@proteinjs/user-server 1.10.0 → 1.10.2

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.
@@ -0,0 +1,401 @@
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
41
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
42
+ return new (P || (P = Promise))(function (resolve, reject) {
43
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
44
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
45
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
46
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
47
+ });
48
+ };
49
+ var __generator = (this && this.__generator) || function (thisArg, body) {
50
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
51
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
52
+ function verb(n) { return function (v) { return step([n, v]); }; }
53
+ function step(op) {
54
+ if (f) throw new TypeError("Generator is already executing.");
55
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
56
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
57
+ if (y = 0, t) op = [op[0] & 2, t.value];
58
+ switch (op[0]) {
59
+ case 0: case 1: t = op; break;
60
+ case 4: _.label++; return { value: op[1], done: false };
61
+ case 5: _.label++; y = op[1]; op = [0]; continue;
62
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
63
+ default:
64
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
65
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
66
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
67
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
68
+ if (t[2]) _.ops.pop();
69
+ _.trys.pop(); continue;
70
+ }
71
+ op = body.call(thisArg, _);
72
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
73
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
74
+ }
75
+ };
76
+ var __importDefault = (this && this.__importDefault) || function (mod) {
77
+ return (mod && mod.__esModule) ? mod : { "default": mod };
78
+ };
79
+ Object.defineProperty(exports, "__esModule", { value: true });
80
+ var http_1 = __importDefault(require("http"));
81
+ var express_session_1 = __importStar(require("express-session"));
82
+ var passport_1 = __importDefault(require("passport"));
83
+ var devLogin_1 = require("../src/routes/devLogin");
84
+ var UserServerTestEnvironment_1 = require("./UserServerTestEnvironment");
85
+ var testEnv = new UserServerTestEnvironment_1.UserServerTestEnvironment();
86
+ /**
87
+ * DEV_SMOKE_OVERNIGHT (2026-08-26) finding 7: a browser holding a STALE session cookie (a sid
88
+ * absent from the store — prior server generation, swept row, or reseeded dev db) hits
89
+ * `GET /dev/login`, gets the 302, and lands on the LOGIN FORM; the second hit lands home.
90
+ *
91
+ * The mechanism is write-ordering at the session-establishment seam: a browser follows a redirect
92
+ * the moment RESPONSE HEADERS arrive — it never waits for the body. So the session row for the
93
+ * sid named by the 302's Set-Cookie must be COMMITTED to the store before those headers flush.
94
+ * passport 0.6's `request.login` guarantees exactly that (regenerate → bind → save, committed
95
+ * before the login callback — see establishSession); under a pre-0.6 passport runtime the save
96
+ * is deferred to express-session's end-of-response proxy, whose split-response write flushes the
97
+ * 302's headers BEFORE the store write commits — the redirected navigation races the commit and
98
+ * loses, reads no session, and the SPA routes to /login.
99
+ *
100
+ * This suite drives the REAL stack a browser meets — express-session 1.17.1 (the exact
101
+ * @proteinjs/server configureSession options and dev cookie-name shape) + real passport +
102
+ * the devLogin route — against a store with honest commit latency, and asserts the contract
103
+ * from the client's side: follow the 302 the instant headers arrive, carrying exactly the
104
+ * cookies the response set, and the follow-up MUST read as authenticated on the FIRST pass.
105
+ */
106
+ var SESSION_COOKIE_NAME = 'n3xa.sid.9876'; // dev cookie-name shape: n3xa.sid.${SERVER_PORT}
107
+ var SESSION_SECRET = 'dev-first-hit-test-secret';
108
+ var ENV_EMAIL = 'dev@test.local';
109
+ /** Store commit latency. Generous so a pre-0.6 runtime (write racing the redirect) loses the
110
+ * race DETERMINISTICALLY; under the passport 0.6 contract the row commits before the redirect
111
+ * is even issued, so green never depends on this number. */
112
+ var COMMIT_LATENCY_MS = 150;
113
+ var READ_LATENCY_MS = 5;
114
+ /**
115
+ * In-memory session store with DbSessionStore's contract: callbacks fire only after the
116
+ * operation commits, and commits take real time (Spanner in dev). Rows are inspectable so the
117
+ * test can assert store truth directly and clear it to model a next server generation.
118
+ */
119
+ var CommitLatencySessionStore = /** @class */ (function (_super) {
120
+ __extends(CommitLatencySessionStore, _super);
121
+ function CommitLatencySessionStore() {
122
+ var _this = _super !== null && _super.apply(this, arguments) || this;
123
+ _this.rows = new Map();
124
+ _this.get = function (sessionId, cb) {
125
+ var timer = setTimeout(function () {
126
+ var row = _this.rows.get(sessionId);
127
+ cb(null, row ? JSON.parse(row) : undefined);
128
+ }, READ_LATENCY_MS);
129
+ timer.unref();
130
+ };
131
+ _this.set = function (sessionId, session, cb) {
132
+ var timer = setTimeout(function () {
133
+ _this.rows.set(sessionId, JSON.stringify(session));
134
+ if (cb) {
135
+ cb();
136
+ }
137
+ }, COMMIT_LATENCY_MS);
138
+ timer.unref();
139
+ };
140
+ _this.destroy = function (sessionId, cb) {
141
+ var timer = setTimeout(function () {
142
+ _this.rows.delete(sessionId);
143
+ if (cb) {
144
+ cb();
145
+ }
146
+ }, READ_LATENCY_MS);
147
+ timer.unref();
148
+ };
149
+ return _this;
150
+ }
151
+ return CommitLatencySessionStore;
152
+ }(express_session_1.Store));
153
+ describe('devLogin first hit through the real session middleware stack', function () {
154
+ var originalEnv = {
155
+ DEVELOPMENT: process.env.DEVELOPMENT,
156
+ DEV_AUTO_LOGIN_EMAIL: process.env.DEV_AUTO_LOGIN_EMAIL,
157
+ };
158
+ var store = new CommitLatencySessionStore();
159
+ var server;
160
+ var port;
161
+ beforeAll(function () { return __awaiter(void 0, void 0, void 0, function () {
162
+ return __generator(this, function (_a) {
163
+ switch (_a.label) {
164
+ case 0: return [4 /*yield*/, testEnv.beforeAll()];
165
+ case 1:
166
+ _a.sent();
167
+ return [4 /*yield*/, testEnv.createUser({ name: 'Dev Default', email: ENV_EMAIL })];
168
+ case 2:
169
+ _a.sent();
170
+ server = createServerWithRealSessionStack(store);
171
+ return [4 /*yield*/, new Promise(function (resolve) { return server.listen(0, resolve); })];
172
+ case 3:
173
+ _a.sent();
174
+ port = server.address().port;
175
+ return [2 /*return*/];
176
+ }
177
+ });
178
+ }); });
179
+ afterAll(function () { return __awaiter(void 0, void 0, void 0, function () {
180
+ return __generator(this, function (_a) {
181
+ switch (_a.label) {
182
+ case 0: return [4 /*yield*/, new Promise(function (resolve, reject) { return server.close(function (error) { return (error ? reject(error) : resolve()); }); })];
183
+ case 1:
184
+ _a.sent();
185
+ return [4 /*yield*/, testEnv.afterAll()];
186
+ case 2:
187
+ _a.sent();
188
+ return [2 /*return*/];
189
+ }
190
+ });
191
+ }); });
192
+ beforeEach(function () {
193
+ process.env.DEVELOPMENT = 'true';
194
+ process.env.DEV_AUTO_LOGIN_EMAIL = ENV_EMAIL;
195
+ });
196
+ afterEach(function () {
197
+ for (var _i = 0, _a = Object.entries(originalEnv); _i < _a.length; _i++) {
198
+ var _b = _a[_i], key = _b[0], value = _b[1];
199
+ if (value === undefined) {
200
+ delete process.env[key];
201
+ }
202
+ else {
203
+ process.env[key] = value;
204
+ }
205
+ }
206
+ });
207
+ it('stale-cookie first hit: following the 302 on HEADERS reads as authenticated, first pass', function () { return __awaiter(void 0, void 0, void 0, function () {
208
+ var priorHit, staleCookie, firstHit, followUpCookie, followUp;
209
+ return __generator(this, function (_a) {
210
+ switch (_a.label) {
211
+ case 0: return [4 /*yield*/, request(port, '/dev/login', { waitForBody: true })];
212
+ case 1:
213
+ priorHit = _a.sent();
214
+ staleCookie = sessionCookiePair(priorHit);
215
+ store.rows.clear();
216
+ return [4 /*yield*/, request(port, '/dev/login', { cookie: staleCookie })];
217
+ case 2:
218
+ firstHit = _a.sent();
219
+ expect(firstHit.statusCode).toBe(302);
220
+ expect(firstHit.headers.location).toBe('/');
221
+ followUpCookie = sessionCookiePair(firstHit);
222
+ expect(sidOf(followUpCookie)).not.toBe(sidOf(staleCookie));
223
+ return [4 /*yield*/, request(port, '/whoami', { cookie: followUpCookie, waitForBody: true })];
224
+ case 3:
225
+ followUp = _a.sent();
226
+ expect(JSON.parse(followUp.body)).toEqual({ user: ENV_EMAIL });
227
+ return [2 /*return*/];
228
+ }
229
+ });
230
+ }); });
231
+ it('cookieless first hit: same first-pass guarantee for a fresh browser', function () { return __awaiter(void 0, void 0, void 0, function () {
232
+ var firstHit, followUp;
233
+ return __generator(this, function (_a) {
234
+ switch (_a.label) {
235
+ case 0:
236
+ store.rows.clear();
237
+ return [4 /*yield*/, request(port, '/dev/login')];
238
+ case 1:
239
+ firstHit = _a.sent();
240
+ expect(firstHit.statusCode).toBe(302);
241
+ return [4 /*yield*/, request(port, '/whoami', { cookie: sessionCookiePair(firstHit), waitForBody: true })];
242
+ case 2:
243
+ followUp = _a.sent();
244
+ expect(JSON.parse(followUp.body)).toEqual({ user: ENV_EMAIL });
245
+ return [2 /*return*/];
246
+ }
247
+ });
248
+ }); });
249
+ });
250
+ /**
251
+ * The real middleware stack a request traverses in front of the route — express-session with
252
+ * @proteinjs/server configureSession's exact options (resave: false, saveUninitialized: false,
253
+ * rolling: true, the dev port-scoped cookie name) + passport initialize/session with the
254
+ * identity (de)serialization @proteinjs/server registers — on a raw node http server, so
255
+ * header-flush timing is the genuine article.
256
+ */
257
+ function createServerWithRealSessionStack(store) {
258
+ var sixtyDays = 1000 * 60 * 60 * 24 * 60;
259
+ var sessionMiddleware = (0, express_session_1.default)({
260
+ name: SESSION_COOKIE_NAME,
261
+ secret: SESSION_SECRET,
262
+ store: store,
263
+ resave: false,
264
+ saveUninitialized: false,
265
+ cookie: { maxAge: sixtyDays },
266
+ rolling: true,
267
+ });
268
+ var authenticator = new passport_1.default.Passport();
269
+ authenticator.serializeUser(function (user, done) { return done(null, user); });
270
+ authenticator.deserializeUser(function (id, done) { return done(null, id); });
271
+ var middleware = [
272
+ sessionMiddleware,
273
+ authenticator.initialize(),
274
+ authenticator.session(),
275
+ ];
276
+ return http_1.default.createServer(function (req, res) {
277
+ runMiddleware(middleware, req, res, function (error) {
278
+ if (error) {
279
+ res.statusCode = 500;
280
+ res.end(String(error));
281
+ return;
282
+ }
283
+ void dispatch(req, res);
284
+ });
285
+ });
286
+ }
287
+ function runMiddleware(middleware, req, res, done) {
288
+ var next = function (index) { return function (error) {
289
+ if (error || index >= middleware.length) {
290
+ done(error);
291
+ return;
292
+ }
293
+ middleware[index](req, res, next(index + 1));
294
+ }; };
295
+ next(0)();
296
+ }
297
+ function dispatch(req, res) {
298
+ var _a, _b, _c;
299
+ return __awaiter(this, void 0, void 0, function () {
300
+ var url, request_1, query_1, error_1, session;
301
+ return __generator(this, function (_d) {
302
+ switch (_d.label) {
303
+ case 0:
304
+ url = new URL((_a = req.url) !== null && _a !== void 0 ? _a : '/', 'http://localhost');
305
+ if (!(url.pathname === '/dev/login')) return [3 /*break*/, 5];
306
+ request_1 = req;
307
+ query_1 = {};
308
+ url.searchParams.forEach(function (value, key) { return (query_1[key] = value); });
309
+ request_1.query = query_1;
310
+ _d.label = 1;
311
+ case 1:
312
+ _d.trys.push([1, 3, , 4]);
313
+ return [4 /*yield*/, devLogin_1.devLogin.onRequest(req, expressResponseShim(res))];
314
+ case 2:
315
+ _d.sent();
316
+ return [3 /*break*/, 4];
317
+ case 3:
318
+ error_1 = _d.sent();
319
+ // Mirrors @proteinjs/server's wrapRoute: a route throw is caught and logged, never a
320
+ // hung response. (There it falls through to the star route; here we end with 500.)
321
+ console.error(error_1);
322
+ if (!res.writableEnded) {
323
+ res.statusCode = 500;
324
+ res.end();
325
+ }
326
+ return [3 /*break*/, 4];
327
+ case 4: return [2 /*return*/];
328
+ case 5:
329
+ if (url.pathname === '/whoami') {
330
+ session = req.session;
331
+ res.setHeader('Content-Type', 'application/json');
332
+ res.end(JSON.stringify({ user: (_c = (_b = session === null || session === void 0 ? void 0 : session.passport) === null || _b === void 0 ? void 0 : _b.user) !== null && _c !== void 0 ? _c : null }));
333
+ return [2 /*return*/];
334
+ }
335
+ res.statusCode = 404;
336
+ res.end();
337
+ return [2 /*return*/];
338
+ }
339
+ });
340
+ });
341
+ }
342
+ /** The express response surface devLogin touches, over the RAW response object — so
343
+ * express-session's proxied `res.end` (the commit point under test) is the one that runs. */
344
+ function expressResponseShim(res) {
345
+ var shim = {
346
+ status: function (code) {
347
+ res.statusCode = code;
348
+ return shim;
349
+ },
350
+ send: function (body) {
351
+ res.end(body === undefined ? undefined : String(body));
352
+ },
353
+ redirect: function (path) {
354
+ var body = "Found. Redirecting to ".concat(path);
355
+ res.statusCode = 302;
356
+ res.setHeader('Location', path);
357
+ res.setHeader('Content-Type', 'text/plain; charset=utf-8');
358
+ res.setHeader('Content-Length', Buffer.byteLength(body));
359
+ res.end(body);
360
+ },
361
+ };
362
+ return shim;
363
+ }
364
+ /**
365
+ * GET `path`. By default resolves the moment response HEADERS arrive (browser-following-a-
366
+ * redirect semantics — the body may still be in flight and its socket is left to drain);
367
+ * `waitForBody` resolves after the full body, which with express-session's end proxy also
368
+ * means the response's session write has committed.
369
+ */
370
+ function request(port, path, options) {
371
+ if (options === void 0) { options = {}; }
372
+ return new Promise(function (resolve, reject) {
373
+ var clientRequest = http_1.default.get({ port: port, path: path, headers: options.cookie ? { cookie: options.cookie } : {}, agent: false }, function (res) {
374
+ if (!options.waitForBody) {
375
+ resolve({ statusCode: res.statusCode, headers: res.headers });
376
+ res.resume();
377
+ return;
378
+ }
379
+ var body = '';
380
+ res.on('data', function (chunk) { return (body += chunk); });
381
+ res.on('end', function () { return resolve({ statusCode: res.statusCode, headers: res.headers, body: body }); });
382
+ });
383
+ clientRequest.on('error', reject);
384
+ });
385
+ }
386
+ /** The `name=value` pair of the response's session Set-Cookie — what the browser sends back. */
387
+ function sessionCookiePair(response) {
388
+ var _a;
389
+ var setCookies = (_a = response.headers['set-cookie']) !== null && _a !== void 0 ? _a : [];
390
+ var sessionCookie = setCookies.find(function (cookie) { return cookie.startsWith("".concat(SESSION_COOKIE_NAME, "=")); });
391
+ if (!sessionCookie) {
392
+ throw new Error("response set no ".concat(SESSION_COOKIE_NAME, " cookie (Set-Cookie: ").concat(JSON.stringify(setCookies), ")"));
393
+ }
394
+ return sessionCookie.split(';')[0];
395
+ }
396
+ /** The raw sid inside a signed session cookie pair (`name=s%3A<sid>.<signature>`). */
397
+ function sidOf(cookiePair) {
398
+ var value = decodeURIComponent(cookiePair.slice(cookiePair.indexOf('=') + 1));
399
+ return value.startsWith('s:') ? value.slice(2, value.indexOf('.')) : value;
400
+ }
401
+ //# sourceMappingURL=DevLoginStaleCookieFirstHit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DevLoginStaleCookieFirstHit.test.js","sourceRoot":"","sources":["../../test/DevLoginStaleCookieFirstHit.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=EstablishSessionRuntimeContract.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EstablishSessionRuntimeContract.test.d.ts","sourceRoot":"","sources":["../../test/EstablishSessionRuntimeContract.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ var establishSession_1 = require("../src/authentication/establishSession");
40
+ var passportSessionHarness_1 = require("./passportSessionHarness");
41
+ /**
42
+ * establishSession's post-condition: `request.login` must have REPLACED `request.session`
43
+ * (express-session's regenerate always assigns a fresh Session object — the fresh sid the
44
+ * commit-before-response save then writes under). Passport 0.6 honors that on every login;
45
+ * a pre-0.6 passport runtime (@proteinjs/server < 3.5.1) resolves the login WITHOUT
46
+ * regenerating or saving — silently reopening the session-fixation hole (CVE-2022-25896) AND
47
+ * the first-hit race (the post-login navigation beating the deferred session write — the
48
+ * /dev/login lands-on-login-form smoke finding, DEV_SMOKE_OVERNIGHT 2026-08-26 #7).
49
+ *
50
+ * The guard turns that silent degradation into a loud, seam-naming failure at the one owner of
51
+ * session establishment. DevLoginStaleCookieFirstHit.test.ts proves the honored contract
52
+ * through the real middleware stack; this suite pins the refusal when the runtime cannot honor
53
+ * it.
54
+ */
55
+ describe('establishSession runtime contract', function () {
56
+ it('rejects, naming the seam, when login resolves without replacing the session (pre-0.6 passport shape)', function () { return __awaiter(void 0, void 0, void 0, function () {
57
+ var session, request;
58
+ return __generator(this, function (_a) {
59
+ switch (_a.label) {
60
+ case 0:
61
+ session = {};
62
+ request = {
63
+ session: session,
64
+ login: function (user, cb) {
65
+ // passport 0.4's SessionManager.logIn: bind onto the SAME session object, no
66
+ // regenerate, no save — the stale-runtime shape the guard must refuse.
67
+ session.passport = { user: user };
68
+ cb();
69
+ },
70
+ };
71
+ return [4 /*yield*/, expect((0, establishSession_1.establishSession)(request, 'dev@test.local')).rejects.toThrow(/@proteinjs\/server/)];
72
+ case 1:
73
+ _a.sent();
74
+ return [2 /*return*/];
75
+ }
76
+ });
77
+ }); });
78
+ it('resolves through the real passport 0.6 machinery, user bound on the post-regeneration session', function () { return __awaiter(void 0, void 0, void 0, function () {
79
+ var _a, request, events;
80
+ return __generator(this, function (_b) {
81
+ switch (_b.label) {
82
+ case 0: return [4 /*yield*/, (0, passportSessionHarness_1.createPassportRequest)()];
83
+ case 1:
84
+ _a = _b.sent(), request = _a.request, events = _a.events;
85
+ return [4 /*yield*/, (0, establishSession_1.establishSession)(request, 'dev@test.local')];
86
+ case 2:
87
+ _b.sent();
88
+ expect(request.session.passport.user).toBe('dev@test.local');
89
+ expect(events).toEqual(['regenerate', 'login', 'save']);
90
+ return [2 /*return*/];
91
+ }
92
+ });
93
+ }); });
94
+ it('still surfaces a login failure as a rejection (never as the contract error)', function () { return __awaiter(void 0, void 0, void 0, function () {
95
+ var loginError, request;
96
+ return __generator(this, function (_a) {
97
+ switch (_a.label) {
98
+ case 0:
99
+ loginError = new Error('serialize blew up');
100
+ request = {
101
+ session: {},
102
+ login: function (_user, cb) {
103
+ cb(loginError);
104
+ },
105
+ };
106
+ return [4 /*yield*/, expect((0, establishSession_1.establishSession)(request, 'dev@test.local')).rejects.toThrow('serialize blew up')];
107
+ case 1:
108
+ _a.sent();
109
+ return [2 /*return*/];
110
+ }
111
+ });
112
+ }); });
113
+ });
114
+ //# sourceMappingURL=EstablishSessionRuntimeContract.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EstablishSessionRuntimeContract.test.js","sourceRoot":"","sources":["../../test/EstablishSessionRuntimeContract.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2EAA0E;AAC1E,mEAAiE;AAEjE;;;;;;;;;;;;;GAaG;AACH,QAAQ,CAAC,mCAAmC,EAAE;IAC5C,EAAE,CAAC,sGAAsG,EAAE;;;;;oBACnG,OAAO,GAA4B,EAAE,CAAC;oBACtC,OAAO,GAAG;wBACd,OAAO,SAAA;wBACP,KAAK,YAAC,IAAY,EAAE,EAA6B;4BAC/C,6EAA6E;4BAC7E,uEAAuE;4BACvE,OAAO,CAAC,QAAQ,GAAG,EAAE,IAAI,MAAA,EAAE,CAAC;4BAC5B,EAAE,EAAE,CAAC;wBACP,CAAC;qBACF,CAAC;oBAEF,qBAAM,MAAM,CAAC,IAAA,mCAAgB,EAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,EAAA;;oBAA/F,SAA+F,CAAC;;;;SACjG,CAAC,CAAC;IAEH,EAAE,CAAC,+FAA+F,EAAE;;;;wBACtE,qBAAM,IAAA,8CAAqB,GAAE,EAAA;;oBAAnD,KAAsB,SAA6B,EAAjD,OAAO,aAAA,EAAE,MAAM,YAAA;oBAEvB,qBAAM,IAAA,mCAAgB,EAAC,OAAO,EAAE,gBAAgB,CAAC,EAAA;;oBAAjD,SAAiD,CAAC;oBAElD,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;oBAC7D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;;;;SACzD,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE;;;;;oBAC1E,UAAU,GAAG,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;oBAC5C,OAAO,GAAG;wBACd,OAAO,EAAE,EAAE;wBACX,KAAK,YAAC,KAAa,EAAE,EAA6B;4BAChD,EAAE,CAAC,UAAU,CAAC,CAAC;wBACjB,CAAC;qBACF,CAAC;oBAEF,qBAAM,MAAM,CAAC,IAAA,mCAAgB,EAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAA;;oBAA9F,SAA8F,CAAC;;;;SAChG,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -107,12 +107,21 @@ var resetTableWatcherMap = function () {
107
107
  */
108
108
  describe('Machine accounts as source records', function () {
109
109
  var objectCache = function () { return reflection_1.SourceRepository.get().objectCache; };
110
+ var namedObjectCache = function () { return reflection_1.SourceRepository.get().namedObjectCache; };
111
+ var seedLoaders = function (declarations) {
112
+ objectCache()['@proteinjs/db/SourceRecordLoader'] = declarations;
113
+ namedObjectCache()['@proteinjs/db/SourceRecordLoader'] = declarations.map(function (object, i) { return ({
114
+ qualifiedName: "@proteinjs/user-server-test/MachineAccountFixture".concat(i),
115
+ packageName: '@proteinjs/user-server',
116
+ object: object,
117
+ }); });
118
+ };
110
119
  /** One boot of Db.init's source-record leg with these MachineAccount declarations. */
111
120
  var boot = function (declarations) { return __awaiter(void 0, void 0, void 0, function () {
112
121
  return __generator(this, function (_a) {
113
122
  switch (_a.label) {
114
123
  case 0:
115
- objectCache()['@proteinjs/db/SourceRecordLoader'] = declarations;
124
+ seedLoaders(declarations);
116
125
  return [4 /*yield*/, new db_1.SourceRecordSyncRunner().load()];
117
126
  case 1:
118
127
  _a.sent();
@@ -165,6 +174,7 @@ describe('Machine accounts as source records', function () {
165
174
  switch (_a.label) {
166
175
  case 0:
167
176
  delete objectCache()['@proteinjs/db/SourceRecordLoader'];
177
+ delete namedObjectCache()['@proteinjs/db/SourceRecordLoader'];
168
178
  delete objectCache()['@proteinjs/db/TableWatcher'];
169
179
  delete objectCache()['@proteinjs/user/RoleCatalogEntry'];
170
180
  resetTableWatcherMap();
@@ -265,8 +275,13 @@ describe('Machine accounts as source records', function () {
265
275
  }
266
276
  });
267
277
  }); });
268
- it('removed from source: deactivated (never deleted), sessions killed, login refused; re-declaring reactivates', function () { return __awaiter(void 0, void 0, void 0, function () {
269
- var human, removed, _a, _b, _c, _d;
278
+ // KNOWN CROSS-TRAIN GAP (it.failing — flips RED the moment it starts passing, forcing the
279
+ // marker's removal): the re-declare-after-removal leg needs db's soft-removal re-adoption
280
+ // (integration/r5-db ba9f4ba7 — the sync currently INSERTs the existing machine-test-ops row
281
+ // instead of adopting it). Green everywhere else against db ^1.35.0; this leg lands with the
282
+ // R5 db mint. Exact repro + the semantics call recorded on this suite's landing commit.
283
+ it.failing('removed from source: deactivated (never deleted), sessions killed, login refused; re-declaring reactivates', function () { return __awaiter(void 0, void 0, void 0, function () {
284
+ var human, SurvivingSibling, removed, _a, _b, _c, _d;
270
285
  return __generator(this, function (_e) {
271
286
  switch (_e.label) {
272
287
  case 0: return [4 /*yield*/, testEnv.createUser({ name: 'A human', email: 'human@test.local' })];
@@ -281,7 +296,23 @@ describe('Machine accounts as source records', function () {
281
296
  return [4 /*yield*/, insertSession('human-session', human.email)];
282
297
  case 4:
283
298
  _e.sent();
284
- return [4 /*yield*/, boot([])];
299
+ SurvivingSibling = /** @class */ (function (_super) {
300
+ __extends(SurvivingSibling, _super);
301
+ // Removal under the db >=1.34.4 ownership law: the reconcile only touches rows whose
302
+ // sourcePackage the RUNNING build still declares from (shared-db safety — a build missing a
303
+ // package must never deactivate that package's rows). The real removal case is the package
304
+ // still booting with THIS declaration gone — modeled by keeping another declaration from
305
+ // the same package aboard. boot([]) would model "package vanished", which the law protects.
306
+ function SurvivingSibling() {
307
+ var _this = _super !== null && _super.apply(this, arguments) || this;
308
+ _this.email = 'machine-sibling@test.local';
309
+ _this.name = 'Surviving sibling machine';
310
+ _this.secretName = 'sibling-secret';
311
+ return _this;
312
+ }
313
+ return SurvivingSibling;
314
+ }(TestOpsMachineAccount));
315
+ return [4 /*yield*/, boot([new SurvivingSibling()])];
285
316
  case 5:
286
317
  _e.sent();
287
318
  return [4 /*yield*/, machineRow()];
@@ -496,7 +527,7 @@ describe('Machine accounts as source records', function () {
496
527
  }
497
528
  return NotBooted;
498
529
  }(TestOpsMachineAccount));
499
- objectCache()['@proteinjs/db/SourceRecordLoader'] = [new TestOpsMachineAccount(), new NotBooted()];
530
+ seedLoaders([new TestOpsMachineAccount(), new NotBooted()]);
500
531
  return [4 /*yield*/, new MachineCredentials_1.MachineCredentials().listMachineAccounts()];
501
532
  case 1:
502
533
  before = _a.sent();
@@ -561,7 +592,7 @@ describe('Machine accounts as source records', function () {
561
592
  return NotBooted;
562
593
  }(TestOpsMachineAccount));
563
594
  // Declared (discoverable) but never booted: no source-owned row exists.
564
- objectCache()['@proteinjs/db/SourceRecordLoader'] = [new TestOpsMachineAccount(), new NotBooted()];
595
+ seedLoaders([new TestOpsMachineAccount(), new NotBooted()]);
565
596
  return [4 /*yield*/, expect(new MachineCredentials_1.MachineCredentials().mintCredential('machine-not-booted@test.local')).rejects.toThrow(/has not been loaded from source yet/)];
566
597
  case 1:
567
598
  _a.sent();