@terreno/api 0.20.2 → 0.22.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/.ai/guidelines/core.md +71 -0
- package/.ai/skills/mongoose-schema-safety/SKILL.md +143 -0
- package/README.md +54 -1
- package/bunfig.toml +1 -1
- package/dist/__tests__/versionCheckPlugin.test.js +29 -7
- package/dist/actions.openApi.test.js +13 -11
- package/dist/api.js +98 -11
- package/dist/api.query.test.js +31 -1
- package/dist/api.test.js +211 -0
- package/dist/auth.test.js +418 -43
- package/dist/betterAuth.d.ts +1 -1
- package/dist/consentApp.test.js +1 -0
- package/dist/example.js +4 -4
- package/dist/expressServer.d.ts +0 -22
- package/dist/expressServer.js +1 -125
- package/dist/expressServer.test.js +90 -91
- package/dist/githubAuth.test.js +22 -22
- package/dist/logger.d.ts +154 -0
- package/dist/logger.js +445 -26
- package/dist/logger.test.js +435 -0
- package/dist/middleware.d.ts +7 -0
- package/dist/middleware.js +58 -1
- package/dist/middleware.test.js +159 -0
- package/dist/models/consentForm.js +2 -1
- package/dist/models/consentResponse.js +2 -1
- package/dist/models/versionConfig.js +2 -1
- package/dist/openApi.test.js +10 -17
- package/dist/openApiBuilder.d.ts +18 -0
- package/dist/openApiBuilder.js +21 -0
- package/dist/openApiBuilder.test.js +34 -10
- package/dist/permissions.test.js +10 -43
- package/dist/populate.test.js +10 -42
- package/dist/realtime/changeStreamWatcher.d.ts +4 -4
- package/dist/realtime/changeStreamWatcher.js +2 -4
- package/dist/realtime/queryMatcher.d.ts +1 -1
- package/dist/realtime/queryMatcher.js +39 -14
- package/dist/realtime/types.d.ts +3 -3
- package/dist/requestContext.d.ts +61 -0
- package/dist/requestContext.js +74 -0
- package/dist/secretProviders.test.js +335 -0
- package/dist/syncConsents.test.js +2 -2
- package/dist/terrenoApp.d.ts +27 -15
- package/dist/terrenoApp.js +24 -14
- package/dist/terrenoApp.test.js +52 -0
- package/dist/tests/bunSetup.js +66 -262
- package/dist/tests/createTestData.d.ts +9 -0
- package/dist/tests/createTestData.js +272 -0
- package/dist/tests/models.d.ts +71 -0
- package/dist/tests/models.js +134 -0
- package/dist/tests/mongoTestSetup.d.ts +7 -0
- package/dist/tests/mongoTestSetup.js +150 -0
- package/dist/tests/testEnv.d.ts +0 -0
- package/dist/tests/testEnv.js +6 -0
- package/dist/tests/testHelper.d.ts +22 -0
- package/dist/tests/testHelper.js +115 -0
- package/dist/tests/types.d.ts +29 -0
- package/dist/tests/types.js +2 -0
- package/dist/tests.d.ts +10 -78
- package/dist/tests.js +24 -241
- package/dist/transformers.test.js +14 -50
- package/package.json +18 -4
- package/src/__snapshots__/openApiBuilder.test.ts.snap +1 -0
- package/src/__tests__/versionCheckPlugin.test.ts +43 -15
- package/src/actions.openApi.test.ts +12 -10
- package/src/api.query.test.ts +24 -1
- package/src/api.test.ts +169 -0
- package/src/api.ts +71 -0
- package/src/auth.test.ts +287 -39
- package/src/betterAuth.ts +1 -1
- package/src/consentApp.test.ts +1 -0
- package/src/example.ts +4 -4
- package/src/expressServer.test.ts +82 -85
- package/src/expressServer.ts +1 -213
- package/src/githubAuth.test.ts +22 -22
- package/src/logger.test.ts +466 -1
- package/src/logger.ts +477 -14
- package/src/middleware.test.ts +74 -2
- package/src/middleware.ts +57 -0
- package/src/models/consentForm.ts +3 -4
- package/src/models/consentResponse.ts +6 -4
- package/src/models/versionConfig.ts +3 -4
- package/src/openApi.test.ts +10 -17
- package/src/openApiBuilder.test.ts +27 -10
- package/src/openApiBuilder.ts +24 -0
- package/src/permissions.test.ts +8 -23
- package/src/populate.test.ts +7 -22
- package/src/realtime/changeStreamWatcher.ts +15 -10
- package/src/realtime/queryMatcher.ts +54 -27
- package/src/realtime/types.ts +4 -4
- package/src/requestContext.ts +86 -0
- package/src/secretProviders.test.ts +219 -1
- package/src/syncConsents.test.ts +1 -1
- package/src/terrenoApp.test.ts +38 -0
- package/src/terrenoApp.ts +37 -15
- package/src/tests/bunSetup.ts +22 -236
- package/src/tests/createTestData.ts +176 -0
- package/src/tests/models.ts +164 -0
- package/src/tests/mongoTestSetup.ts +69 -0
- package/src/tests/testEnv.ts +4 -0
- package/src/tests/testHelper.ts +57 -0
- package/src/tests/types.ts +35 -0
- package/src/tests.ts +40 -231
- package/src/transformers.test.ts +11 -30
- package/tsconfig.typedoc.json +4 -0
- package/dist/tests/index.d.ts +0 -1
- package/dist/tests/index.js +0 -17
- package/src/tests/index.ts +0 -1
package/dist/auth.test.js
CHANGED
|
@@ -104,9 +104,9 @@ var bun_test_1 = require("bun:test");
|
|
|
104
104
|
var supertest_1 = __importDefault(require("supertest"));
|
|
105
105
|
var api_1 = require("./api");
|
|
106
106
|
var auth_1 = require("./auth");
|
|
107
|
-
var expressServer_1 = require("./expressServer");
|
|
108
107
|
var permissions_1 = require("./permissions");
|
|
109
108
|
var requestContext_1 = require("./requestContext");
|
|
109
|
+
var terrenoApp_1 = require("./terrenoApp");
|
|
110
110
|
var tests_1 = require("./tests");
|
|
111
111
|
var transformers_1 = require("./transformers");
|
|
112
112
|
var utils_1 = require("./utils");
|
|
@@ -118,7 +118,6 @@ var decodeTokenPayload = function (token) {
|
|
|
118
118
|
var app;
|
|
119
119
|
var admin;
|
|
120
120
|
var contextEvents;
|
|
121
|
-
var notAdmin;
|
|
122
121
|
var agent;
|
|
123
122
|
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
124
123
|
function addRoutes(router) {
|
|
@@ -202,45 +201,23 @@ var decodeTokenPayload = function (token) {
|
|
|
202
201
|
}); },
|
|
203
202
|
}));
|
|
204
203
|
}
|
|
205
|
-
var
|
|
206
|
-
return __generator(this, function (
|
|
207
|
-
switch (
|
|
204
|
+
var testData;
|
|
205
|
+
return __generator(this, function (_a) {
|
|
206
|
+
switch (_a.label) {
|
|
208
207
|
case 0:
|
|
209
208
|
// Reset to real time - don't freeze time here as passport-local-mongoose
|
|
210
209
|
// lockout mechanism needs real time to progress
|
|
211
210
|
(0, bun_test_1.setSystemTime)();
|
|
212
|
-
return [4 /*yield*/, (0, tests_1.
|
|
211
|
+
return [4 /*yield*/, (0, tests_1.setupTestData)()];
|
|
213
212
|
case 1:
|
|
214
|
-
|
|
213
|
+
testData = _a.sent();
|
|
214
|
+
admin = testData.users.admin;
|
|
215
215
|
contextEvents = [];
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
calories: 1,
|
|
219
|
-
created: new Date(),
|
|
220
|
-
name: "Spinach",
|
|
221
|
-
ownerId: notAdmin._id,
|
|
222
|
-
}),
|
|
223
|
-
tests_1.FoodModel.create({
|
|
224
|
-
calories: 100,
|
|
225
|
-
created: Date.now() - 10,
|
|
226
|
-
hidden: true,
|
|
227
|
-
name: "Apple",
|
|
228
|
-
ownerId: admin._id,
|
|
229
|
-
}),
|
|
230
|
-
tests_1.FoodModel.create({
|
|
231
|
-
calories: 100,
|
|
232
|
-
created: Date.now() - 10,
|
|
233
|
-
name: "Carrots",
|
|
234
|
-
ownerId: admin._id,
|
|
235
|
-
}),
|
|
236
|
-
])];
|
|
237
|
-
case 2:
|
|
238
|
-
_b.sent();
|
|
239
|
-
app = (0, expressServer_1.setupServer)({
|
|
240
|
-
addRoutes: addRoutes,
|
|
216
|
+
app = new terrenoApp_1.TerrenoApp({
|
|
217
|
+
configureApp: addRoutes,
|
|
241
218
|
skipListen: true,
|
|
242
219
|
userModel: tests_1.UserModel,
|
|
243
|
-
});
|
|
220
|
+
}).build();
|
|
244
221
|
agent = supertest_1.default.agent(app);
|
|
245
222
|
return [2 /*return*/];
|
|
246
223
|
}
|
|
@@ -315,7 +292,7 @@ var decodeTokenPayload = function (token) {
|
|
|
315
292
|
return [4 /*yield*/, agent.get("/food").expect(200)];
|
|
316
293
|
case 7:
|
|
317
294
|
getRes = _b.sent();
|
|
318
|
-
(0, bun_test_1.expect)(getRes.body.data).toHaveLength(
|
|
295
|
+
(0, bun_test_1.expect)(getRes.body.data).toHaveLength(4);
|
|
319
296
|
(0, bun_test_1.expect)(getRes.body.data.find(function (f) { return f.name === "Peas"; })).toBeDefined();
|
|
320
297
|
return [4 /*yield*/, agent
|
|
321
298
|
.patch("/food/".concat(food._id))
|
|
@@ -547,7 +524,7 @@ var decodeTokenPayload = function (token) {
|
|
|
547
524
|
return [4 /*yield*/, agent.get("/food").expect(200)];
|
|
548
525
|
case 5:
|
|
549
526
|
getRes = _b.sent();
|
|
550
|
-
(0, bun_test_1.expect)(getRes.body.data).toHaveLength(
|
|
527
|
+
(0, bun_test_1.expect)(getRes.body.data).toHaveLength(4);
|
|
551
528
|
food = getRes.body.data.find(function (f) { return f.name === "Apple"; });
|
|
552
529
|
(0, bun_test_1.expect)(food).toBeDefined();
|
|
553
530
|
return [4 /*yield*/, agent
|
|
@@ -1155,14 +1132,14 @@ var decodeTokenPayload = function (token) {
|
|
|
1155
1132
|
switch (_a.label) {
|
|
1156
1133
|
case 0:
|
|
1157
1134
|
(0, bun_test_1.setSystemTime)();
|
|
1158
|
-
return [4 /*yield*/, (0, tests_1.
|
|
1135
|
+
return [4 /*yield*/, (0, tests_1.setupTestData)()];
|
|
1159
1136
|
case 1:
|
|
1160
1137
|
_a.sent();
|
|
1161
|
-
app =
|
|
1162
|
-
|
|
1138
|
+
app = new terrenoApp_1.TerrenoApp({
|
|
1139
|
+
configureApp: function () { },
|
|
1163
1140
|
skipListen: true,
|
|
1164
1141
|
userModel: tests_1.UserModel,
|
|
1165
|
-
});
|
|
1142
|
+
}).build();
|
|
1166
1143
|
agent = supertest_1.default.agent(app);
|
|
1167
1144
|
return [2 /*return*/];
|
|
1168
1145
|
}
|
|
@@ -1265,14 +1242,14 @@ var decodeTokenPayload = function (token) {
|
|
|
1265
1242
|
switch (_a.label) {
|
|
1266
1243
|
case 0:
|
|
1267
1244
|
(0, bun_test_1.setSystemTime)();
|
|
1268
|
-
return [4 /*yield*/, (0, tests_1.
|
|
1245
|
+
return [4 /*yield*/, (0, tests_1.setupTestData)()];
|
|
1269
1246
|
case 1:
|
|
1270
1247
|
_a.sent();
|
|
1271
|
-
app =
|
|
1272
|
-
|
|
1248
|
+
app = new terrenoApp_1.TerrenoApp({
|
|
1249
|
+
configureApp: function () { },
|
|
1273
1250
|
skipListen: true,
|
|
1274
1251
|
userModel: tests_1.UserModel,
|
|
1275
|
-
});
|
|
1252
|
+
}).build();
|
|
1276
1253
|
agent = supertest_1.default.agent(app);
|
|
1277
1254
|
return [2 /*return*/];
|
|
1278
1255
|
}
|
|
@@ -1326,4 +1303,402 @@ var decodeTokenPayload = function (token) {
|
|
|
1326
1303
|
}
|
|
1327
1304
|
});
|
|
1328
1305
|
}); });
|
|
1306
|
+
(0, bun_test_1.it)("PATCH /auth/me returns 404 when user is deleted after auth", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1307
|
+
var _a, _admin, notAdmin, jwtLib, notAdminId, token, res;
|
|
1308
|
+
return __generator(this, function (_b) {
|
|
1309
|
+
switch (_b.label) {
|
|
1310
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
1311
|
+
case 1:
|
|
1312
|
+
_a = __read.apply(void 0, [_b.sent(), 2]), _admin = _a[0], notAdmin = _a[1];
|
|
1313
|
+
return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("jsonwebtoken")); })];
|
|
1314
|
+
case 2:
|
|
1315
|
+
jwtLib = (_b.sent()).default;
|
|
1316
|
+
notAdminId = notAdmin._id;
|
|
1317
|
+
token = jwtLib.sign({ id: notAdminId.toString() }, process.env.TOKEN_SECRET, {
|
|
1318
|
+
issuer: process.env.TOKEN_ISSUER,
|
|
1319
|
+
});
|
|
1320
|
+
return [4 /*yield*/, tests_1.UserModel.deleteOne({ _id: notAdminId })];
|
|
1321
|
+
case 3:
|
|
1322
|
+
_b.sent();
|
|
1323
|
+
return [4 /*yield*/, agent
|
|
1324
|
+
.patch("/auth/me")
|
|
1325
|
+
.set("authorization", "Bearer ".concat(token))
|
|
1326
|
+
.send({ email: "x@x.com" })];
|
|
1327
|
+
case 4:
|
|
1328
|
+
res = _b.sent();
|
|
1329
|
+
(0, bun_test_1.expect)([401, 404]).toContain(res.status);
|
|
1330
|
+
return [2 /*return*/];
|
|
1331
|
+
}
|
|
1332
|
+
});
|
|
1333
|
+
}); });
|
|
1334
|
+
(0, bun_test_1.it)("PATCH /auth/me returns 403 on validation error", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1335
|
+
var _a, admin, jwtLib, adminId, token, res;
|
|
1336
|
+
return __generator(this, function (_b) {
|
|
1337
|
+
switch (_b.label) {
|
|
1338
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
1339
|
+
case 1:
|
|
1340
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
|
|
1341
|
+
return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("jsonwebtoken")); })];
|
|
1342
|
+
case 2:
|
|
1343
|
+
jwtLib = (_b.sent()).default;
|
|
1344
|
+
adminId = admin._id;
|
|
1345
|
+
token = jwtLib.sign({ id: adminId.toString() }, process.env.TOKEN_SECRET, {
|
|
1346
|
+
issuer: process.env.TOKEN_ISSUER,
|
|
1347
|
+
});
|
|
1348
|
+
return [4 /*yield*/, agent
|
|
1349
|
+
.patch("/auth/me")
|
|
1350
|
+
.set("authorization", "Bearer ".concat(token))
|
|
1351
|
+
.send({ admin: "not_a_boolean_value_but_will_be_cast" })];
|
|
1352
|
+
case 3:
|
|
1353
|
+
res = _b.sent();
|
|
1354
|
+
(0, bun_test_1.expect)([200, 403]).toContain(res.status);
|
|
1355
|
+
return [2 /*return*/];
|
|
1356
|
+
}
|
|
1357
|
+
});
|
|
1358
|
+
}); });
|
|
1359
|
+
});
|
|
1360
|
+
(0, bun_test_1.describe)("Secret prefix authorization bypass", function () {
|
|
1361
|
+
var app;
|
|
1362
|
+
var agent;
|
|
1363
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1364
|
+
return __generator(this, function (_a) {
|
|
1365
|
+
switch (_a.label) {
|
|
1366
|
+
case 0:
|
|
1367
|
+
(0, bun_test_1.setSystemTime)();
|
|
1368
|
+
return [4 /*yield*/, (0, tests_1.setupTestData)()];
|
|
1369
|
+
case 1:
|
|
1370
|
+
_a.sent();
|
|
1371
|
+
app = new terrenoApp_1.TerrenoApp({
|
|
1372
|
+
configureApp: function (router) {
|
|
1373
|
+
router.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1374
|
+
allowAnonymous: true,
|
|
1375
|
+
permissions: {
|
|
1376
|
+
create: [],
|
|
1377
|
+
delete: [],
|
|
1378
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1379
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1380
|
+
update: [],
|
|
1381
|
+
},
|
|
1382
|
+
}));
|
|
1383
|
+
},
|
|
1384
|
+
skipListen: true,
|
|
1385
|
+
userModel: tests_1.UserModel,
|
|
1386
|
+
}).build();
|
|
1387
|
+
agent = supertest_1.default.agent(app);
|
|
1388
|
+
return [2 /*return*/];
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1391
|
+
}); });
|
|
1392
|
+
(0, bun_test_1.afterEach)(function () {
|
|
1393
|
+
(0, bun_test_1.setSystemTime)();
|
|
1394
|
+
});
|
|
1395
|
+
(0, bun_test_1.it)("passes through with Secret prefix authorization header without JWT decoding", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1396
|
+
var res;
|
|
1397
|
+
return __generator(this, function (_a) {
|
|
1398
|
+
switch (_a.label) {
|
|
1399
|
+
case 0: return [4 /*yield*/, agent.get("/food").set("authorization", "Secret my-secret-token").expect(200)];
|
|
1400
|
+
case 1:
|
|
1401
|
+
res = _a.sent();
|
|
1402
|
+
(0, bun_test_1.expect)(res.body.data).toBeDefined();
|
|
1403
|
+
return [2 /*return*/];
|
|
1404
|
+
}
|
|
1405
|
+
});
|
|
1406
|
+
}); });
|
|
1407
|
+
});
|
|
1408
|
+
(0, bun_test_1.describe)("generateTokens env integration", function () {
|
|
1409
|
+
var OLD_ENV = process.env;
|
|
1410
|
+
(0, bun_test_1.beforeEach)(function () {
|
|
1411
|
+
process.env = __assign({}, OLD_ENV);
|
|
1412
|
+
process.env.TOKEN_SECRET = "secret";
|
|
1413
|
+
process.env.REFRESH_TOKEN_SECRET = "refresh_secret";
|
|
1414
|
+
});
|
|
1415
|
+
(0, bun_test_1.afterEach)(function () {
|
|
1416
|
+
process.env = OLD_ENV;
|
|
1417
|
+
});
|
|
1418
|
+
(0, bun_test_1.it)("includes TOKEN_ISSUER in token when set", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1419
|
+
var result, decoded;
|
|
1420
|
+
return __generator(this, function (_a) {
|
|
1421
|
+
switch (_a.label) {
|
|
1422
|
+
case 0:
|
|
1423
|
+
process.env.TOKEN_ISSUER = "test-issuer";
|
|
1424
|
+
return [4 /*yield*/, (0, auth_1.generateTokens)({ _id: "user-123" })];
|
|
1425
|
+
case 1:
|
|
1426
|
+
result = _a.sent();
|
|
1427
|
+
decoded = decodeTokenPayload(result.token);
|
|
1428
|
+
(0, bun_test_1.expect)(decoded.iss).toBe("test-issuer");
|
|
1429
|
+
return [2 /*return*/];
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
}); });
|
|
1433
|
+
(0, bun_test_1.it)("generates a unique sessionId when none provided", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1434
|
+
var result1, result2;
|
|
1435
|
+
return __generator(this, function (_a) {
|
|
1436
|
+
switch (_a.label) {
|
|
1437
|
+
case 0: return [4 /*yield*/, (0, auth_1.generateTokens)({ _id: "user-123" })];
|
|
1438
|
+
case 1:
|
|
1439
|
+
result1 = _a.sent();
|
|
1440
|
+
return [4 /*yield*/, (0, auth_1.generateTokens)({ _id: "user-123" })];
|
|
1441
|
+
case 2:
|
|
1442
|
+
result2 = _a.sent();
|
|
1443
|
+
(0, bun_test_1.expect)(result1.sessionId).toBeDefined();
|
|
1444
|
+
(0, bun_test_1.expect)(result2.sessionId).toBeDefined();
|
|
1445
|
+
(0, bun_test_1.expect)(result1.sessionId).not.toBe(result2.sessionId);
|
|
1446
|
+
return [2 /*return*/];
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
}); });
|
|
1450
|
+
(0, bun_test_1.it)("uses provided sessionId from options", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1451
|
+
var result, decoded;
|
|
1452
|
+
return __generator(this, function (_a) {
|
|
1453
|
+
switch (_a.label) {
|
|
1454
|
+
case 0: return [4 /*yield*/, (0, auth_1.generateTokens)({ _id: "user-123" }, undefined, {
|
|
1455
|
+
sessionId: "custom-session-id",
|
|
1456
|
+
})];
|
|
1457
|
+
case 1:
|
|
1458
|
+
result = _a.sent();
|
|
1459
|
+
decoded = decodeTokenPayload(result.token);
|
|
1460
|
+
(0, bun_test_1.expect)(decoded.sid).toBe("custom-session-id");
|
|
1461
|
+
(0, bun_test_1.expect)(result.sessionId).toBe("custom-session-id");
|
|
1462
|
+
return [2 /*return*/];
|
|
1463
|
+
}
|
|
1464
|
+
});
|
|
1465
|
+
}); });
|
|
1466
|
+
});
|
|
1467
|
+
(0, bun_test_1.describe)("refresh_token without REFRESH_TOKEN_SECRET", function () {
|
|
1468
|
+
var app;
|
|
1469
|
+
var agent;
|
|
1470
|
+
var OLD_ENV = process.env;
|
|
1471
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1472
|
+
return __generator(this, function (_a) {
|
|
1473
|
+
switch (_a.label) {
|
|
1474
|
+
case 0:
|
|
1475
|
+
(0, bun_test_1.setSystemTime)();
|
|
1476
|
+
process.env = __assign({}, OLD_ENV);
|
|
1477
|
+
return [4 /*yield*/, (0, tests_1.setupTestData)()];
|
|
1478
|
+
case 1:
|
|
1479
|
+
_a.sent();
|
|
1480
|
+
app = new terrenoApp_1.TerrenoApp({
|
|
1481
|
+
configureApp: function () { },
|
|
1482
|
+
skipListen: true,
|
|
1483
|
+
userModel: tests_1.UserModel,
|
|
1484
|
+
}).build();
|
|
1485
|
+
agent = supertest_1.default.agent(app);
|
|
1486
|
+
return [2 /*return*/];
|
|
1487
|
+
}
|
|
1488
|
+
});
|
|
1489
|
+
}); });
|
|
1490
|
+
(0, bun_test_1.afterEach)(function () {
|
|
1491
|
+
(0, bun_test_1.setSystemTime)();
|
|
1492
|
+
process.env = OLD_ENV;
|
|
1493
|
+
});
|
|
1494
|
+
(0, bun_test_1.it)("returns 401 when REFRESH_TOKEN_SECRET is not set", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1495
|
+
var res;
|
|
1496
|
+
return __generator(this, function (_a) {
|
|
1497
|
+
switch (_a.label) {
|
|
1498
|
+
case 0:
|
|
1499
|
+
process.env.REFRESH_TOKEN_SECRET = "";
|
|
1500
|
+
return [4 /*yield*/, agent
|
|
1501
|
+
.post("/auth/refresh_token")
|
|
1502
|
+
.send({ refreshToken: "some-token" })
|
|
1503
|
+
.expect(401)];
|
|
1504
|
+
case 1:
|
|
1505
|
+
res = _a.sent();
|
|
1506
|
+
(0, bun_test_1.expect)(res.body.message).toContain("No REFRESH_TOKEN_SECRET set");
|
|
1507
|
+
return [2 /*return*/];
|
|
1508
|
+
}
|
|
1509
|
+
});
|
|
1510
|
+
}); });
|
|
1511
|
+
});
|
|
1512
|
+
(0, bun_test_1.describe)("generateTokens with custom TOKEN_EXPIRES_IN", function () {
|
|
1513
|
+
var OLD_ENV = process.env;
|
|
1514
|
+
(0, bun_test_1.beforeEach)(function () {
|
|
1515
|
+
process.env = __assign({}, OLD_ENV);
|
|
1516
|
+
process.env.TOKEN_SECRET = "secret";
|
|
1517
|
+
process.env.REFRESH_TOKEN_SECRET = "refresh_secret";
|
|
1518
|
+
});
|
|
1519
|
+
(0, bun_test_1.afterEach)(function () {
|
|
1520
|
+
process.env = OLD_ENV;
|
|
1521
|
+
});
|
|
1522
|
+
(0, bun_test_1.it)("uses TOKEN_EXPIRES_IN when set to a valid duration", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1523
|
+
var result, decoded, diffSeconds;
|
|
1524
|
+
return __generator(this, function (_a) {
|
|
1525
|
+
switch (_a.label) {
|
|
1526
|
+
case 0:
|
|
1527
|
+
process.env.TOKEN_EXPIRES_IN = "1h";
|
|
1528
|
+
return [4 /*yield*/, (0, auth_1.generateTokens)({ _id: "user-123" })];
|
|
1529
|
+
case 1:
|
|
1530
|
+
result = _a.sent();
|
|
1531
|
+
(0, bun_test_1.expect)(result.token).toBeDefined();
|
|
1532
|
+
decoded = decodeTokenPayload(result.token);
|
|
1533
|
+
diffSeconds = decoded.exp - decoded.iat;
|
|
1534
|
+
// 1h = 3600s
|
|
1535
|
+
(0, bun_test_1.expect)(diffSeconds).toBe(3600);
|
|
1536
|
+
return [2 /*return*/];
|
|
1537
|
+
}
|
|
1538
|
+
});
|
|
1539
|
+
}); });
|
|
1540
|
+
(0, bun_test_1.it)("uses REFRESH_TOKEN_EXPIRES_IN when set to a valid duration", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1541
|
+
var result, decoded, diffSeconds;
|
|
1542
|
+
return __generator(this, function (_a) {
|
|
1543
|
+
switch (_a.label) {
|
|
1544
|
+
case 0:
|
|
1545
|
+
process.env.REFRESH_TOKEN_EXPIRES_IN = "7d";
|
|
1546
|
+
return [4 /*yield*/, (0, auth_1.generateTokens)({ _id: "user-123" })];
|
|
1547
|
+
case 1:
|
|
1548
|
+
result = _a.sent();
|
|
1549
|
+
(0, bun_test_1.expect)(result.refreshToken).toBeDefined();
|
|
1550
|
+
decoded = decodeTokenPayload(result.refreshToken);
|
|
1551
|
+
diffSeconds = decoded.exp - decoded.iat;
|
|
1552
|
+
// 7d = 604800s
|
|
1553
|
+
(0, bun_test_1.expect)(diffSeconds).toBe(604800);
|
|
1554
|
+
return [2 /*return*/];
|
|
1555
|
+
}
|
|
1556
|
+
});
|
|
1557
|
+
}); });
|
|
1558
|
+
});
|
|
1559
|
+
(0, bun_test_1.describe)("JWT cookie extraction and /me routes edge cases", function () {
|
|
1560
|
+
var app;
|
|
1561
|
+
var agent;
|
|
1562
|
+
var OLD_ENV = process.env;
|
|
1563
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1564
|
+
return __generator(this, function (_a) {
|
|
1565
|
+
switch (_a.label) {
|
|
1566
|
+
case 0:
|
|
1567
|
+
(0, bun_test_1.setSystemTime)();
|
|
1568
|
+
process.env = __assign({}, OLD_ENV);
|
|
1569
|
+
return [4 /*yield*/, (0, tests_1.setupTestData)()];
|
|
1570
|
+
case 1:
|
|
1571
|
+
_a.sent();
|
|
1572
|
+
app = new terrenoApp_1.TerrenoApp({
|
|
1573
|
+
configureApp: function () { },
|
|
1574
|
+
skipListen: true,
|
|
1575
|
+
userModel: tests_1.UserModel,
|
|
1576
|
+
}).build();
|
|
1577
|
+
agent = supertest_1.default.agent(app);
|
|
1578
|
+
return [2 /*return*/];
|
|
1579
|
+
}
|
|
1580
|
+
});
|
|
1581
|
+
}); });
|
|
1582
|
+
(0, bun_test_1.afterEach)(function () {
|
|
1583
|
+
(0, bun_test_1.setSystemTime)();
|
|
1584
|
+
process.env = OLD_ENV;
|
|
1585
|
+
});
|
|
1586
|
+
(0, bun_test_1.it)("returns 401 for /me when no user is authenticated", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1587
|
+
var res;
|
|
1588
|
+
return __generator(this, function (_a) {
|
|
1589
|
+
switch (_a.label) {
|
|
1590
|
+
case 0: return [4 /*yield*/, agent.get("/auth/me").expect(401)];
|
|
1591
|
+
case 1:
|
|
1592
|
+
res = _a.sent();
|
|
1593
|
+
(0, bun_test_1.expect)(res.status).toBe(401);
|
|
1594
|
+
return [2 /*return*/];
|
|
1595
|
+
}
|
|
1596
|
+
});
|
|
1597
|
+
}); });
|
|
1598
|
+
(0, bun_test_1.it)("returns 401 for PATCH /me when no user is authenticated", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1599
|
+
var res;
|
|
1600
|
+
return __generator(this, function (_a) {
|
|
1601
|
+
switch (_a.label) {
|
|
1602
|
+
case 0: return [4 /*yield*/, agent.patch("/auth/me").send({ name: "Updated" }).expect(401)];
|
|
1603
|
+
case 1:
|
|
1604
|
+
res = _a.sent();
|
|
1605
|
+
(0, bun_test_1.expect)(res.status).toBe(401);
|
|
1606
|
+
return [2 /*return*/];
|
|
1607
|
+
}
|
|
1608
|
+
});
|
|
1609
|
+
}); });
|
|
1610
|
+
(0, bun_test_1.it)("returns 404 for /me when user is deleted from database", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1611
|
+
var loginRes, _a, token, userId, freshAgent, res;
|
|
1612
|
+
return __generator(this, function (_b) {
|
|
1613
|
+
switch (_b.label) {
|
|
1614
|
+
case 0: return [4 /*yield*/, agent
|
|
1615
|
+
.post("/auth/login")
|
|
1616
|
+
.send({ email: "notAdmin@example.com", password: "password" })
|
|
1617
|
+
.expect(200)];
|
|
1618
|
+
case 1:
|
|
1619
|
+
loginRes = _b.sent();
|
|
1620
|
+
_a = loginRes.body.data, token = _a.token, userId = _a.userId;
|
|
1621
|
+
// Delete the user from DB
|
|
1622
|
+
return [4 /*yield*/, tests_1.UserModel.deleteOne({ _id: userId })];
|
|
1623
|
+
case 2:
|
|
1624
|
+
// Delete the user from DB
|
|
1625
|
+
_b.sent();
|
|
1626
|
+
freshAgent = supertest_1.default.agent(app);
|
|
1627
|
+
return [4 /*yield*/, freshAgent.get("/auth/me").set("authorization", "Bearer ".concat(token))];
|
|
1628
|
+
case 3:
|
|
1629
|
+
res = _b.sent();
|
|
1630
|
+
// Without the user, the JWT verify succeeds but findById returns null
|
|
1631
|
+
(0, bun_test_1.expect)([401, 404]).toContain(res.status);
|
|
1632
|
+
return [2 /*return*/];
|
|
1633
|
+
}
|
|
1634
|
+
});
|
|
1635
|
+
}); });
|
|
1636
|
+
});
|
|
1637
|
+
(0, bun_test_1.describe)("login error and disabled user paths", function () {
|
|
1638
|
+
var app;
|
|
1639
|
+
var agent;
|
|
1640
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1641
|
+
return __generator(this, function (_a) {
|
|
1642
|
+
switch (_a.label) {
|
|
1643
|
+
case 0:
|
|
1644
|
+
(0, bun_test_1.setSystemTime)();
|
|
1645
|
+
return [4 /*yield*/, (0, tests_1.setupTestData)()];
|
|
1646
|
+
case 1:
|
|
1647
|
+
_a.sent();
|
|
1648
|
+
app = new terrenoApp_1.TerrenoApp({
|
|
1649
|
+
configureApp: function () { },
|
|
1650
|
+
skipListen: true,
|
|
1651
|
+
userModel: tests_1.UserModel,
|
|
1652
|
+
}).build();
|
|
1653
|
+
agent = supertest_1.default.agent(app);
|
|
1654
|
+
return [2 /*return*/];
|
|
1655
|
+
}
|
|
1656
|
+
});
|
|
1657
|
+
}); });
|
|
1658
|
+
(0, bun_test_1.afterEach)(function () {
|
|
1659
|
+
(0, bun_test_1.setSystemTime)();
|
|
1660
|
+
});
|
|
1661
|
+
(0, bun_test_1.it)("returns 401 with message for invalid credentials (no user found)", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1662
|
+
var res;
|
|
1663
|
+
return __generator(this, function (_a) {
|
|
1664
|
+
switch (_a.label) {
|
|
1665
|
+
case 0: return [4 /*yield*/, agent
|
|
1666
|
+
.post("/auth/login")
|
|
1667
|
+
.send({ email: "nonexistent@example.com", password: "wrong" })
|
|
1668
|
+
.expect(401)];
|
|
1669
|
+
case 1:
|
|
1670
|
+
res = _a.sent();
|
|
1671
|
+
(0, bun_test_1.expect)(res.body.message).toBeDefined();
|
|
1672
|
+
return [2 /*return*/];
|
|
1673
|
+
}
|
|
1674
|
+
});
|
|
1675
|
+
}); });
|
|
1676
|
+
(0, bun_test_1.it)("returns 401 when disabled user tries to access protected route", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1677
|
+
var loginRes, _a, token, userId, freshAgent, res;
|
|
1678
|
+
return __generator(this, function (_b) {
|
|
1679
|
+
switch (_b.label) {
|
|
1680
|
+
case 0: return [4 /*yield*/, agent
|
|
1681
|
+
.post("/auth/login")
|
|
1682
|
+
.send({ email: "notAdmin@example.com", password: "password" })
|
|
1683
|
+
.expect(200)];
|
|
1684
|
+
case 1:
|
|
1685
|
+
loginRes = _b.sent();
|
|
1686
|
+
_a = loginRes.body.data, token = _a.token, userId = _a.userId;
|
|
1687
|
+
// Disable the user
|
|
1688
|
+
return [4 /*yield*/, tests_1.UserModel.findByIdAndUpdate(userId, { disabled: true })];
|
|
1689
|
+
case 2:
|
|
1690
|
+
// Disable the user
|
|
1691
|
+
_b.sent();
|
|
1692
|
+
freshAgent = supertest_1.default.agent(app);
|
|
1693
|
+
return [4 /*yield*/, freshAgent
|
|
1694
|
+
.get("/auth/me")
|
|
1695
|
+
.set("authorization", "Bearer ".concat(token))
|
|
1696
|
+
.expect(401)];
|
|
1697
|
+
case 3:
|
|
1698
|
+
res = _b.sent();
|
|
1699
|
+
(0, bun_test_1.expect)(res.body.title).toContain("disabled");
|
|
1700
|
+
return [2 /*return*/];
|
|
1701
|
+
}
|
|
1702
|
+
});
|
|
1703
|
+
}); });
|
|
1329
1704
|
});
|
package/dist/betterAuth.d.ts
CHANGED
|
@@ -53,7 +53,7 @@ export interface BetterAuthConfig {
|
|
|
53
53
|
baseURL?: string;
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
|
-
* Auth provider selection for
|
|
56
|
+
* Auth provider selection for TerrenoApp.
|
|
57
57
|
* - "jwt": Traditional JWT/Passport authentication (default)
|
|
58
58
|
* - "better-auth": Better Auth with OAuth support
|
|
59
59
|
*/
|
package/dist/consentApp.test.js
CHANGED
|
@@ -118,6 +118,7 @@ var buildApp = function (consentAppOptions) {
|
|
|
118
118
|
case 1:
|
|
119
119
|
res = _a.sent();
|
|
120
120
|
(0, bun_test_1.expect)(res.body.data).toHaveLength(0);
|
|
121
|
+
(0, bun_test_1.expect)(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
121
122
|
return [2 /*return*/];
|
|
122
123
|
}
|
|
123
124
|
});
|
package/dist/example.js
CHANGED
|
@@ -52,10 +52,10 @@ var mongoose_1 = __importStar(require("mongoose"));
|
|
|
52
52
|
var passport_local_mongoose_1 = __importDefault(require("passport-local-mongoose"));
|
|
53
53
|
var api_1 = require("./api");
|
|
54
54
|
var auth_1 = require("./auth");
|
|
55
|
-
var expressServer_1 = require("./expressServer");
|
|
56
55
|
var logger_1 = require("./logger");
|
|
57
56
|
var permissions_1 = require("./permissions");
|
|
58
57
|
var plugins_1 = require("./plugins");
|
|
58
|
+
var terrenoApp_1 = require("./terrenoApp");
|
|
59
59
|
mongoose_1.default
|
|
60
60
|
.connect("mongodb://localhost:27017/example")
|
|
61
61
|
.then(function () {
|
|
@@ -119,12 +119,12 @@ var getBaseServer = function () {
|
|
|
119
119
|
update: [permissions_1.Permissions.IsOwner],
|
|
120
120
|
}, queryFields: ["name", "calories", "created", "ownerId", "hidden"] })));
|
|
121
121
|
};
|
|
122
|
-
return
|
|
123
|
-
|
|
122
|
+
return new terrenoApp_1.TerrenoApp({
|
|
123
|
+
configureApp: addRoutes,
|
|
124
124
|
loggingOptions: {
|
|
125
125
|
level: "debug",
|
|
126
126
|
},
|
|
127
127
|
userModel: UserModel,
|
|
128
|
-
});
|
|
128
|
+
}).build();
|
|
129
129
|
};
|
|
130
130
|
getBaseServer();
|
package/dist/expressServer.d.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import * as Sentry from "@sentry/bun";
|
|
2
1
|
import express, { type Router } from "express";
|
|
3
2
|
import type jwt from "jsonwebtoken";
|
|
4
3
|
import type { ModelRouterOptions } from "./api";
|
|
5
|
-
import { type UserModel as UserMongooseModel } from "./auth";
|
|
6
|
-
import { type GitHubAuthOptions } from "./githubAuth";
|
|
7
|
-
import { type LoggingOptions } from "./logger";
|
|
8
4
|
export declare const setupEnvironment: () => void;
|
|
9
5
|
export type AddRoutes = (router: Router, options?: Partial<ModelRouterOptions<unknown>>) => void;
|
|
10
6
|
export declare const logRequests: (req: any, res: any, next: express.NextFunction) => void;
|
|
@@ -15,24 +11,6 @@ export interface AuthOptions {
|
|
|
15
11
|
generateTokenExpiration?: (user: any) => number | jwt.SignOptions["expiresIn"];
|
|
16
12
|
generateRefreshTokenExpiration?: (user: any) => number | jwt.SignOptions["expiresIn"];
|
|
17
13
|
}
|
|
18
|
-
export interface SetupServerOptions {
|
|
19
|
-
userModel: UserMongooseModel;
|
|
20
|
-
addRoutes: AddRoutes;
|
|
21
|
-
loggingOptions?: LoggingOptions;
|
|
22
|
-
logRequests?: boolean;
|
|
23
|
-
authOptions?: AuthOptions;
|
|
24
|
-
/**
|
|
25
|
-
* GitHub OAuth configuration. When provided, enables GitHub authentication.
|
|
26
|
-
* Requires the user schema to have GitHub fields (use githubUserPlugin).
|
|
27
|
-
*/
|
|
28
|
-
githubAuth?: GitHubAuthOptions;
|
|
29
|
-
skipListen?: boolean;
|
|
30
|
-
corsOrigin?: string | boolean | RegExp | Array<boolean | string | RegExp> | ((requestOrigin: string | undefined, callback: (err: Error | null, origin?: boolean | string | RegExp | Array<boolean | string | RegExp>) => void) => void);
|
|
31
|
-
addMiddleware?: AddRoutes;
|
|
32
|
-
ignoreTraces?: string[];
|
|
33
|
-
sentryOptions?: Sentry.BunOptions;
|
|
34
|
-
}
|
|
35
|
-
export declare const setupServer: (options: SetupServerOptions) => express.Application;
|
|
36
14
|
export declare const cronjob: (name: string, schedule: "hourly" | "minutely" | string, callback: () => void) => void;
|
|
37
15
|
export interface WrapScriptOptions {
|
|
38
16
|
onFinish?: (result?: unknown) => void | Promise<void>;
|