@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.
- package/CHANGELOG.md +22 -0
- package/dist/generated/index.js +1 -1
- package/dist/src/authentication/establishSession.d.ts +9 -2
- package/dist/src/authentication/establishSession.d.ts.map +1 -1
- package/dist/src/authentication/establishSession.js +23 -5
- package/dist/src/authentication/establishSession.js.map +1 -1
- package/dist/test/DevLoginStaleCookieFirstHit.test.d.ts +2 -0
- package/dist/test/DevLoginStaleCookieFirstHit.test.d.ts.map +1 -0
- package/dist/test/DevLoginStaleCookieFirstHit.test.js +401 -0
- package/dist/test/DevLoginStaleCookieFirstHit.test.js.map +1 -0
- package/dist/test/EstablishSessionRuntimeContract.test.d.ts +2 -0
- package/dist/test/EstablishSessionRuntimeContract.test.d.ts.map +1 -0
- package/dist/test/EstablishSessionRuntimeContract.test.js +114 -0
- package/dist/test/EstablishSessionRuntimeContract.test.js.map +1 -0
- package/dist/test/MachineAccounts.integration.test.js +37 -6
- package/dist/test/MachineAccounts.integration.test.js.map +1 -1
- package/generated/index.ts +1 -1
- package/jest.config.js +7 -0
- package/package.json +5 -5
- package/src/authentication/establishSession.ts +20 -2
- package/test/DevLoginStaleCookieFirstHit.test.ts +304 -0
- package/test/EstablishSessionRuntimeContract.test.ts +54 -0
- package/test/MachineAccounts.integration.test.ts +60 -26
- package/tsconfig.json +5 -1
|
@@ -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 @@
|
|
|
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
|
-
|
|
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
|
-
|
|
269
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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();
|