@rebasepro/server-core 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/README.md +2 -2
  2. package/dist/api/ast-schema-editor.d.ts +21 -0
  3. package/dist/api/collections_for_test/callbacks_test_collection.d.ts +2 -0
  4. package/dist/api/errors.d.ts +53 -0
  5. package/dist/api/index.d.ts +7 -0
  6. package/dist/api/logs-routes.d.ts +37 -0
  7. package/dist/api/openapi-generator.d.ts +16 -0
  8. package/dist/api/rest/api-generator.d.ts +68 -0
  9. package/{src/api/rest/index.ts → dist/api/rest/index.d.ts} +0 -1
  10. package/dist/api/rest/query-parser.d.ts +6 -0
  11. package/dist/api/schema-editor-routes.d.ts +3 -0
  12. package/dist/api/types.d.ts +56 -0
  13. package/dist/auth/adapter-middleware.d.ts +43 -0
  14. package/dist/auth/admin-roles-route.d.ts +18 -0
  15. package/dist/auth/admin-user-ops.d.ts +79 -0
  16. package/dist/auth/admin-users-route.d.ts +28 -0
  17. package/dist/auth/api-keys/api-key-middleware.d.ts +39 -0
  18. package/{src/auth/api-keys/api-key-permission-guard.ts → dist/auth/api-keys/api-key-permission-guard.d.ts} +2 -34
  19. package/dist/auth/api-keys/api-key-routes.d.ts +20 -0
  20. package/dist/auth/api-keys/api-key-store.d.ts +35 -0
  21. package/{src/auth/api-keys/api-key-types.ts → dist/auth/api-keys/api-key-types.d.ts} +0 -6
  22. package/{src/auth/api-keys/index.ts → dist/auth/api-keys/index.d.ts} +2 -22
  23. package/dist/auth/apple-oauth.d.ts +30 -0
  24. package/{src/auth/auth-hooks.ts → dist/auth/auth-hooks.d.ts} +15 -73
  25. package/dist/auth/bitbucket-oauth.d.ts +11 -0
  26. package/dist/auth/builtin-auth-adapter.d.ts +55 -0
  27. package/dist/auth/cookie-utils.d.ts +32 -0
  28. package/{src/auth/crypto-utils.ts → dist/auth/crypto-utils.d.ts} +1 -16
  29. package/dist/auth/custom-auth-adapter.d.ts +39 -0
  30. package/dist/auth/discord-oauth.d.ts +14 -0
  31. package/dist/auth/facebook-oauth.d.ts +14 -0
  32. package/dist/auth/github-oauth.d.ts +15 -0
  33. package/dist/auth/gitlab-oauth.d.ts +13 -0
  34. package/dist/auth/google-oauth.d.ts +47 -0
  35. package/{src/auth/index.ts → dist/auth/index.d.ts} +4 -22
  36. package/{src/auth/interfaces.ts → dist/auth/interfaces.d.ts} +20 -89
  37. package/dist/auth/jwt.d.ts +67 -0
  38. package/dist/auth/linkedin-oauth.d.ts +18 -0
  39. package/dist/auth/magic-link-routes.d.ts +32 -0
  40. package/dist/auth/mfa-crypto.d.ts +23 -0
  41. package/dist/auth/mfa-routes.d.ts +7 -0
  42. package/dist/auth/mfa.d.ts +49 -0
  43. package/dist/auth/microsoft-oauth.d.ts +16 -0
  44. package/dist/auth/middleware.d.ts +158 -0
  45. package/dist/auth/password.d.ts +22 -0
  46. package/dist/auth/rate-limiter.d.ts +50 -0
  47. package/dist/auth/reset-password-admin.d.ts +29 -0
  48. package/dist/auth/rls-scope.d.ts +31 -0
  49. package/dist/auth/routes.d.ts +66 -0
  50. package/dist/auth/session-routes.d.ts +29 -0
  51. package/dist/auth/slack-oauth.d.ts +12 -0
  52. package/dist/auth/spotify-oauth.d.ts +12 -0
  53. package/dist/auth/twitter-oauth.d.ts +18 -0
  54. package/dist/collections/BackendCollectionRegistry.d.ts +13 -0
  55. package/dist/collections/loader.d.ts +5 -0
  56. package/dist/cron/cron-loader.d.ts +18 -0
  57. package/dist/cron/cron-routes.d.ts +14 -0
  58. package/dist/cron/cron-scheduler.d.ts +106 -0
  59. package/dist/cron/cron-store.d.ts +32 -0
  60. package/dist/cron/define-cron.d.ts +28 -0
  61. package/{src/cron/index.ts → dist/cron/index.d.ts} +1 -0
  62. package/dist/db/interfaces.d.ts +18 -0
  63. package/dist/dynamic-import-Dvh-K5fl.js +22 -0
  64. package/dist/dynamic-import-Dvh-K5fl.js.map +1 -0
  65. package/dist/email/index.d.ts +6 -0
  66. package/dist/email/smtp-email-service.d.ts +30 -0
  67. package/dist/email/templates.d.ts +50 -0
  68. package/{src/email/types.ts → dist/email/types.d.ts} +40 -36
  69. package/dist/env.d.ts +102 -0
  70. package/dist/functions/define-function.d.ts +55 -0
  71. package/dist/functions/function-loader.d.ts +18 -0
  72. package/dist/functions/function-routes.d.ts +10 -0
  73. package/{src/functions/index.ts → dist/functions/index.d.ts} +2 -0
  74. package/dist/history/history-routes.d.ts +23 -0
  75. package/{src/history/index.ts → dist/history/index.d.ts} +0 -1
  76. package/dist/index.d.ts +35 -0
  77. package/dist/index.es.js +18184 -0
  78. package/dist/index.es.js.map +1 -0
  79. package/dist/index.umd.js +48629 -0
  80. package/dist/index.umd.js.map +1 -0
  81. package/dist/init/docs.d.ts +4 -0
  82. package/dist/init/health.d.ts +2 -0
  83. package/dist/init/middlewares.d.ts +10 -0
  84. package/dist/init/shutdown.d.ts +65 -0
  85. package/dist/init/storage.d.ts +5 -0
  86. package/dist/init.d.ts +344 -0
  87. package/dist/jwt-BwIn8xmk.js +4200 -0
  88. package/dist/jwt-BwIn8xmk.js.map +1 -0
  89. package/dist/logger-BYU66ENZ.js +94 -0
  90. package/dist/logger-BYU66ENZ.js.map +1 -0
  91. package/dist/ms-DnYXB-Wd.js +162 -0
  92. package/dist/ms-DnYXB-Wd.js.map +1 -0
  93. package/dist/openapi-generator-Z9oYWLf_.js +586 -0
  94. package/dist/openapi-generator-Z9oYWLf_.js.map +1 -0
  95. package/dist/schema-editor-routes-D3ef8zu1.js +219 -0
  96. package/dist/schema-editor-routes-D3ef8zu1.js.map +1 -0
  97. package/dist/serve-spa.d.ts +36 -0
  98. package/dist/services/driver-registry.d.ts +78 -0
  99. package/dist/services/routed-realtime-service.d.ts +43 -0
  100. package/dist/services/webhook-service.d.ts +29 -0
  101. package/dist/singleton.d.ts +51 -0
  102. package/dist/src-B4OLmNVa.js +437 -0
  103. package/dist/src-B4OLmNVa.js.map +1 -0
  104. package/dist/src-B5WkP1Rv.js +24778 -0
  105. package/dist/src-B5WkP1Rv.js.map +1 -0
  106. package/dist/storage/GCSStorageController.d.ts +43 -0
  107. package/dist/storage/LocalStorageController.d.ts +46 -0
  108. package/dist/storage/S3StorageController.d.ts +40 -0
  109. package/dist/storage/image-transform.d.ts +50 -0
  110. package/{src/storage/index.ts → dist/storage/index.d.ts} +6 -21
  111. package/dist/storage/routes.d.ts +70 -0
  112. package/dist/storage/storage-registry.d.ts +78 -0
  113. package/dist/storage/tus-handler.d.ts +53 -0
  114. package/{src/storage/types.ts → dist/storage/types.d.ts} +32 -38
  115. package/dist/types/index.d.ts +11 -0
  116. package/dist/utils/dev-port.d.ts +45 -0
  117. package/dist/utils/dynamic-import.d.ts +25 -0
  118. package/dist/utils/logger.d.ts +31 -0
  119. package/dist/utils/logging.d.ts +9 -0
  120. package/dist/utils/request-id.d.ts +4 -0
  121. package/dist/utils/request-logger.d.ts +19 -0
  122. package/{src/utils/sql.ts → dist/utils/sql.d.ts} +5 -16
  123. package/package.json +49 -22
  124. package/coverage/clover.xml +0 -3739
  125. package/coverage/coverage-final.json +0 -31
  126. package/coverage/lcov-report/base.css +0 -224
  127. package/coverage/lcov-report/block-navigation.js +0 -87
  128. package/coverage/lcov-report/favicon.png +0 -0
  129. package/coverage/lcov-report/index.html +0 -266
  130. package/coverage/lcov-report/prettify.css +0 -1
  131. package/coverage/lcov-report/prettify.js +0 -2
  132. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  133. package/coverage/lcov-report/sorter.js +0 -210
  134. package/coverage/lcov-report/src/api/ast-schema-editor.ts.html +0 -952
  135. package/coverage/lcov-report/src/api/errors.ts.html +0 -472
  136. package/coverage/lcov-report/src/api/graphql/graphql-schema-generator.ts.html +0 -1069
  137. package/coverage/lcov-report/src/api/graphql/index.html +0 -116
  138. package/coverage/lcov-report/src/api/index.html +0 -176
  139. package/coverage/lcov-report/src/api/openapi-generator.ts.html +0 -565
  140. package/coverage/lcov-report/src/api/rest/api-generator.ts.html +0 -994
  141. package/coverage/lcov-report/src/api/rest/index.html +0 -131
  142. package/coverage/lcov-report/src/api/rest/query-parser.ts.html +0 -550
  143. package/coverage/lcov-report/src/api/schema-editor-routes.ts.html +0 -202
  144. package/coverage/lcov-report/src/api/server.ts.html +0 -823
  145. package/coverage/lcov-report/src/auth/admin-routes.ts.html +0 -973
  146. package/coverage/lcov-report/src/auth/index.html +0 -176
  147. package/coverage/lcov-report/src/auth/jwt.ts.html +0 -574
  148. package/coverage/lcov-report/src/auth/middleware.ts.html +0 -745
  149. package/coverage/lcov-report/src/auth/password.ts.html +0 -310
  150. package/coverage/lcov-report/src/auth/services.ts.html +0 -2074
  151. package/coverage/lcov-report/src/collections/index.html +0 -116
  152. package/coverage/lcov-report/src/collections/loader.ts.html +0 -232
  153. package/coverage/lcov-report/src/db/auth-schema.ts.html +0 -523
  154. package/coverage/lcov-report/src/db/data-transformer.ts.html +0 -1753
  155. package/coverage/lcov-report/src/db/entityService.ts.html +0 -700
  156. package/coverage/lcov-report/src/db/index.html +0 -146
  157. package/coverage/lcov-report/src/db/services/EntityFetchService.ts.html +0 -4048
  158. package/coverage/lcov-report/src/db/services/EntityPersistService.ts.html +0 -883
  159. package/coverage/lcov-report/src/db/services/RelationService.ts.html +0 -3121
  160. package/coverage/lcov-report/src/db/services/entity-helpers.ts.html +0 -442
  161. package/coverage/lcov-report/src/db/services/index.html +0 -176
  162. package/coverage/lcov-report/src/db/services/index.ts.html +0 -124
  163. package/coverage/lcov-report/src/generate-drizzle-schema-logic.ts.html +0 -1960
  164. package/coverage/lcov-report/src/index.html +0 -116
  165. package/coverage/lcov-report/src/services/driver-registry.ts.html +0 -631
  166. package/coverage/lcov-report/src/services/index.html +0 -131
  167. package/coverage/lcov-report/src/services/postgresDataDriver.ts.html +0 -3025
  168. package/coverage/lcov-report/src/storage/LocalStorageController.ts.html +0 -1189
  169. package/coverage/lcov-report/src/storage/S3StorageController.ts.html +0 -970
  170. package/coverage/lcov-report/src/storage/index.html +0 -161
  171. package/coverage/lcov-report/src/storage/storage-registry.ts.html +0 -646
  172. package/coverage/lcov-report/src/storage/types.ts.html +0 -451
  173. package/coverage/lcov-report/src/utils/drizzle-conditions.ts.html +0 -3082
  174. package/coverage/lcov-report/src/utils/index.html +0 -116
  175. package/coverage/lcov.info +0 -7179
  176. package/jest.config.cjs +0 -19
  177. package/src/api/ast-schema-editor.ts +0 -289
  178. package/src/api/collections_for_test/callbacks_test_collection.ts +0 -61
  179. package/src/api/errors.ts +0 -278
  180. package/src/api/graphql/graphql-schema-generator.ts +0 -383
  181. package/src/api/graphql/index.ts +0 -2
  182. package/src/api/index.ts +0 -11
  183. package/src/api/logs-routes.ts +0 -132
  184. package/src/api/openapi-generator.ts +0 -727
  185. package/src/api/rest/api-generator-count.test.ts +0 -126
  186. package/src/api/rest/api-generator.ts +0 -747
  187. package/src/api/rest/query-parser.ts +0 -300
  188. package/src/api/schema-editor-routes.ts +0 -41
  189. package/src/api/server.ts +0 -271
  190. package/src/api/types.ts +0 -104
  191. package/src/auth/adapter-middleware.ts +0 -114
  192. package/src/auth/admin-roles-route.ts +0 -36
  193. package/src/auth/admin-user-ops.ts +0 -236
  194. package/src/auth/admin-users-route.ts +0 -302
  195. package/src/auth/api-keys/api-key-middleware.ts +0 -133
  196. package/src/auth/api-keys/api-key-routes.ts +0 -193
  197. package/src/auth/api-keys/api-key-store.ts +0 -340
  198. package/src/auth/apple-oauth.ts +0 -122
  199. package/src/auth/bitbucket-oauth.ts +0 -84
  200. package/src/auth/builtin-auth-adapter.ts +0 -380
  201. package/src/auth/custom-auth-adapter.ts +0 -86
  202. package/src/auth/discord-oauth.ts +0 -85
  203. package/src/auth/facebook-oauth.ts +0 -74
  204. package/src/auth/github-oauth.ts +0 -112
  205. package/src/auth/gitlab-oauth.ts +0 -72
  206. package/src/auth/google-oauth.ts +0 -223
  207. package/src/auth/jwt.ts +0 -208
  208. package/src/auth/linkedin-oauth.ts +0 -83
  209. package/src/auth/magic-link-routes.ts +0 -167
  210. package/src/auth/mfa-crypto.ts +0 -91
  211. package/src/auth/mfa-routes.ts +0 -323
  212. package/src/auth/mfa.ts +0 -161
  213. package/src/auth/microsoft-oauth.ts +0 -90
  214. package/src/auth/middleware.ts +0 -369
  215. package/src/auth/password.ts +0 -77
  216. package/src/auth/rate-limiter.ts +0 -225
  217. package/src/auth/reset-password-admin.ts +0 -160
  218. package/src/auth/rls-scope.ts +0 -58
  219. package/src/auth/routes.ts +0 -802
  220. package/src/auth/session-routes.ts +0 -353
  221. package/src/auth/slack-oauth.ts +0 -73
  222. package/src/auth/spotify-oauth.ts +0 -69
  223. package/src/auth/twitter-oauth.ts +0 -128
  224. package/src/collections/BackendCollectionRegistry.ts +0 -20
  225. package/src/collections/loader.ts +0 -50
  226. package/src/cron/cron-loader.ts +0 -84
  227. package/src/cron/cron-routes.test.ts +0 -265
  228. package/src/cron/cron-routes.ts +0 -85
  229. package/src/cron/cron-scheduler.test.ts +0 -559
  230. package/src/cron/cron-scheduler.ts +0 -581
  231. package/src/cron/cron-store.ts +0 -166
  232. package/src/db/interfaces.ts +0 -60
  233. package/src/email/index.ts +0 -19
  234. package/src/email/smtp-email-service.ts +0 -123
  235. package/src/email/templates.ts +0 -470
  236. package/src/env.ts +0 -227
  237. package/src/functions/function-loader.ts +0 -116
  238. package/src/functions/function-routes.ts +0 -31
  239. package/src/history/history-routes.ts +0 -129
  240. package/src/index.ts +0 -70
  241. package/src/init/docs.ts +0 -47
  242. package/src/init/health.ts +0 -37
  243. package/src/init/middlewares.ts +0 -61
  244. package/src/init/shutdown.ts +0 -56
  245. package/src/init/storage.ts +0 -57
  246. package/src/init.ts +0 -1041
  247. package/src/serve-spa.ts +0 -89
  248. package/src/services/driver-registry.ts +0 -183
  249. package/src/services/routed-realtime-service.ts +0 -113
  250. package/src/services/webhook-service.ts +0 -155
  251. package/src/singleton.ts +0 -70
  252. package/src/storage/LocalStorageController.ts +0 -389
  253. package/src/storage/S3StorageController.ts +0 -300
  254. package/src/storage/image-transform.ts +0 -218
  255. package/src/storage/routes.ts +0 -368
  256. package/src/storage/storage-registry.ts +0 -188
  257. package/src/storage/tus-handler.ts +0 -315
  258. package/src/types/index.ts +0 -27
  259. package/src/utils/dev-port.ts +0 -196
  260. package/src/utils/logger.ts +0 -143
  261. package/src/utils/logging.ts +0 -40
  262. package/src/utils/request-id.ts +0 -40
  263. package/src/utils/request-logger.ts +0 -72
  264. package/test/api-generator.test.ts +0 -588
  265. package/test/api-key-permission-guard.test.ts +0 -144
  266. package/test/ast-schema-editor.test.ts +0 -89
  267. package/test/auth-middleware-hono.test.ts +0 -556
  268. package/test/auth-routes.test.ts +0 -1098
  269. package/test/backend-hooks-data.test.ts +0 -477
  270. package/test/custom-auth-adapter.test.ts +0 -189
  271. package/test/driver-registry.test.ts +0 -282
  272. package/test/email-templates.test.ts +0 -174
  273. package/test/env.test.ts +0 -128
  274. package/test/error-propagation.test.ts +0 -226
  275. package/test/errors-hono.test.ts +0 -133
  276. package/test/errors.test.ts +0 -155
  277. package/test/function-loader.test.ts +0 -127
  278. package/test/graphql-schema-generator.test.ts +0 -554
  279. package/test/jwt-security.test.ts +0 -182
  280. package/test/jwt.test.ts +0 -326
  281. package/test/mfa.test.ts +0 -197
  282. package/test/middleware.test.ts +0 -305
  283. package/test/multi-datasource-routing.test.ts +0 -113
  284. package/test/password.test.ts +0 -165
  285. package/test/query-parser.test.ts +0 -233
  286. package/test/rate-limiter.test.ts +0 -102
  287. package/test/reset-password-admin.test.ts +0 -113
  288. package/test/routed-realtime-service.test.ts +0 -86
  289. package/test/safe-compare.test.ts +0 -66
  290. package/test/singleton.test.ts +0 -59
  291. package/test/smtp-email-service.test.ts +0 -169
  292. package/test/storage-local.test.ts +0 -271
  293. package/test/storage-registry.test.ts +0 -282
  294. package/test/storage-routes.test.ts +0 -222
  295. package/test/storage-s3.test.ts +0 -304
  296. package/test/transform-auth-response.test.ts +0 -305
  297. package/test/webhook-service.test.ts +0 -260
  298. package/tsconfig.json +0 -54
  299. package/tsconfig.prod.json +0 -23
  300. package/vite.config.ts +0 -86
@@ -1,182 +0,0 @@
1
- import {
2
- configureJwt,
3
- generateAccessToken,
4
- verifyAccessToken,
5
- generateRefreshToken,
6
- hashRefreshToken,
7
- getAccessTokenExpiryMs,
8
- getRefreshTokenExpiry
9
- } from "../src/auth/jwt";
10
-
11
- const STRONG_SECRET = "this-is-a-strong-secret-for-jwt-testing-at-least-32-chars-long";
12
-
13
- describe("JWT Security Hardening", () => {
14
-
15
- beforeEach(() => {
16
- configureJwt({ secret: STRONG_SECRET,
17
- accessExpiresIn: "1h",
18
- refreshExpiresIn: "30d" });
19
- });
20
-
21
- // ── Secret validation ───────────────────────────────────
22
- describe("configureJwt secret validation", () => {
23
- it("rejects secrets shorter than 32 characters", () => {
24
- expect(() => configureJwt({ secret: "short" })).toThrow("too short");
25
- });
26
-
27
- it("rejects empty secret", () => {
28
- expect(() => configureJwt({ secret: "" })).toThrow("too short");
29
- });
30
-
31
- it("rejects known weak secrets", () => {
32
- expect(() => configureJwt({ secret: "your-super-secret-jwt-key-change-in-production" })).toThrow("weak");
33
- });
34
-
35
- it("rejects 'changeme' and variations", () => {
36
- expect(() => configureJwt({ secret: "changeme-padding-for-32-chars!!!" })).not.toThrow();
37
- expect(() => configureJwt({ secret: "changeme" })).toThrow("too short");
38
- });
39
-
40
- it("accepts strong, random secrets", () => {
41
- expect(() => configureJwt({
42
- secret: "aG7x!kL2$mP9#qR5+tU8*wZ0^bD3&fH6"
43
- })).not.toThrow();
44
- });
45
- });
46
-
47
- // ── Token generation ────────────────────────────────────
48
- describe("token generation", () => {
49
- it("generates valid JWT with 3 parts", () => {
50
- const token = generateAccessToken("user-1", ["admin"]);
51
- expect(token.split(".")).toHaveLength(3);
52
- });
53
-
54
- it("embeds userId and roles in payload", () => {
55
- const token = generateAccessToken("user-42", ["admin", "editor"]);
56
- const payload = verifyAccessToken(token);
57
- expect(payload?.userId).toBe("user-42");
58
- expect(payload?.roles).toEqual(["admin", "editor"]);
59
- });
60
-
61
- it("generates different tokens for different users", () => {
62
- const t1 = generateAccessToken("user-1", ["admin"]);
63
- const t2 = generateAccessToken("user-2", ["admin"]);
64
- expect(t1).not.toBe(t2);
65
- });
66
-
67
- it("throws when secret is not configured", () => {
68
- // Force empty secret
69
-
70
- Object.defineProperty(require("../src/auth/jwt"), "jwtConfig", { value: { secret: "" },
71
- writable: true });
72
- // This won't work since jwtConfig is module-scoped, but generateAccessToken has its own check
73
- // We'll test via configureJwt + clearing
74
- });
75
- });
76
-
77
- // ── Token verification ──────────────────────────────────
78
- describe("token verification", () => {
79
- it("verifies a valid token", () => {
80
- const token = generateAccessToken("user-1", ["editor"]);
81
- const payload = verifyAccessToken(token);
82
- expect(payload).not.toBeNull();
83
- expect(payload!.userId).toBe("user-1");
84
- });
85
-
86
- it("returns null for tampered token", () => {
87
- const token = generateAccessToken("user-1", ["admin"]);
88
- const tampered = token.slice(0, -5) + "XXXXX";
89
- expect(verifyAccessToken(tampered)).toBeNull();
90
- });
91
-
92
- it("returns null for garbage string", () => {
93
- expect(verifyAccessToken("not.a.jwt")).toBeNull();
94
- });
95
-
96
- it("returns null for empty string", () => {
97
- expect(verifyAccessToken("")).toBeNull();
98
- });
99
-
100
- it("returns null for token signed with different secret", () => {
101
- const token = generateAccessToken("user-1", ["admin"]);
102
- // Reconfigure with different secret
103
- configureJwt({ secret: "another-secret-that-is-at-least-32-chars-long-for-test" });
104
- expect(verifyAccessToken(token)).toBeNull();
105
- // Reset
106
- configureJwt({ secret: STRONG_SECRET });
107
- });
108
-
109
- it("extracts roles as array", () => {
110
- const token = generateAccessToken("u", ["admin", "editor", "viewer"]);
111
- const payload = verifyAccessToken(token);
112
- expect(payload!.roles).toEqual(["admin", "editor", "viewer"]);
113
- });
114
-
115
- it("handles empty roles array", () => {
116
- const token = generateAccessToken("u", []);
117
- const payload = verifyAccessToken(token);
118
- expect(payload!.roles).toEqual([]);
119
- });
120
- });
121
-
122
- // ── Refresh tokens ──────────────────────────────────────
123
- describe("refresh tokens", () => {
124
- it("generates random hex strings", () => {
125
- const t1 = generateRefreshToken();
126
- const t2 = generateRefreshToken();
127
- expect(t1).not.toBe(t2);
128
- expect(t1.length).toBe(80); // 40 bytes in hex
129
- });
130
-
131
- it("hashes deterministically (SHA-256)", () => {
132
- const token = "test-refresh-token";
133
- const h1 = hashRefreshToken(token);
134
- const h2 = hashRefreshToken(token);
135
- expect(h1).toBe(h2);
136
- expect(h1.length).toBe(64); // SHA-256 hex
137
- });
138
-
139
- it("different tokens produce different hashes", () => {
140
- const h1 = hashRefreshToken("token-a");
141
- const h2 = hashRefreshToken("token-b");
142
- expect(h1).not.toBe(h2);
143
- });
144
- });
145
-
146
- // ── Expiry calculations ─────────────────────────────────
147
- describe("expiry calculations", () => {
148
- it("calculates 1h as 3600000ms", () => {
149
- configureJwt({ secret: STRONG_SECRET,
150
- accessExpiresIn: "1h" });
151
- expect(getAccessTokenExpiryMs()).toBe(3600000);
152
- });
153
-
154
- it("calculates 30m as 1800000ms", () => {
155
- configureJwt({ secret: STRONG_SECRET,
156
- accessExpiresIn: "30m" });
157
- expect(getAccessTokenExpiryMs()).toBe(1800000);
158
- });
159
-
160
- it("calculates 7d correctly", () => {
161
- configureJwt({ secret: STRONG_SECRET,
162
- accessExpiresIn: "7d" });
163
- expect(getAccessTokenExpiryMs()).toBe(7 * 24 * 60 * 60 * 1000);
164
- });
165
-
166
- it("defaults to 1h for unparseable duration", () => {
167
- configureJwt({ secret: STRONG_SECRET,
168
- accessExpiresIn: "invalid" });
169
- expect(getAccessTokenExpiryMs()).toBe(3600000);
170
- });
171
-
172
- it("refresh expiry is in the future", () => {
173
- configureJwt({ secret: STRONG_SECRET,
174
- refreshExpiresIn: "30d" });
175
- const expiry = getRefreshTokenExpiry();
176
- expect(expiry.getTime()).toBeGreaterThan(Date.now());
177
- // Should be approximately 30 days in the future
178
- const thirtyDays = 30 * 24 * 60 * 60 * 1000;
179
- expect(expiry.getTime() - Date.now()).toBeCloseTo(thirtyDays, -4);
180
- });
181
- });
182
- });
package/test/jwt.test.ts DELETED
@@ -1,326 +0,0 @@
1
- import {
2
- configureJwt,
3
- generateAccessToken,
4
- verifyAccessToken,
5
- generateRefreshToken,
6
- hashRefreshToken,
7
- getRefreshTokenExpiry,
8
- getAccessTokenExpiryMs,
9
- getAccessTokenExpiry
10
- } from "../src/auth/jwt";
11
-
12
- describe("JWT Utilities", () => {
13
- const testSecret = "test-secret-key-for-jwt-testing-1234567890";
14
-
15
- beforeEach(() => {
16
- // Reset JWT config before each test
17
- configureJwt({
18
- secret: testSecret,
19
- accessExpiresIn: "1h",
20
- refreshExpiresIn: "30d"
21
- });
22
- });
23
-
24
- describe("configureJwt", () => {
25
- it("should configure JWT with provided secret", () => {
26
- configureJwt({ secret: "new-secret-key-that-is-at-least-32-chars" });
27
- // Configuration is internal, but we can verify it works by generating a token
28
- expect(() => generateAccessToken("user-1", ["admin"])).not.toThrow();
29
- });
30
-
31
- it("should allow partial configuration updates", () => {
32
- configureJwt({ secret: testSecret,
33
- accessExpiresIn: "2h" });
34
- // Token generation should still work
35
- const token = generateAccessToken("user-1", ["admin"]);
36
- expect(token).toBeTruthy();
37
- });
38
- });
39
-
40
- describe("generateAccessToken", () => {
41
- it("should generate a valid JWT token", () => {
42
- const token = generateAccessToken("user-123", ["admin", "editor"]);
43
- expect(token).toBeTruthy();
44
- expect(typeof token).toBe("string");
45
- // JWT tokens have 3 parts separated by dots
46
- expect(token.split(".")).toHaveLength(3);
47
- });
48
-
49
- it("should throw error if secret is empty", () => {
50
- expect(() => configureJwt({ secret: "" }))
51
- .toThrow("JWT secret is too short");
52
- });
53
-
54
- it("should include userId and roles in payload", () => {
55
- const token = generateAccessToken("user-456", ["viewer"]);
56
- const payload = verifyAccessToken(token);
57
- expect(payload).toEqual({
58
- userId: "user-456",
59
- roles: ["viewer"],
60
- aal: "aal1"
61
- });
62
- });
63
-
64
- it("should handle empty roles array", () => {
65
- const token = generateAccessToken("user-789", []);
66
- const payload = verifyAccessToken(token);
67
- expect(payload?.roles).toEqual([]);
68
- });
69
- });
70
-
71
- describe("verifyAccessToken", () => {
72
- it("should verify and decode a valid token", () => {
73
- const token = generateAccessToken("user-123", ["admin"]);
74
- const payload = verifyAccessToken(token);
75
- expect(payload).toEqual({
76
- userId: "user-123",
77
- roles: ["admin"],
78
- aal: "aal1"
79
- });
80
- });
81
-
82
- it("should return null for invalid token", () => {
83
- const payload = verifyAccessToken("invalid-token");
84
- expect(payload).toBeNull();
85
- });
86
-
87
- it("should return null for token signed with different secret", () => {
88
- const token = generateAccessToken("user-123", ["admin"]);
89
- configureJwt({ secret: "different-secret-that-is-at-least-32-chars-long" });
90
- const payload = verifyAccessToken(token);
91
- expect(payload).toBeNull();
92
- });
93
-
94
- it("should return null for malformed JWT", () => {
95
- const payload = verifyAccessToken("not.a.valid.jwt.token");
96
- expect(payload).toBeNull();
97
- });
98
-
99
- it("should throw error if secret is empty", () => {
100
- expect(() => configureJwt({ secret: "" }))
101
- .toThrow("JWT secret is too short");
102
- });
103
- });
104
-
105
- describe("generateRefreshToken", () => {
106
- it("should generate a random token", () => {
107
- const token = generateRefreshToken();
108
- expect(token).toBeTruthy();
109
- expect(typeof token).toBe("string");
110
- // 40 random bytes = 80 hex characters
111
- expect(token).toHaveLength(80);
112
- });
113
-
114
- it("should generate unique tokens each time", () => {
115
- const token1 = generateRefreshToken();
116
- const token2 = generateRefreshToken();
117
- expect(token1).not.toBe(token2);
118
- });
119
- });
120
-
121
- describe("hashRefreshToken", () => {
122
- it("should hash a token consistently", () => {
123
- const token = "test-refresh-token";
124
- const hash1 = hashRefreshToken(token);
125
- const hash2 = hashRefreshToken(token);
126
- expect(hash1).toBe(hash2);
127
- });
128
-
129
- it("should produce different hashes for different tokens", () => {
130
- const hash1 = hashRefreshToken("token1");
131
- const hash2 = hashRefreshToken("token2");
132
- expect(hash1).not.toBe(hash2);
133
- });
134
-
135
- it("should return a SHA256 hash (64 hex characters)", () => {
136
- const hash = hashRefreshToken("any-token");
137
- expect(hash).toHaveLength(64);
138
- expect(/^[a-f0-9]+$/.test(hash)).toBe(true);
139
- });
140
- });
141
-
142
- describe("getAccessTokenExpiryMs", () => {
143
- it("should return correct milliseconds for hours", () => {
144
- configureJwt({ secret: testSecret,
145
- accessExpiresIn: "2h" });
146
- expect(getAccessTokenExpiryMs()).toBe(2 * 60 * 60 * 1000);
147
- });
148
-
149
- it("should return correct milliseconds for days", () => {
150
- configureJwt({ secret: testSecret,
151
- accessExpiresIn: "7d" });
152
- expect(getAccessTokenExpiryMs()).toBe(7 * 24 * 60 * 60 * 1000);
153
- });
154
-
155
- it("should return correct milliseconds for minutes", () => {
156
- configureJwt({ secret: testSecret,
157
- accessExpiresIn: "30m" });
158
- expect(getAccessTokenExpiryMs()).toBe(30 * 60 * 1000);
159
- });
160
-
161
- it("should return correct milliseconds for seconds", () => {
162
- configureJwt({ secret: testSecret,
163
- accessExpiresIn: "300s" });
164
- expect(getAccessTokenExpiryMs()).toBe(300 * 1000);
165
- });
166
-
167
- it("should default to 1 hour for invalid format", () => {
168
- configureJwt({ secret: testSecret,
169
- accessExpiresIn: "invalid" });
170
- expect(getAccessTokenExpiryMs()).toBe(60 * 60 * 1000);
171
- });
172
- });
173
-
174
- describe("getAccessTokenExpiry", () => {
175
- it("should return a timestamp in the future", () => {
176
- const now = Date.now();
177
- const expiry = getAccessTokenExpiry();
178
- expect(expiry).toBeGreaterThan(now);
179
- });
180
-
181
- it("should match the configured expiry duration", () => {
182
- configureJwt({ secret: testSecret,
183
- accessExpiresIn: "1h" });
184
- const now = Date.now();
185
- const expiry = getAccessTokenExpiry();
186
- // Should be approximately 1 hour from now (with small tolerance)
187
- const expectedExpiry = now + (60 * 60 * 1000);
188
- expect(expiry).toBeGreaterThanOrEqual(expectedExpiry - 1000);
189
- expect(expiry).toBeLessThanOrEqual(expectedExpiry + 1000);
190
- });
191
- });
192
-
193
- describe("getRefreshTokenExpiry", () => {
194
- it("should return a Date in the future", () => {
195
- const expiry = getRefreshTokenExpiry();
196
- expect(expiry).toBeInstanceOf(Date);
197
- expect(expiry.getTime()).toBeGreaterThan(Date.now());
198
- });
199
-
200
- it("should return approximately 30 days from now by default", () => {
201
- const expiry = getRefreshTokenExpiry();
202
- const expected = Date.now() + (30 * 24 * 60 * 60 * 1000);
203
- // Allow 1 second tolerance
204
- expect(expiry.getTime()).toBeGreaterThanOrEqual(expected - 1000);
205
- expect(expiry.getTime()).toBeLessThanOrEqual(expected + 1000);
206
- });
207
-
208
- it("should respect custom refresh expiry configuration", () => {
209
- configureJwt({ secret: testSecret,
210
- refreshExpiresIn: "7d" });
211
- const expiry = getRefreshTokenExpiry();
212
- const expected = Date.now() + (7 * 24 * 60 * 60 * 1000);
213
- expect(expiry.getTime()).toBeGreaterThanOrEqual(expected - 1000);
214
- expect(expiry.getTime()).toBeLessThanOrEqual(expected + 1000);
215
- });
216
-
217
- it("should handle hour-based refresh expiry", () => {
218
- configureJwt({ secret: testSecret,
219
- refreshExpiresIn: "24h" });
220
- const expiry = getRefreshTokenExpiry();
221
- const expected = Date.now() + (24 * 60 * 60 * 1000);
222
- expect(expiry.getTime()).toBeGreaterThanOrEqual(expected - 1000);
223
- expect(expiry.getTime()).toBeLessThanOrEqual(expected + 1000);
224
- });
225
-
226
- it("should handle minute-based refresh expiry", () => {
227
- configureJwt({ secret: testSecret,
228
- refreshExpiresIn: "90m" });
229
- const expiry = getRefreshTokenExpiry();
230
- const expected = Date.now() + (90 * 60 * 1000);
231
- expect(expiry.getTime()).toBeGreaterThanOrEqual(expected - 1000);
232
- expect(expiry.getTime()).toBeLessThanOrEqual(expected + 1000);
233
- });
234
-
235
- it("should handle second-based refresh expiry", () => {
236
- configureJwt({ secret: testSecret,
237
- refreshExpiresIn: "3600s" });
238
- const expiry = getRefreshTokenExpiry();
239
- const expected = Date.now() + (3600 * 1000);
240
- expect(expiry.getTime()).toBeGreaterThanOrEqual(expected - 1000);
241
- expect(expiry.getTime()).toBeLessThanOrEqual(expected + 1000);
242
- });
243
-
244
- it("should default to 30 days for invalid refresh format", () => {
245
- configureJwt({ secret: testSecret,
246
- refreshExpiresIn: "invalid" });
247
- const expiry = getRefreshTokenExpiry();
248
- const expected = Date.now() + (30 * 24 * 60 * 60 * 1000);
249
- expect(expiry.getTime()).toBeGreaterThanOrEqual(expected - 1000);
250
- expect(expiry.getTime()).toBeLessThanOrEqual(expected + 1000);
251
- });
252
- });
253
-
254
- // ── Weak secret rejection ────────────────────────────────
255
- describe("configureJwt — weak secret rejection", () => {
256
- it("should reject known weak secret 'secret'", () => {
257
- expect(() => configureJwt({ secret: "secret".padEnd(32, "x") })).not.toThrow();
258
- // But the actual word "secret" is too short AND is a known weak value
259
- expect(() => configureJwt({ secret: "secret" })).toThrow("JWT secret is too short");
260
- });
261
-
262
- it("should reject known weak secrets like 'changeme'", () => {
263
- // 'changeme' is only 8 chars, fails the length check first
264
- expect(() => configureJwt({ secret: "changeme" })).toThrow("JWT secret is too short");
265
- });
266
-
267
- it("should reject secret that is exactly 31 characters", () => {
268
- const shortSecret = "a".repeat(31);
269
- expect(() => configureJwt({ secret: shortSecret })).toThrow("JWT secret is too short");
270
- });
271
-
272
- it("should accept secret that is exactly 32 characters", () => {
273
- const validSecret = "a".repeat(32);
274
- expect(() => configureJwt({ secret: validSecret })).not.toThrow();
275
- });
276
-
277
- it("should accept long randomly generated secrets", () => {
278
- const longSecret = "aB3dEfGhIjKlMnOpQrStUvWxYz012345678901234567890";
279
- expect(() => configureJwt({ secret: longSecret })).not.toThrow();
280
- });
281
- });
282
-
283
- // ── Expired token ────────────────────────────────────────
284
- describe("expired token handling", () => {
285
- it("should return null for an expired token", () => {
286
- // Configure with 1 second expiry
287
- configureJwt({ secret: testSecret,
288
- accessExpiresIn: "1s" });
289
- const token = generateAccessToken("user-1", ["admin"]);
290
-
291
- // Immediately verify should work
292
- const payload = verifyAccessToken(token);
293
- expect(payload).not.toBeNull();
294
-
295
- // We can't easily wait for expiry in a unit test,
296
- // but we can verify the token structure is correct
297
- expect(payload!.userId).toBe("user-1");
298
- expect(payload!.roles).toEqual(["admin"]);
299
- });
300
- });
301
-
302
- // ── Access token round-trip with various roles ────────────
303
- describe("access token round-trip", () => {
304
- it("should preserve multiple roles through encode/decode", () => {
305
- const roles = ["admin", "editor", "viewer", "moderator"];
306
- const token = generateAccessToken("user-multi", roles);
307
- const payload = verifyAccessToken(token);
308
- expect(payload!.userId).toBe("user-multi");
309
- expect(payload!.roles).toEqual(roles);
310
- });
311
-
312
- it("should handle special characters in userId", () => {
313
- const token = generateAccessToken("user@example.com", ["admin"]);
314
- const payload = verifyAccessToken(token);
315
- expect(payload!.userId).toBe("user@example.com");
316
- });
317
-
318
- it("should handle UUID-style userId", () => {
319
- const uuid = "550e8400-e29b-41d4-a716-446655440000";
320
- const token = generateAccessToken(uuid, []);
321
- const payload = verifyAccessToken(token);
322
- expect(payload!.userId).toBe(uuid);
323
- });
324
- });
325
- });
326
-