@tstdl/base 0.93.182 → 0.93.184

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 (353) hide show
  1. package/api/server/api-request-token.provider.js +1 -1
  2. package/api/server/gateway.js +6 -1
  3. package/authentication/authentication.api.d.ts +13 -40
  4. package/authentication/authentication.api.js +5 -14
  5. package/authentication/client/authentication.service.d.ts +6 -14
  6. package/authentication/client/authentication.service.js +22 -4
  7. package/authentication/client/module.d.ts +1 -1
  8. package/authentication/client/module.js +4 -4
  9. package/authentication/models/index.d.ts +1 -0
  10. package/authentication/models/index.js +1 -0
  11. package/authentication/models/totp-results.model.d.ts +11 -0
  12. package/authentication/models/totp-results.model.js +37 -0
  13. package/authentication/server/authentication.api-controller.d.ts +3 -3
  14. package/authentication/server/authentication.api-controller.js +31 -4
  15. package/authentication/server/authentication.service.d.ts +5 -14
  16. package/authentication/server/authentication.service.js +6 -4
  17. package/core.d.ts +0 -5
  18. package/core.js +0 -8
  19. package/document-management/api/document-management.api.d.ts +2 -2
  20. package/document-management/service-models/document.service-model.d.ts +1 -1
  21. package/examples/config.d.ts +25 -0
  22. package/examples/config.js +26 -0
  23. package/notification/server/module.d.ts +1 -1
  24. package/notification/server/module.js +1 -1
  25. package/package.json +5 -5
  26. package/signals/api.d.ts +5 -1
  27. package/signals/api.js +3 -1
  28. package/signals/implementation/api.d.ts +10 -1
  29. package/signals/implementation/api.js +7 -1
  30. package/signals/implementation/asserts.d.ts +2 -2
  31. package/signals/implementation/asserts.js +3 -3
  32. package/signals/implementation/computed.d.ts +7 -34
  33. package/signals/implementation/computed.js +14 -83
  34. package/signals/implementation/configure.js +6 -2
  35. package/signals/implementation/effect.d.ts +65 -46
  36. package/signals/implementation/effect.js +97 -62
  37. package/signals/implementation/index.d.ts +2 -4
  38. package/signals/implementation/index.js +2 -4
  39. package/signals/implementation/linked_signal.d.ts +36 -0
  40. package/signals/implementation/linked_signal.js +34 -0
  41. package/signals/implementation/primitive/computed.d.ts +55 -0
  42. package/signals/implementation/primitive/computed.js +107 -0
  43. package/signals/implementation/primitive/effect.d.ts +26 -0
  44. package/signals/implementation/primitive/effect.js +31 -0
  45. package/signals/implementation/{equality.d.ts → primitive/equality.d.ts} +1 -1
  46. package/signals/implementation/{equality.js → primitive/equality.js} +1 -1
  47. package/signals/implementation/primitive/errors.d.ts +10 -0
  48. package/signals/implementation/{errors.js → primitive/errors.js} +3 -4
  49. package/signals/implementation/primitive/formatter.d.ts +19 -0
  50. package/signals/implementation/primitive/formatter.js +136 -0
  51. package/signals/implementation/{graph.d.ts → primitive/graph.d.ts} +68 -36
  52. package/signals/implementation/primitive/graph.js +386 -0
  53. package/signals/implementation/primitive/linked_signal.d.ts +46 -0
  54. package/signals/implementation/primitive/linked_signal.js +110 -0
  55. package/signals/implementation/primitive/signal.d.ts +31 -0
  56. package/signals/implementation/primitive/signal.js +80 -0
  57. package/signals/implementation/primitive/untracked.d.ts +12 -0
  58. package/signals/implementation/primitive/untracked.js +23 -0
  59. package/signals/implementation/{watch.d.ts → primitive/watch.d.ts} +1 -2
  60. package/signals/implementation/{watch.js → primitive/watch.js} +22 -16
  61. package/signals/implementation/resource/api.d.ts +275 -0
  62. package/signals/implementation/resource/api.js +26 -0
  63. package/signals/implementation/resource/debounce.d.ts +13 -0
  64. package/signals/implementation/resource/debounce.js +113 -0
  65. package/signals/implementation/resource/from_snapshots.d.ts +16 -0
  66. package/signals/implementation/resource/from_snapshots.js +44 -0
  67. package/signals/implementation/resource/index.d.ts +11 -0
  68. package/signals/implementation/resource/index.js +11 -0
  69. package/signals/implementation/resource/resource.d.ts +110 -0
  70. package/signals/implementation/resource/resource.js +402 -0
  71. package/signals/implementation/root_effect_scheduler.d.ts +50 -0
  72. package/signals/implementation/root_effect_scheduler.js +66 -0
  73. package/signals/implementation/signal.d.ts +42 -18
  74. package/signals/implementation/signal.js +29 -49
  75. package/signals/implementation/to-observable.d.ts +12 -5
  76. package/signals/implementation/to-observable.js +12 -2
  77. package/signals/implementation/to-signal.d.ts +9 -18
  78. package/signals/implementation/to-signal.js +46 -13
  79. package/signals/implementation/untracked.d.ts +1 -1
  80. package/signals/implementation/untracked.js +3 -11
  81. package/signals/operators/debounce.d.ts +8 -0
  82. package/signals/operators/debounce.js +19 -0
  83. package/signals/operators/derive-async.js +43 -15
  84. package/signals/operators/index.d.ts +2 -0
  85. package/signals/operators/index.js +2 -0
  86. package/signals/operators/throttle.d.ts +8 -0
  87. package/signals/operators/throttle.js +31 -0
  88. package/ai/genkit/tests/multi-region.test.d.ts +0 -2
  89. package/ai/genkit/tests/multi-region.test.js +0 -179
  90. package/ai/genkit/tests/token-limit-fallback.test.d.ts +0 -2
  91. package/ai/genkit/tests/token-limit-fallback.test.js +0 -209
  92. package/ai/prompts/tests/prompt-builder.test.d.ts +0 -1
  93. package/ai/prompts/tests/prompt-builder.test.js +0 -22
  94. package/ai/tests/instructions-formatter.test.d.ts +0 -1
  95. package/ai/tests/instructions-formatter.test.js +0 -116
  96. package/ai/tests/steering.test.d.ts +0 -1
  97. package/ai/tests/steering.test.js +0 -37
  98. package/api/client/tests/api-client.test.d.ts +0 -1
  99. package/api/client/tests/api-client.test.js +0 -194
  100. package/api/server/tests/csrf.middleware.test.d.ts +0 -1
  101. package/api/server/tests/csrf.middleware.test.js +0 -91
  102. package/authentication/tests/authentication-password-requirements.validator.test.d.ts +0 -1
  103. package/authentication/tests/authentication-password-requirements.validator.test.js +0 -29
  104. package/authentication/tests/authentication.api-controller.test.d.ts +0 -1
  105. package/authentication/tests/authentication.api-controller.test.js +0 -156
  106. package/authentication/tests/authentication.api-request-token.provider.test.d.ts +0 -1
  107. package/authentication/tests/authentication.api-request-token.provider.test.js +0 -48
  108. package/authentication/tests/authentication.client-error-handling.test.d.ts +0 -1
  109. package/authentication/tests/authentication.client-error-handling.test.js +0 -123
  110. package/authentication/tests/authentication.client-middleware.test.d.ts +0 -1
  111. package/authentication/tests/authentication.client-middleware.test.js +0 -118
  112. package/authentication/tests/authentication.client-service-methods.test.d.ts +0 -1
  113. package/authentication/tests/authentication.client-service-methods.test.js +0 -177
  114. package/authentication/tests/authentication.client-service-refresh.test.d.ts +0 -1
  115. package/authentication/tests/authentication.client-service-refresh.test.js +0 -153
  116. package/authentication/tests/authentication.client-service.test.d.ts +0 -1
  117. package/authentication/tests/authentication.client-service.test.js +0 -76
  118. package/authentication/tests/authentication.refresh-busy-loop.test.d.ts +0 -1
  119. package/authentication/tests/authentication.refresh-busy-loop.test.js +0 -84
  120. package/authentication/tests/authentication.service.test.d.ts +0 -1
  121. package/authentication/tests/authentication.service.test.js +0 -167
  122. package/authentication/tests/authentication.test-ancillary-service.d.ts +0 -9
  123. package/authentication/tests/authentication.test-ancillary-service.js +0 -27
  124. package/authentication/tests/brute-force-protection.test.d.ts +0 -1
  125. package/authentication/tests/brute-force-protection.test.js +0 -211
  126. package/authentication/tests/helper.test.d.ts +0 -1
  127. package/authentication/tests/helper.test.js +0 -122
  128. package/authentication/tests/password-requirements.error.test.d.ts +0 -1
  129. package/authentication/tests/password-requirements.error.test.js +0 -14
  130. package/authentication/tests/remember.api.test.d.ts +0 -1
  131. package/authentication/tests/remember.api.test.js +0 -117
  132. package/authentication/tests/remember.service.test.d.ts +0 -1
  133. package/authentication/tests/remember.service.test.js +0 -83
  134. package/authentication/tests/subject.service.test.d.ts +0 -1
  135. package/authentication/tests/subject.service.test.js +0 -140
  136. package/authentication/tests/suspended-subject.test.d.ts +0 -1
  137. package/authentication/tests/suspended-subject.test.js +0 -120
  138. package/authentication/tests/totp.enrollment.test.d.ts +0 -1
  139. package/authentication/tests/totp.enrollment.test.js +0 -123
  140. package/authentication/tests/totp.login.test.d.ts +0 -1
  141. package/authentication/tests/totp.login.test.js +0 -213
  142. package/authentication/tests/totp.recovery-codes.test.d.ts +0 -1
  143. package/authentication/tests/totp.recovery-codes.test.js +0 -97
  144. package/authentication/tests/totp.status.test.d.ts +0 -1
  145. package/authentication/tests/totp.status.test.js +0 -72
  146. package/cancellation/tests/coverage.test.d.ts +0 -1
  147. package/cancellation/tests/coverage.test.js +0 -49
  148. package/cancellation/tests/leak.test.d.ts +0 -1
  149. package/cancellation/tests/leak.test.js +0 -35
  150. package/cancellation/tests/token.test.d.ts +0 -1
  151. package/cancellation/tests/token.test.js +0 -136
  152. package/circuit-breaker/tests/circuit-breaker.test.d.ts +0 -1
  153. package/circuit-breaker/tests/circuit-breaker.test.js +0 -116
  154. package/cryptography/tests/cryptography.test.d.ts +0 -1
  155. package/cryptography/tests/cryptography.test.js +0 -175
  156. package/cryptography/tests/jwt.test.d.ts +0 -1
  157. package/cryptography/tests/jwt.test.js +0 -54
  158. package/cryptography/tests/modern.test.d.ts +0 -1
  159. package/cryptography/tests/modern.test.js +0 -105
  160. package/cryptography/tests/module.test.d.ts +0 -1
  161. package/cryptography/tests/module.test.js +0 -100
  162. package/cryptography/tests/totp.test.d.ts +0 -1
  163. package/cryptography/tests/totp.test.js +0 -108
  164. package/document-management/tests/ai-config-hierarchy.test.d.ts +0 -1
  165. package/document-management/tests/ai-config-hierarchy.test.js +0 -59
  166. package/document-management/tests/ai-config-integration.test.d.ts +0 -1
  167. package/document-management/tests/ai-config-integration.test.js +0 -125
  168. package/document-management/tests/ai-config-merge.test.d.ts +0 -1
  169. package/document-management/tests/ai-config-merge.test.js +0 -46
  170. package/document-management/tests/document-management-ai-overrides.test.d.ts +0 -1
  171. package/document-management/tests/document-management-ai-overrides.test.js +0 -63
  172. package/document-management/tests/document-management-core.test.d.ts +0 -1
  173. package/document-management/tests/document-management-core.test.js +0 -157
  174. package/document-management/tests/document-management.api.test.d.ts +0 -1
  175. package/document-management/tests/document-management.api.test.js +0 -101
  176. package/document-management/tests/document-statistics.service.test.d.ts +0 -1
  177. package/document-management/tests/document-statistics.service.test.js +0 -498
  178. package/document-management/tests/document-validation-ai-overrides.test.d.ts +0 -1
  179. package/document-management/tests/document-validation-ai-overrides.test.js +0 -87
  180. package/document-management/tests/document.service.test.d.ts +0 -1
  181. package/document-management/tests/document.service.test.js +0 -143
  182. package/document-management/tests/enum-helpers.test.d.ts +0 -1
  183. package/document-management/tests/enum-helpers.test.js +0 -452
  184. package/document-management/tests/helper.d.ts +0 -24
  185. package/document-management/tests/helper.js +0 -39
  186. package/errors/tests/format.test.d.ts +0 -1
  187. package/errors/tests/format.test.js +0 -84
  188. package/http/tests/server-timing.test.d.ts +0 -1
  189. package/http/tests/server-timing.test.js +0 -42
  190. package/injector/tests/advanced.test.d.ts +0 -1
  191. package/injector/tests/advanced.test.js +0 -116
  192. package/injector/tests/async-init.test.d.ts +0 -1
  193. package/injector/tests/async-init.test.js +0 -77
  194. package/injector/tests/basic.test.d.ts +0 -1
  195. package/injector/tests/basic.test.js +0 -114
  196. package/injector/tests/hierarchical.test.d.ts +0 -1
  197. package/injector/tests/hierarchical.test.js +0 -59
  198. package/injector/tests/leak.test.d.ts +0 -1
  199. package/injector/tests/leak.test.js +0 -45
  200. package/injector/tests/lifecycles.test.d.ts +0 -1
  201. package/injector/tests/lifecycles.test.js +0 -109
  202. package/logger/tests/pretty-print.test.d.ts +0 -1
  203. package/logger/tests/pretty-print.test.js +0 -60
  204. package/notification/tests/notification-api.test.d.ts +0 -1
  205. package/notification/tests/notification-api.test.js +0 -124
  206. package/notification/tests/notification-client.test.d.ts +0 -1
  207. package/notification/tests/notification-client.test.js +0 -101
  208. package/notification/tests/notification-flow.test.d.ts +0 -1
  209. package/notification/tests/notification-flow.test.js +0 -296
  210. package/notification/tests/notification-sse.service.test.d.ts +0 -1
  211. package/notification/tests/notification-sse.service.test.js +0 -43
  212. package/notification/tests/notification-type.service.test.d.ts +0 -1
  213. package/notification/tests/notification-type.service.test.js +0 -41
  214. package/object-storage/s3/tests/s3.object-storage.integration.test.d.ts +0 -1
  215. package/object-storage/s3/tests/s3.object-storage.integration.test.js +0 -303
  216. package/orm/tests/build-jsonb.test.d.ts +0 -1
  217. package/orm/tests/build-jsonb.test.js +0 -39
  218. package/orm/tests/data-types.test.d.ts +0 -1
  219. package/orm/tests/data-types.test.js +0 -39
  220. package/orm/tests/database-extension.test.d.ts +0 -1
  221. package/orm/tests/database-extension.test.js +0 -63
  222. package/orm/tests/database-migration.test.d.ts +0 -1
  223. package/orm/tests/database-migration.test.js +0 -83
  224. package/orm/tests/decorators.test.d.ts +0 -1
  225. package/orm/tests/decorators.test.js +0 -77
  226. package/orm/tests/encryption.test.d.ts +0 -1
  227. package/orm/tests/encryption.test.js +0 -31
  228. package/orm/tests/query-complex.test.d.ts +0 -1
  229. package/orm/tests/query-complex.test.js +0 -172
  230. package/orm/tests/query-converter-complex.test.d.ts +0 -1
  231. package/orm/tests/query-converter-complex.test.js +0 -131
  232. package/orm/tests/query-converter.test.d.ts +0 -1
  233. package/orm/tests/query-converter.test.js +0 -123
  234. package/orm/tests/repository-advanced.test.d.ts +0 -1
  235. package/orm/tests/repository-advanced.test.js +0 -189
  236. package/orm/tests/repository-attributes.test.d.ts +0 -1
  237. package/orm/tests/repository-attributes.test.js +0 -83
  238. package/orm/tests/repository-compound-primary-key.test.d.ts +0 -2
  239. package/orm/tests/repository-compound-primary-key.test.js +0 -226
  240. package/orm/tests/repository-comprehensive.test.d.ts +0 -1
  241. package/orm/tests/repository-comprehensive.test.js +0 -162
  242. package/orm/tests/repository-coverage.test.d.ts +0 -2
  243. package/orm/tests/repository-coverage.test.js +0 -242
  244. package/orm/tests/repository-cti-complex.test.d.ts +0 -1
  245. package/orm/tests/repository-cti-complex.test.js +0 -151
  246. package/orm/tests/repository-cti-embedded.test.d.ts +0 -1
  247. package/orm/tests/repository-cti-embedded.test.js +0 -178
  248. package/orm/tests/repository-cti-extensive.test.d.ts +0 -2
  249. package/orm/tests/repository-cti-extensive.test.js +0 -279
  250. package/orm/tests/repository-cti-mapping.test.d.ts +0 -2
  251. package/orm/tests/repository-cti-mapping.test.js +0 -108
  252. package/orm/tests/repository-cti-search.test.d.ts +0 -1
  253. package/orm/tests/repository-cti-search.test.js +0 -141
  254. package/orm/tests/repository-cti-soft-delete.test.d.ts +0 -2
  255. package/orm/tests/repository-cti-soft-delete.test.js +0 -103
  256. package/orm/tests/repository-cti-transactions.test.d.ts +0 -1
  257. package/orm/tests/repository-cti-transactions.test.js +0 -112
  258. package/orm/tests/repository-cti-upsert-many.test.d.ts +0 -2
  259. package/orm/tests/repository-cti-upsert-many.test.js +0 -115
  260. package/orm/tests/repository-cti.test.d.ts +0 -2
  261. package/orm/tests/repository-cti.test.js +0 -390
  262. package/orm/tests/repository-edge-cases.test.d.ts +0 -1
  263. package/orm/tests/repository-edge-cases.test.js +0 -178
  264. package/orm/tests/repository-expiration.test.d.ts +0 -2
  265. package/orm/tests/repository-expiration.test.js +0 -140
  266. package/orm/tests/repository-extra-coverage.test.d.ts +0 -2
  267. package/orm/tests/repository-extra-coverage.test.js +0 -402
  268. package/orm/tests/repository-mapping.test.d.ts +0 -2
  269. package/orm/tests/repository-mapping.test.js +0 -65
  270. package/orm/tests/repository-regression.test.d.ts +0 -1
  271. package/orm/tests/repository-regression.test.js +0 -288
  272. package/orm/tests/repository-search-coverage.test.d.ts +0 -1
  273. package/orm/tests/repository-search-coverage.test.js +0 -107
  274. package/orm/tests/repository-search.test.d.ts +0 -1
  275. package/orm/tests/repository-search.test.js +0 -105
  276. package/orm/tests/repository-soft-delete.test.d.ts +0 -1
  277. package/orm/tests/repository-soft-delete.test.js +0 -118
  278. package/orm/tests/repository-transactions-nested.test.d.ts +0 -1
  279. package/orm/tests/repository-transactions-nested.test.js +0 -178
  280. package/orm/tests/repository-types.test.d.ts +0 -1
  281. package/orm/tests/repository-types.test.js +0 -184
  282. package/orm/tests/repository-undelete.test.d.ts +0 -2
  283. package/orm/tests/repository-undelete.test.js +0 -201
  284. package/orm/tests/schema-converter.test.d.ts +0 -1
  285. package/orm/tests/schema-converter.test.js +0 -82
  286. package/orm/tests/schema-generation.test.d.ts +0 -2
  287. package/orm/tests/schema-generation.test.js +0 -174
  288. package/orm/tests/sql-helpers.test.d.ts +0 -1
  289. package/orm/tests/sql-helpers.test.js +0 -67
  290. package/orm/tests/transaction-safety.test.d.ts +0 -1
  291. package/orm/tests/transaction-safety.test.js +0 -81
  292. package/orm/tests/transactional.test.d.ts +0 -1
  293. package/orm/tests/transactional.test.js +0 -215
  294. package/orm/tests/utils.test.d.ts +0 -1
  295. package/orm/tests/utils.test.js +0 -70
  296. package/pdf/tests/utils.test.d.ts +0 -1
  297. package/pdf/tests/utils.test.js +0 -187
  298. package/process/tests/spawn.test.d.ts +0 -1
  299. package/process/tests/spawn.test.js +0 -182
  300. package/rate-limit/tests/postgres-rate-limiter.test.d.ts +0 -1
  301. package/rate-limit/tests/postgres-rate-limiter.test.js +0 -84
  302. package/renderer/tests/renderer.test.d.ts +0 -1
  303. package/renderer/tests/renderer.test.js +0 -88
  304. package/rpc/tests/rpc.integration.test.d.ts +0 -1
  305. package/rpc/tests/rpc.integration.test.js +0 -615
  306. package/signals/implementation/errors.d.ts +0 -2
  307. package/signals/implementation/graph.js +0 -312
  308. package/signals/implementation/writable-signal.d.ts +0 -48
  309. package/signals/implementation/writable-signal.js +0 -32
  310. package/task-queue/tests/coverage-branch.test.d.ts +0 -1
  311. package/task-queue/tests/coverage-branch.test.js +0 -395
  312. package/task-queue/tests/coverage-enhancement.test.d.ts +0 -1
  313. package/task-queue/tests/coverage-enhancement.test.js +0 -150
  314. package/task-queue/tests/dag.test.d.ts +0 -1
  315. package/task-queue/tests/dag.test.js +0 -188
  316. package/task-queue/tests/dependencies.test.d.ts +0 -1
  317. package/task-queue/tests/dependencies.test.js +0 -296
  318. package/task-queue/tests/enqueue-batch.test.d.ts +0 -1
  319. package/task-queue/tests/enqueue-batch.test.js +0 -125
  320. package/task-queue/tests/enqueue-item.test.d.ts +0 -1
  321. package/task-queue/tests/enqueue-item.test.js +0 -12
  322. package/task-queue/tests/fan-out-spawning.test.d.ts +0 -1
  323. package/task-queue/tests/fan-out-spawning.test.js +0 -94
  324. package/task-queue/tests/idempotent-replacement.test.d.ts +0 -1
  325. package/task-queue/tests/idempotent-replacement.test.js +0 -114
  326. package/task-queue/tests/missing-idempotent-tasks.test.d.ts +0 -1
  327. package/task-queue/tests/missing-idempotent-tasks.test.js +0 -39
  328. package/task-queue/tests/optimization-edge-cases.test.d.ts +0 -1
  329. package/task-queue/tests/optimization-edge-cases.test.js +0 -124
  330. package/task-queue/tests/queue-generic.test.d.ts +0 -1
  331. package/task-queue/tests/queue-generic.test.js +0 -8
  332. package/task-queue/tests/queue.test.d.ts +0 -1
  333. package/task-queue/tests/queue.test.js +0 -756
  334. package/task-queue/tests/shutdown.test.d.ts +0 -1
  335. package/task-queue/tests/shutdown.test.js +0 -41
  336. package/task-queue/tests/task-context.test.d.ts +0 -1
  337. package/task-queue/tests/task-context.test.js +0 -7
  338. package/task-queue/tests/task-union.test.d.ts +0 -1
  339. package/task-queue/tests/task-union.test.js +0 -18
  340. package/task-queue/tests/transactions.test.d.ts +0 -1
  341. package/task-queue/tests/transactions.test.js +0 -47
  342. package/task-queue/tests/typing.test.d.ts +0 -1
  343. package/task-queue/tests/typing.test.js +0 -9
  344. package/task-queue/tests/worker.test.d.ts +0 -1
  345. package/task-queue/tests/worker.test.js +0 -258
  346. package/task-queue/tests/zombie-parent.test.d.ts +0 -1
  347. package/task-queue/tests/zombie-parent.test.js +0 -45
  348. package/task-queue/tests/zombie-recovery.test.d.ts +0 -1
  349. package/task-queue/tests/zombie-recovery.test.js +0 -51
  350. package/utils/tests/backoff.test.d.ts +0 -1
  351. package/utils/tests/backoff.test.js +0 -41
  352. package/utils/tests/retry-with-backoff.test.d.ts +0 -1
  353. package/utils/tests/retry-with-backoff.test.js +0 -49
@@ -1,167 +0,0 @@
1
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from 'vitest';
2
- import { ActorType, Auditor } from '../../audit/index.js';
3
- import { NIL_UUID } from '../../constants.js';
4
- import { clearTenantData, setupIntegrationTest } from '../../testing/index.js';
5
- import { AuthenticationAncillaryService } from '../server/authentication-ancillary.service.js';
6
- import { AuthenticationService } from '../server/authentication.service.js';
7
- import { SubjectService } from '../server/subject.service.js';
8
- import { DefaultAuthenticationAncillaryService } from './authentication.test-ancillary-service.js';
9
- describe('AuthenticationService', () => {
10
- let injector;
11
- let database;
12
- let authenticationService;
13
- let subjectService;
14
- let auditor;
15
- let ancillaryService;
16
- const schema = 'authentication';
17
- const tenantId = crypto.randomUUID();
18
- beforeAll(async () => {
19
- ({ injector, database } = await setupIntegrationTest({
20
- modules: { authentication: true },
21
- authentication: {
22
- ancillaryService: DefaultAuthenticationAncillaryService,
23
- },
24
- }));
25
- authenticationService = await injector.resolveAsync(AuthenticationService);
26
- subjectService = await injector.resolveAsync(SubjectService);
27
- auditor = injector.resolve(Auditor);
28
- ancillaryService = await injector.resolveAsync(AuthenticationAncillaryService);
29
- });
30
- afterAll(async () => {
31
- await injector?.dispose();
32
- });
33
- beforeEach(async () => {
34
- await clearTenantData(database, schema, ['password', 'session', 'user', 'service_account', 'system_account', 'subject'], tenantId);
35
- });
36
- test('login should create a session and listSessions should return it', async () => {
37
- const user = await subjectService.createUser({
38
- tenantId,
39
- email: 'test@example.com',
40
- firstName: 'John',
41
- lastName: 'Doe',
42
- });
43
- await authenticationService.setPassword(user, 'Strong-Password-2026!');
44
- const loginResult = await authenticationService.login({ tenantId, subject: user.id }, 'Strong-Password-2026!', undefined, auditor.with({ actor: user.id, actorType: ActorType.Subject }));
45
- expect(loginResult.type).toBe('success');
46
- const tokenResult = loginResult.result;
47
- expect(tokenResult.token).toBeDefined();
48
- const sessions = await authenticationService.listSessions(tenantId, user.id);
49
- expect(sessions).toHaveLength(1);
50
- expect(sessions[0]?.id).toBe(tokenResult.jsonToken.payload.session);
51
- });
52
- test('invalidateAllSessions should end all active sessions', async () => {
53
- const user = await subjectService.createUser({
54
- tenantId,
55
- email: 'test@example.com',
56
- firstName: 'John',
57
- lastName: 'Doe',
58
- });
59
- await authenticationService.setPassword(user, 'Strong-Password-2026!');
60
- const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
61
- await authenticationService.login({ tenantId, subject: user.id }, 'Strong-Password-2026!', undefined, userAuditor);
62
- await authenticationService.login({ tenantId, subject: user.id }, 'Strong-Password-2026!', undefined, userAuditor);
63
- let sessions = await authenticationService.listSessions(tenantId, user.id);
64
- expect(sessions).toHaveLength(2);
65
- const now = Date.now();
66
- expect(sessions.every((s) => s.end > now)).toBe(true);
67
- await authenticationService.invalidateAllSessions(tenantId, user.id, userAuditor);
68
- sessions = await authenticationService.listSessions(tenantId, user.id);
69
- expect(sessions).toHaveLength(0);
70
- });
71
- test('getSession and tryGetSession', async () => {
72
- const user = await subjectService.createUser({
73
- tenantId,
74
- email: 'test@example.com',
75
- firstName: 'John',
76
- lastName: 'Doe',
77
- });
78
- await authenticationService.setPassword(user, 'Strong-Password-2026!');
79
- const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
80
- const loginResult = await authenticationService.login({ tenantId, subject: user.id }, 'Strong-Password-2026!', undefined, userAuditor);
81
- expect(loginResult.type).toBe('success');
82
- const tokenResult = loginResult.result;
83
- const sessionId = tokenResult.jsonToken.payload.session;
84
- const session = await authenticationService.getSession(sessionId);
85
- expect(session.id).toBe(sessionId);
86
- const triedSession = await authenticationService.tryGetSession(sessionId);
87
- expect(triedSession?.id).toBe(sessionId);
88
- const nonExistent = await authenticationService.tryGetSession(NIL_UUID);
89
- expect(nonExistent).toBeUndefined();
90
- });
91
- test('refresh should issue new token and session', async () => {
92
- const user = await subjectService.createUser({ tenantId, email: 'refresh@example.com', firstName: 'R', lastName: 'F' });
93
- await authenticationService.setPassword(user, 'Strong-Password-2026!');
94
- const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
95
- const loginResult = await authenticationService.login({ tenantId, subject: user.id }, 'Strong-Password-2026!', undefined, userAuditor);
96
- expect(loginResult.type).toBe('success');
97
- const loginSuccessResult = loginResult.result;
98
- const refreshResult = await authenticationService.refresh(loginSuccessResult.refreshToken, undefined, {}, userAuditor);
99
- expect(refreshResult.token).toBeDefined();
100
- expect(refreshResult.refreshToken).not.toBe(loginSuccessResult.refreshToken);
101
- });
102
- test('changePassword should update password', async () => {
103
- const user = await subjectService.createUser({ tenantId, email: 'change@example.com', firstName: 'C', lastName: 'S' });
104
- await authenticationService.setPassword(user, 'Old-Password-2026!');
105
- const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
106
- await authenticationService.changePassword({ tenantId, subject: user.id }, 'Old-Password-2026!', 'New-Password-2026!', userAuditor);
107
- const authResult = await authenticationService.authenticateWithPassword({ tenantId, subject: user.id }, 'New-Password-2026!');
108
- expect(authResult.success).toBe(true);
109
- });
110
- test('checkPassword, testPassword, and validatePassword', async () => {
111
- const weak = 'abc';
112
- const strong = 'Very-Strong-Password-2026-!@#$';
113
- expect((await authenticationService.checkPassword(weak)).strength).toBeLessThan(2);
114
- expect((await authenticationService.checkPassword(strong)).strength).toBeGreaterThanOrEqual(2);
115
- expect((await authenticationService.testPassword(weak)).success).toBe(false);
116
- expect((await authenticationService.testPassword(strong)).success).toBe(true);
117
- await expect(authenticationService.validatePassword(weak)).rejects.toThrow();
118
- await expect(authenticationService.validatePassword(strong)).resolves.not.toThrow();
119
- });
120
- test('tryResolveSubject and resolveSubject', async () => {
121
- const user = await subjectService.createUser({ tenantId, email: 'resolve@example.com', firstName: 'R', lastName: 'S' });
122
- const resolved = await authenticationService.tryResolveSubject({ tenantId, subject: user.id });
123
- expect(resolved?.id).toBe(user.id);
124
- const resolvedByEmail = await authenticationService.resolveSubject({ tenantId, subject: 'resolve@example.com' });
125
- expect(resolvedByEmail.id).toBe(user.id);
126
- await expect(authenticationService.resolveSubject({ tenantId, subject: 'missing' })).rejects.toThrow();
127
- });
128
- test('impersonation and unimpersonation', async () => {
129
- const admin = await subjectService.createUser({ tenantId, email: 'admin@example.com', firstName: 'A', lastName: 'D' });
130
- const user = await subjectService.createUser({ tenantId, email: 'user@example.com', firstName: 'U', lastName: 'S' });
131
- const adminAuditor = auditor.with({ actor: admin.id, actorType: ActorType.Subject });
132
- const adminToken = await authenticationService.getToken(admin, undefined);
133
- const impersonated = await authenticationService.impersonate(adminToken.token, adminToken.refreshToken, user.id, undefined, adminAuditor);
134
- expect(impersonated.jsonToken.payload.subject).toBe(user.id);
135
- expect(impersonated.jsonToken.payload.impersonator).toBe(admin.id);
136
- const unimpersonated = await authenticationService.unimpersonate(impersonated.impersonatorRefreshToken, impersonated.token, undefined, adminAuditor);
137
- expect(unimpersonated.jsonToken.payload.subject).toBe(admin.id);
138
- expect(unimpersonated.jsonToken.payload.impersonator).toBeUndefined();
139
- });
140
- test('refresh should throw on invalid token', async () => {
141
- await expect(authenticationService.refresh('invalid', undefined, {}, auditor)).rejects.toThrow();
142
- });
143
- test('login should throw on invalid password', async () => {
144
- const user = await subjectService.createUser({ tenantId, email: 'fail@example.com', firstName: 'F', lastName: 'L' });
145
- await authenticationService.setPassword(user, 'Very-Strong-Password-2026!');
146
- await expect(authenticationService.login({ tenantId, subject: user.id }, 'wrong', undefined, auditor)).rejects.toThrow();
147
- });
148
- test('endSession should handle non-existent session gracefully', async () => {
149
- await expect(authenticationService.endSession(NIL_UUID, auditor)).resolves.not.toThrow();
150
- });
151
- test('resolveSubject should throw if not found', async () => {
152
- await expect(authenticationService.resolveSubject({ tenantId, subject: 'missing' })).rejects.toThrow();
153
- });
154
- test('password reset flow', async () => {
155
- const user = await subjectService.createUser({ tenantId, email: 'reset@example.com', firstName: 'R', lastName: 'E' });
156
- const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
157
- await authenticationService.initPasswordReset({ tenantId, subject: user.id }, undefined, userAuditor);
158
- expect(ancillaryService.lastResetData).toBeDefined();
159
- await authenticationService.resetPassword(ancillaryService.lastResetData.token, 'New-Password-Reset-2026!', userAuditor);
160
- const authResult = await authenticationService.authenticateWithPassword({ tenantId, subject: user.id }, 'New-Password-Reset-2026!');
161
- expect(authResult.success).toBe(true);
162
- });
163
- test('deriveSigningSecrets should work', async () => {
164
- // This is mostly covered by initialize, but we can test it implicitly by ensuring service works after init
165
- expect(authenticationService).toBeDefined();
166
- });
167
- });
@@ -1,9 +0,0 @@
1
- import { AuthenticationAncillaryService } from '../server/index.js';
2
- export declare class DefaultAuthenticationAncillaryService extends AuthenticationAncillaryService<any, any, any> {
3
- #private;
4
- lastResetData: any;
5
- getTokenPayload(): Promise<{}>;
6
- handleInitPasswordReset(data: any): Promise<void>;
7
- canImpersonate(_token: any, _subject: any, _data: any): Promise<boolean>;
8
- resolveSubjects(data: any): Promise<import("../index.js").Subject[]>;
9
- }
@@ -1,27 +0,0 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- };
7
- import { inject, Injector, Singleton } from '../../injector/index.js';
8
- import { isUndefined } from '../../utils/type-guards.js';
9
- import { AuthenticationAncillaryService, AuthenticationService } from '../server/index.js';
10
- let DefaultAuthenticationAncillaryService = class DefaultAuthenticationAncillaryService extends AuthenticationAncillaryService {
11
- #injector = inject(Injector);
12
- #authenticationService;
13
- lastResetData;
14
- async getTokenPayload() { return {}; }
15
- async handleInitPasswordReset(data) { this.lastResetData = data; }
16
- async canImpersonate(_token, _subject, _data) { return true; }
17
- async resolveSubjects(data) {
18
- if (isUndefined(this.#authenticationService)) {
19
- this.#authenticationService = await this.#injector.resolveAsync(AuthenticationService);
20
- }
21
- return await this.#authenticationService.defaultResolveSubjects(data);
22
- }
23
- };
24
- DefaultAuthenticationAncillaryService = __decorate([
25
- Singleton()
26
- ], DefaultAuthenticationAncillaryService);
27
- export { DefaultAuthenticationAncillaryService };
@@ -1 +0,0 @@
1
- export {};
@@ -1,211 +0,0 @@
1
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from 'vitest';
2
- import { ApiGateway } from '../../api/server/gateway.js';
3
- import { Auditor } from '../../audit/index.js';
4
- import { importKey } from '../../cryptography/index.js';
5
- import { generateTotpToken } from '../../cryptography/totp.js';
6
- import { HttpBody } from '../../http/http-body.js';
7
- import { HttpHeaders } from '../../http/http-headers.js';
8
- import { HttpQuery } from '../../http/http-query.js';
9
- import { HttpServerRequest } from '../../http/server/http-server-request.js';
10
- import { clearTenantData, setupIntegrationTest } from '../../testing/index.js';
11
- import { currentTimestampSeconds } from '../../utils/date-time.js';
12
- import { assert } from '../../utils/type-guards.js';
13
- import { AuthenticationService } from '../server/authentication.service.js';
14
- import { SubjectService } from '../server/subject.service.js';
15
- import { DefaultAuthenticationAncillaryService } from './authentication.test-ancillary-service.js';
16
- describe('Authentication Brute Force Protection (API Level)', () => {
17
- let injector;
18
- let database;
19
- let gateway;
20
- let subjectService;
21
- let authenticationService;
22
- let auditor;
23
- const schema = 'authentication';
24
- const tenantId = crypto.randomUUID();
25
- beforeAll(async () => {
26
- ({ injector, database } = await setupIntegrationTest({
27
- modules: { authentication: true, rateLimiter: true, api: true },
28
- authentication: {
29
- ancillaryService: DefaultAuthenticationAncillaryService,
30
- options: {
31
- bruteForceProtection: {
32
- subjectBurstCapacity: 2,
33
- subjectRefillInterval: 10000,
34
- ipBurstCapacity: 2,
35
- ipRefillInterval: 10000,
36
- },
37
- },
38
- },
39
- }));
40
- gateway = injector.resolve(ApiGateway);
41
- subjectService = await injector.resolveAsync(SubjectService);
42
- authenticationService = await injector.resolveAsync(AuthenticationService);
43
- auditor = injector.resolve(Auditor);
44
- });
45
- afterAll(async () => {
46
- await injector?.dispose();
47
- });
48
- beforeEach(async () => {
49
- await clearTenantData(database, schema, ['used_totp_tokens', 'totp_recovery_code', 'totp', 'password', 'session', 'user', 'service_account', 'system_account', 'subject'], tenantId);
50
- });
51
- async function callLogin(ip, body) {
52
- const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
53
- const encodedBody = new TextEncoder().encode(JSON.stringify(body));
54
- const request = new HttpServerRequest({
55
- url: new URL('http://localhost/api/v1/auth/token'),
56
- method: 'POST',
57
- headers,
58
- query: new HttpQuery(),
59
- ip,
60
- body: HttpBody.from(encodedBody, headers),
61
- });
62
- let capturedResponse;
63
- const respond = async (response) => { capturedResponse = response; };
64
- const close = async () => { };
65
- const abortSignal = new AbortController().signal;
66
- await gateway.handleHttpServerRequestContext({ request, respond, close, abortSignal, context: {} });
67
- return capturedResponse;
68
- }
69
- async function callDisableTotp(ip, token, body) {
70
- const headers = new HttpHeaders({
71
- 'Content-Type': 'application/json',
72
- 'Authorization': `Bearer ${token}`
73
- });
74
- const encodedBody = new TextEncoder().encode(JSON.stringify(body));
75
- const request = new HttpServerRequest({
76
- url: new URL('http://localhost/api/v1/auth/totp/disable'),
77
- method: 'POST',
78
- headers,
79
- query: new HttpQuery(),
80
- ip,
81
- body: HttpBody.from(encodedBody, headers),
82
- });
83
- let capturedResponse;
84
- const respond = async (response) => { capturedResponse = response; };
85
- const close = async () => { };
86
- const abortSignal = new AbortController().signal;
87
- await gateway.handleHttpServerRequestContext({ request, respond, close, abortSignal, context: {} });
88
- return capturedResponse;
89
- }
90
- test('should limit login attempts by user', async () => {
91
- const user = await subjectService.createUser({ tenantId, email: 'user-limit@example.com', firstName: 'John', lastName: 'Doe' });
92
- await authenticationService.setPassword(user, 'Strong-Password-2026!');
93
- const body = { tenantId, subject: user.id, password: 'wrong' };
94
- // 1st attempt - Fail (InvalidCredentials)
95
- let res = await callLogin('1.1.1.1', body);
96
- expect(res.statusCode).toBe(401);
97
- // 2nd attempt - Fail (InvalidCredentials)
98
- res = await callLogin('1.1.1.2', body);
99
- expect(res.statusCode).toBe(401);
100
- // 3rd attempt - Should be throttled (429)
101
- res = await callLogin('1.1.1.3', body);
102
- expect(res.statusCode).toBe(429);
103
- expect(res.body.json.error.name).toBe('TooManyRequestsError');
104
- });
105
- test('should limit login attempts by IP', async () => {
106
- const ip = '2.2.2.2';
107
- const user1 = await subjectService.createUser({ tenantId, email: 'ip-limit-1@example.com', firstName: 'A', lastName: 'B' });
108
- const user2 = await subjectService.createUser({ tenantId, email: 'ip-limit-2@example.com', firstName: 'C', lastName: 'D' });
109
- await authenticationService.setPassword(user1, 'Strong-Password-2026!');
110
- await authenticationService.setPassword(user2, 'Strong-Password-2026!');
111
- // Fail for user1
112
- let res = await callLogin(ip, { tenantId, subject: user1.id, password: 'wrong' });
113
- expect(res.statusCode).toBe(401);
114
- // Fail for user2
115
- res = await callLogin(ip, { tenantId, subject: user2.id, password: 'wrong' });
116
- expect(res.statusCode).toBe(401);
117
- // 3rd attempt from same IP - Should be throttled (429)
118
- res = await callLogin(ip, { tenantId, subject: 'any', password: 'any' });
119
- expect(res.statusCode).toBe(429);
120
- });
121
- test('should work for multiple successful logins', async () => {
122
- const user1 = await subjectService.createUser({ tenantId, email: 'success1@example.com', firstName: 'E', lastName: 'F' });
123
- const user2 = await subjectService.createUser({ tenantId, email: 'success2@example.com', firstName: 'E', lastName: 'F' });
124
- const user3 = await subjectService.createUser({ tenantId, email: 'success3@example.com', firstName: 'E', lastName: 'F' });
125
- await authenticationService.setPassword(user1, 'Strong-Password-2026!');
126
- await authenticationService.setPassword(user2, 'Strong-Password-2026!');
127
- await authenticationService.setPassword(user3, 'Strong-Password-2026!');
128
- // 1st attempt
129
- let res = await callLogin('4.4.4.1', { tenantId, subject: user1.id, password: 'Strong-Password-2026!' });
130
- expect(res.statusCode).toBe(200);
131
- // 2nd attempt
132
- res = await callLogin('4.4.4.2', { tenantId, subject: user2.id, password: 'Strong-Password-2026!' });
133
- expect(res.statusCode).toBe(200);
134
- // 3rd attempt
135
- res = await callLogin('4.4.4.3', { tenantId, subject: user3.id, password: 'Strong-Password-2026!' });
136
- expect(res.statusCode).toBe(200);
137
- });
138
- test('should limit disableTotp attempts by user', async () => {
139
- const password = 'Strong-Password-2026!';
140
- const user = await subjectService.createUser({ tenantId, email: 'totp-limit@example.com', firstName: 'John', lastName: 'Doe' });
141
- await authenticationService.setPassword(user, password);
142
- // Enable TOTP for the user
143
- await authenticationService.initEnrollTotp(tenantId, user.id, auditor);
144
- const totpEntry = await authenticationService.tryGetTotp(tenantId, user.id);
145
- const secret = await importKey('raw-secret', totpEntry.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
146
- const setupToken = await generateTotpToken(secret, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() - 30 });
147
- await authenticationService.completeEnrollTotp(tenantId, user.id, setupToken, auditor);
148
- // Login to get a session token
149
- const loginResult = await authenticationService.login({ tenantId, subject: user.id }, password, undefined, auditor);
150
- expect(loginResult.type).toBe('totp');
151
- assert(loginResult.type == 'totp', 'Login should have triggered TOTP');
152
- const verifyToken = await generateTotpToken(secret, authenticationService.getTotpOptions());
153
- const verifyResult = await authenticationService.loginVerifyTotp(loginResult.challengeToken, verifyToken, auditor);
154
- expect(verifyResult.type).toBe('success');
155
- assert(verifyResult.type == 'success', 'TOTP verification failed');
156
- const sessionToken = verifyResult.result.token;
157
- // 1st attempt - Fail (Invalid TOTP token)
158
- let res = await callDisableTotp('1.1.1.1', sessionToken, { token: '000000' });
159
- expect(res.statusCode).toBe(403);
160
- // 2nd attempt - Fail (Invalid TOTP token)
161
- res = await callDisableTotp('1.1.1.2', sessionToken, { token: '000001' });
162
- expect(res.statusCode).toBe(403);
163
- // 3rd attempt - Should be throttled (429)
164
- res = await callDisableTotp('1.1.1.3', sessionToken, { token: '000002' });
165
- expect(res.statusCode).toBe(429);
166
- expect(res.body.json.error.name).toBe('TooManyRequestsError');
167
- });
168
- test('should limit disableTotp attempts by IP', async () => {
169
- const password = 'Strong-Password-2026!';
170
- const ip = '3.3.3.3';
171
- // Create two users with TOTP enabled
172
- const user1 = await subjectService.createUser({ tenantId, email: 'totp-ip-1@example.com', firstName: 'A', lastName: 'B' });
173
- await authenticationService.setPassword(user1, password);
174
- await authenticationService.initEnrollTotp(tenantId, user1.id, auditor);
175
- const totpEntry1 = await authenticationService.tryGetTotp(tenantId, user1.id);
176
- const secret1 = await importKey('raw-secret', totpEntry1.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
177
- const setupToken1 = await generateTotpToken(secret1, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() - 30 });
178
- await authenticationService.completeEnrollTotp(tenantId, user1.id, setupToken1, auditor);
179
- const user2 = await subjectService.createUser({ tenantId, email: 'totp-ip-2@example.com', firstName: 'C', lastName: 'D' });
180
- await authenticationService.setPassword(user2, password);
181
- await authenticationService.initEnrollTotp(tenantId, user2.id, auditor);
182
- const totpEntry2 = await authenticationService.tryGetTotp(tenantId, user2.id);
183
- const secret2 = await importKey('raw-secret', totpEntry2.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
184
- const setupToken2 = await generateTotpToken(secret2, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() - 30 });
185
- await authenticationService.completeEnrollTotp(tenantId, user2.id, setupToken2, auditor);
186
- // Login both users
187
- const loginResult1 = await authenticationService.login({ tenantId, subject: user1.id }, password, undefined, auditor);
188
- const loginResult2 = await authenticationService.login({ tenantId, subject: user2.id }, password, undefined, auditor);
189
- expect(loginResult1.type).toBe('totp');
190
- expect(loginResult2.type).toBe('totp');
191
- assert(loginResult1.type == 'totp' && loginResult2.type == 'totp', 'Login should have triggered TOTP');
192
- const verifyToken1 = await generateTotpToken(secret1, authenticationService.getTotpOptions());
193
- const verifyToken2 = await generateTotpToken(secret2, authenticationService.getTotpOptions());
194
- const verifyResult1 = await authenticationService.loginVerifyTotp(loginResult1.challengeToken, verifyToken1, auditor);
195
- const verifyResult2 = await authenticationService.loginVerifyTotp(loginResult2.challengeToken, verifyToken2, auditor);
196
- expect(verifyResult1.type).toBe('success');
197
- expect(verifyResult2.type).toBe('success');
198
- assert(verifyResult1.type == 'success' && verifyResult2.type == 'success', 'TOTP verification failed');
199
- const sessionToken1 = verifyResult1.result.token;
200
- const sessionToken2 = verifyResult2.result.token;
201
- // Fail for user1 from IP
202
- let res = await callDisableTotp(ip, sessionToken1, { token: '000000' });
203
- expect(res.statusCode).toBe(403);
204
- // Fail for user2 from same IP
205
- res = await callDisableTotp(ip, sessionToken2, { token: '000000' });
206
- expect(res.statusCode).toBe(403);
207
- // 3rd attempt from same IP - Should be throttled (429)
208
- res = await callDisableTotp(ip, sessionToken1, { token: 'any' });
209
- expect(res.statusCode).toBe(429);
210
- });
211
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,122 +0,0 @@
1
- import { beforeAll, describe, expect, test } from 'vitest';
2
- import { createJwtTokenString, importKmacKey } from '../../cryptography/index.js';
3
- import { BadRequestError } from '../../errors/bad-request.error.js';
4
- import { InvalidTokenError } from '../../errors/invalid-token.error.js';
5
- import { currentTimestampSeconds } from '../../utils/date-time.js';
6
- import { encodeUtf8 } from '../../utils/encoding.js';
7
- import { getPasswordResetTokenFromString, getRefreshTokenFromString, getTokenFromRequest, getTokenFromString, tryGetAuthorizationTokenStringFromRequest, tryGetTokenFromRequest } from '../server/helper.js';
8
- describe('authentication helper', () => {
9
- let signingKey;
10
- beforeAll(async () => {
11
- signingKey = await importKmacKey('raw-secret', 'KMAC256', encodeUtf8('test-secret-with-enough-length-32'));
12
- });
13
- test('tryGetAuthorizationTokenStringFromRequest should extract bearer token from header', () => {
14
- const request = {
15
- headers: {
16
- tryGet: (name) => name == 'Authorization' ? 'Bearer my-token' : undefined,
17
- },
18
- cookies: {
19
- tryGet: () => undefined,
20
- },
21
- };
22
- expect(tryGetAuthorizationTokenStringFromRequest(request)).toBe('my-token');
23
- });
24
- test('tryGetAuthorizationTokenStringFromRequest should throw if token without scheme from header', () => {
25
- const request = {
26
- headers: {
27
- tryGet: (name) => name == 'Authorization' ? 'my-token' : undefined,
28
- },
29
- cookies: {
30
- tryGet: () => undefined,
31
- },
32
- };
33
- expect(() => tryGetAuthorizationTokenStringFromRequest(request)).toThrow(BadRequestError);
34
- });
35
- test('tryGetAuthorizationTokenStringFromRequest should extract bearer token from cookie', () => {
36
- const request = {
37
- headers: {
38
- tryGet: () => undefined,
39
- },
40
- cookies: {
41
- tryGet: (name) => name == 'token' ? 'my-token' : undefined,
42
- },
43
- };
44
- expect(tryGetAuthorizationTokenStringFromRequest(request)).toBe('my-token');
45
- });
46
- test('tryGetAuthorizationTokenStringFromRequest should throw on unsupported scheme', () => {
47
- const request = {
48
- headers: {
49
- tryGet: (name) => name == 'Authorization' ? 'Basic my-token' : undefined,
50
- },
51
- cookies: {
52
- tryGet: () => undefined,
53
- },
54
- };
55
- expect(() => tryGetAuthorizationTokenStringFromRequest(request)).toThrow(BadRequestError);
56
- });
57
- test('tryGetAuthorizationTokenStringFromRequest should return undefined if no token found', () => {
58
- const request = {
59
- headers: {
60
- tryGet: () => undefined,
61
- },
62
- cookies: {
63
- tryGet: () => undefined,
64
- },
65
- };
66
- expect(tryGetAuthorizationTokenStringFromRequest(request)).toBeUndefined();
67
- });
68
- test('getTokenFromString should validate token', async () => {
69
- const payload = { exp: currentTimestampSeconds() + 60, tenant: 't', subject: 's', jti: 'j' };
70
- const token = await createJwtTokenString({ header: { v: 1, alg: 'KMAC256', typ: 'JWT' }, payload }, signingKey);
71
- const validated = await getTokenFromString(token, 1, signingKey);
72
- expect(validated.payload).toEqual(payload);
73
- });
74
- test('getTokenFromString should throw on version mismatch', async () => {
75
- const payload = { exp: currentTimestampSeconds() + 60, tenant: 't', subject: 's', jti: 'j' };
76
- const token = await createJwtTokenString({ header: { v: 2, alg: 'KMAC256', typ: 'JWT' }, payload }, signingKey);
77
- await expect(getTokenFromString(token, 1, signingKey)).rejects.toThrow(InvalidTokenError);
78
- });
79
- test('getTokenFromString should throw on expired token', async () => {
80
- const payload = { exp: currentTimestampSeconds() - 60, tenant: 't', subject: 's', jti: 'j' };
81
- const token = await createJwtTokenString({ header: { v: 1, alg: 'KMAC256', typ: 'JWT' }, payload }, signingKey);
82
- await expect(getTokenFromString(token, 1, signingKey)).rejects.toThrow('Token expired');
83
- });
84
- test('getRefreshTokenFromString should validate refresh token', async () => {
85
- const payload = { exp: currentTimestampSeconds() + 60, tenant: 't', subject: 's', session: 'sess', secret: 'sec' };
86
- const token = await createJwtTokenString({ header: { alg: 'KMAC256', typ: 'JWT' }, payload }, signingKey);
87
- const validated = await getRefreshTokenFromString(token, signingKey);
88
- expect(validated.payload).toEqual(payload);
89
- });
90
- test('getPasswordResetTokenFromString should validate password reset token', async () => {
91
- const payload = { iat: currentTimestampSeconds(), exp: currentTimestampSeconds() + 60, tenant: 't', subject: 's' };
92
- const token = await createJwtTokenString({ header: { alg: 'KMAC256', typ: 'JWT' }, payload }, signingKey);
93
- const validated = await getPasswordResetTokenFromString(token, signingKey);
94
- expect(validated.payload).toEqual(payload);
95
- });
96
- test('getTokenFromRequest should extract and validate token', async () => {
97
- const payload = { exp: currentTimestampSeconds() + 60, tenant: 't', subject: 's', jti: 'j' };
98
- const token = await createJwtTokenString({ header: { v: 1, alg: 'KMAC256', typ: 'JWT' }, payload }, signingKey);
99
- const request = {
100
- headers: {
101
- tryGet: (name) => name == 'Authorization' ? `Bearer ${token}` : undefined,
102
- },
103
- cookies: {
104
- tryGet: () => undefined,
105
- },
106
- };
107
- const validated = await getTokenFromRequest(request, 1, signingKey);
108
- expect(validated.payload).toEqual(payload);
109
- });
110
- test('tryGetTokenFromRequest should return undefined if no token in request', async () => {
111
- const request = {
112
- headers: {
113
- tryGet: () => undefined,
114
- },
115
- cookies: {
116
- tryGet: () => undefined,
117
- },
118
- };
119
- const validated = await tryGetTokenFromRequest(request, 1, signingKey);
120
- expect(validated).toBeUndefined();
121
- });
122
- });
@@ -1,14 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { PasswordRequirementsError } from '../errors/password-requirements.error.js';
3
- describe('PasswordRequirementsError', () => {
4
- it('should create an error with the given message', () => {
5
- const message = 'Password is too weak.';
6
- const error = new PasswordRequirementsError(message);
7
- expect(error.message).toBe(message);
8
- expect(error.name).toBe('PasswordRequirementsError');
9
- });
10
- it('should have the correct name', () => {
11
- const error = new PasswordRequirementsError('any message');
12
- expect(error.name).toBe('PasswordRequirementsError');
13
- });
14
- });
@@ -1 +0,0 @@
1
- export {};