@tstdl/base 0.93.182 → 0.93.183

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 +13 -5
  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,117 +0,0 @@
1
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from 'vitest';
2
- import { Auditor } from '../../audit/index.js';
3
- import { HttpHeaders } from '../../http/index.js';
4
- import { HttpServerResponse } from '../../http/server/index.js';
5
- import { clearTenantData, setupIntegrationTest } from '../../testing/index.js';
6
- import { toArray } from '../../utils/array/array.js';
7
- import { AuthenticationApiController } from '../server/authentication.api-controller.js';
8
- import { AuthenticationService } from '../server/authentication.service.js';
9
- import { SubjectService } from '../server/subject.service.js';
10
- import { DefaultAuthenticationAncillaryService } from './authentication.test-ancillary-service.js';
11
- describe('AuthenticationApiController Remember Functionality', () => {
12
- let injector;
13
- let database;
14
- let controller;
15
- let authenticationService;
16
- let subjectService;
17
- let auditor;
18
- const schema = 'authentication';
19
- const tenantId = crypto.randomUUID();
20
- beforeAll(async () => {
21
- ({ injector, database } = await setupIntegrationTest({
22
- modules: { authentication: true, audit: true, keyValueStore: true, signals: true, api: true },
23
- authentication: {
24
- ancillaryService: DefaultAuthenticationAncillaryService,
25
- },
26
- }));
27
- authenticationService = await injector.resolveAsync(AuthenticationService);
28
- subjectService = await injector.resolveAsync(SubjectService);
29
- auditor = injector.resolve(Auditor);
30
- controller = injector.resolve(AuthenticationApiController);
31
- });
32
- afterAll(async () => {
33
- await injector?.dispose();
34
- });
35
- beforeEach(async () => {
36
- await clearTenantData(database, schema, ['used_totp_tokens', 'totp_recovery_code', 'totp', 'password', 'session', 'user', 'service_account', 'system_account', 'subject'], tenantId);
37
- });
38
- test('login with remember: true should have Expires in cookies', async () => {
39
- const user = await subjectService.createUser({ tenantId, email: 'api-rem@example.com', firstName: 'A', lastName: 'L' });
40
- await authenticationService.setPassword(user, 'Pass-R3m3mb3r-2026!');
41
- const context = {
42
- request: { ip: '127.0.0.1' },
43
- parameters: { tenantId, subject: user.id, password: 'Pass-R3m3mb3r-2026!', remember: true, data: undefined },
44
- getAuditor: async () => auditor,
45
- };
46
- const response = await controller.login(context);
47
- expect(response).toBeInstanceOf(HttpServerResponse);
48
- const setCookieHeaders = toArray(response.headers.tryGet('Set-Cookie') ?? []);
49
- expect(setCookieHeaders).toHaveLength(2); // authorization and refreshToken
50
- for (const cookie of setCookieHeaders) {
51
- expect(cookie).toMatch(/Expires=/);
52
- }
53
- });
54
- test('login with remember: false should NOT have Expires in cookies', async () => {
55
- const user = await subjectService.createUser({ tenantId, email: 'api-no-rem@example.com', firstName: 'A', lastName: 'L' });
56
- await authenticationService.setPassword(user, 'Pass-R3m3mb3r-2026!');
57
- const context = {
58
- request: { ip: '127.0.0.1' },
59
- parameters: { tenantId, subject: user.id, password: 'Pass-R3m3mb3r-2026!', remember: false, data: undefined },
60
- getAuditor: async () => auditor,
61
- };
62
- const response = await controller.login(context);
63
- const setCookieHeaders = toArray(response.headers.tryGet('Set-Cookie') ?? []);
64
- for (const cookie of setCookieHeaders) {
65
- expect(cookie).not.toMatch(/Expires=/);
66
- expect(cookie).not.toMatch(/Max-Age=/);
67
- }
68
- });
69
- test('refresh should propagate remember status to cookies', async () => {
70
- const user = await subjectService.createUser({ tenantId, email: 'api-refresh-rem@example.com', firstName: 'A', lastName: 'L' });
71
- await authenticationService.setPassword(user, 'Pass-R3m3mb3r-2026!');
72
- // 1. Login with remember: true
73
- const loginResult = await authenticationService.login({ tenantId, subject: user.id }, 'Pass-R3m3mb3r-2026!', undefined, auditor, true);
74
- expect(loginResult.type).toBe('success');
75
- // 2. Refresh
76
- const context = {
77
- request: {
78
- ip: '127.0.0.1',
79
- headers: new HttpHeaders({
80
- 'X-Refresh-Token': `Bearer ${loginResult.result.refreshToken}`,
81
- }),
82
- cookies: {
83
- tryGet: () => undefined,
84
- },
85
- },
86
- parameters: { data: undefined },
87
- getAuditor: async () => auditor,
88
- };
89
- const response = await controller.refresh(context);
90
- const setCookieHeaders = toArray(response.headers.tryGet('Set-Cookie') ?? []);
91
- for (const cookie of setCookieHeaders) {
92
- expect(cookie).toMatch(/Expires=/);
93
- }
94
- // 3. Login with remember: false
95
- const loginResultNoRem = await authenticationService.login({ tenantId, subject: user.id }, 'Pass-R3m3mb3r-2026!', undefined, auditor, false);
96
- expect(loginResultNoRem.type).toBe('success');
97
- // 4. Refresh
98
- const contextNoRem = {
99
- request: {
100
- ip: '127.0.0.1',
101
- headers: new HttpHeaders({
102
- 'X-Refresh-Token': `Bearer ${loginResultNoRem.result.refreshToken}`,
103
- }),
104
- cookies: {
105
- tryGet: () => undefined,
106
- },
107
- },
108
- parameters: { data: undefined },
109
- getAuditor: async () => auditor,
110
- };
111
- const responseNoRem = await controller.refresh(contextNoRem);
112
- const setCookieHeadersNoRem = toArray(responseNoRem.headers.tryGet('Set-Cookie') ?? []);
113
- for (const cookie of setCookieHeadersNoRem) {
114
- expect(cookie).not.toMatch(/Expires=/);
115
- }
116
- });
117
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,83 +0,0 @@
1
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from 'vitest';
2
- import { Auditor } from '../../audit/index.js';
3
- import { clearTenantData, setupIntegrationTest } from '../../testing/index.js';
4
- import { AuthenticationService } from '../server/authentication.service.js';
5
- import { SubjectService } from '../server/subject.service.js';
6
- import { DefaultAuthenticationAncillaryService } from './authentication.test-ancillary-service.js';
7
- describe('AuthenticationService Remember Functionality', () => {
8
- let injector;
9
- let database;
10
- let authenticationService;
11
- let subjectService;
12
- let auditor;
13
- const schema = 'authentication';
14
- const tenantId = crypto.randomUUID();
15
- beforeAll(async () => {
16
- ({ injector, database } = await setupIntegrationTest({
17
- modules: { authentication: true },
18
- authentication: {
19
- ancillaryService: DefaultAuthenticationAncillaryService,
20
- },
21
- }));
22
- authenticationService = await injector.resolveAsync(AuthenticationService);
23
- subjectService = await injector.resolveAsync(SubjectService);
24
- auditor = injector.resolve(Auditor);
25
- });
26
- afterAll(async () => {
27
- await injector?.dispose();
28
- });
29
- beforeEach(async () => {
30
- await clearTenantData(database, schema, ['password', 'session', 'user', 'service_account', 'system_account', 'subject'], tenantId);
31
- });
32
- test('RefreshToken should contain remember flag', async () => {
33
- const user = await subjectService.createUser({
34
- tenantId,
35
- email: 'remember@example.com',
36
- firstName: 'Rem',
37
- lastName: 'Ember',
38
- });
39
- const tokenResult = await authenticationService.getToken(user, undefined, { remember: true });
40
- const refreshToken = await authenticationService.validateRefreshToken(tokenResult.refreshToken);
41
- expect(refreshToken.payload).toHaveProperty('remember', true);
42
- expect(tokenResult.remember).toBe(true);
43
- });
44
- test('RefreshToken should respect remember flag for expiration', async () => {
45
- const user = await subjectService.createUser({ tenantId, email: 'ttl@example.com', firstName: 'T', lastName: 'L' });
46
- const normalResult = await authenticationService.getToken(user, undefined, { remember: false });
47
- const rememberResult = await authenticationService.getToken(user, undefined, { remember: true });
48
- const normalToken = await authenticationService.validateRefreshToken(normalResult.refreshToken);
49
- const rememberToken = await authenticationService.validateRefreshToken(rememberResult.refreshToken);
50
- expect(rememberToken.payload.exp).toBeGreaterThan(normalToken.payload.exp);
51
- });
52
- test('login should pass remember flag to getToken', async () => {
53
- const user = await subjectService.createUser({ tenantId, email: 'login-rem@example.com', firstName: 'L', lastName: 'R' });
54
- await authenticationService.setPassword(user, 'Strong-Password-2026!');
55
- const result = await authenticationService.login({ tenantId, subject: user.id }, 'Strong-Password-2026!', undefined, auditor, true);
56
- expect(result.type).toBe('success');
57
- const tokenResult = result.result;
58
- expect(tokenResult.remember).toBe(true);
59
- const refreshToken = await authenticationService.validateRefreshToken(tokenResult.refreshToken);
60
- expect(refreshToken.payload.remember).toBe(true);
61
- });
62
- test('refresh should propagate remember flag', async () => {
63
- const user = await subjectService.createUser({ tenantId, email: 'refresh-rem@example.com', firstName: 'R', lastName: 'R' });
64
- await authenticationService.setPassword(user, 'Strong-Password-2026!');
65
- const loginResult = await authenticationService.login({ tenantId, subject: user.id }, 'Strong-Password-2026!', undefined, auditor, true);
66
- expect(loginResult.type).toBe('success');
67
- const loginSuccessResult = loginResult.result;
68
- expect(loginSuccessResult.remember).toBe(true);
69
- const refreshResult = await authenticationService.refresh(loginSuccessResult.refreshToken, undefined, {}, auditor);
70
- expect(refreshResult.remember).toBe(true);
71
- const newRefreshToken = await authenticationService.validateRefreshToken(refreshResult.refreshToken);
72
- expect(newRefreshToken.payload.remember).toBe(true);
73
- // Verify it also works when not remembered
74
- const loginResultNoRem = await authenticationService.login({ tenantId, subject: user.id }, 'Strong-Password-2026!', undefined, auditor, false);
75
- expect(loginResultNoRem.type).toBe('success');
76
- const loginSuccessResultNoRem = loginResultNoRem.result;
77
- expect(loginSuccessResultNoRem.remember).toBe(false);
78
- const refreshResultNoRem = await authenticationService.refresh(loginSuccessResultNoRem.refreshToken, undefined, {}, auditor);
79
- expect(refreshResultNoRem.remember).toBe(false);
80
- const newRefreshTokenNoRem = await authenticationService.validateRefreshToken(refreshResultNoRem.refreshToken);
81
- expect(newRefreshTokenNoRem.payload.remember).toBe(false);
82
- });
83
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,140 +0,0 @@
1
- import { beforeAll, beforeEach, describe, expect, test } from 'vitest';
2
- import { NIL_UUID } from '../../constants.js';
3
- import { runInInjectionContext } from '../../injector/index.js';
4
- import { clearTenantData, setupIntegrationTest } from '../../testing/index.js';
5
- import { SubjectStatus } from '../models/index.js';
6
- import { SubjectService } from '../server/subject.service.js';
7
- describe('SubjectService', () => {
8
- let injector;
9
- let database;
10
- let subjectService;
11
- const schema = 'authentication';
12
- const tenantId = crypto.randomUUID();
13
- beforeAll(async () => {
14
- ({ injector, database } = await setupIntegrationTest({ modules: { authentication: true } }));
15
- subjectService = injector.resolve(SubjectService);
16
- });
17
- beforeEach(async () => {
18
- await clearTenantData(database, schema, ['user', 'service_account', 'system_account', 'subject'], tenantId);
19
- });
20
- test('createUser should create a user', async () => {
21
- await runInInjectionContext(injector, async () => {
22
- const user = await subjectService.createUser({
23
- tenantId,
24
- email: 'test@example.com',
25
- firstName: 'John',
26
- lastName: 'Doe',
27
- });
28
- expect(user.id).toBeDefined();
29
- expect(user.email).toBe('test@example.com');
30
- expect(user.status).toBe(SubjectStatus.Active);
31
- });
32
- });
33
- test('exists should return true if subject exists', async () => {
34
- await runInInjectionContext(injector, async () => {
35
- const user = await subjectService.createUser({
36
- tenantId,
37
- email: 'test@example.com',
38
- firstName: 'John',
39
- lastName: 'Doe',
40
- });
41
- expect(await subjectService.exists(tenantId, user.id)).toBe(true);
42
- expect(await subjectService.exists(tenantId, NIL_UUID)).toBe(false);
43
- const found = await subjectService.hasUserByEmail(tenantId, 'test@example.com');
44
- const notFound = await subjectService.hasUserByEmail(tenantId, 'missing@example.com');
45
- expect(found).toBe(true);
46
- expect(notFound).toBe(false);
47
- });
48
- });
49
- test('deleteUser should soft delete a user', async () => {
50
- await runInInjectionContext(injector, async () => {
51
- const user = await subjectService.createUser({
52
- tenantId,
53
- email: 'test@example.com',
54
- firstName: 'John',
55
- lastName: 'Doe',
56
- });
57
- expect(await subjectService.exists(tenantId, user.id)).toBe(true);
58
- await subjectService.deleteUser(tenantId, user.id);
59
- const deletedUser = await subjectService.getSubject(user.id, { withDeleted: true });
60
- expect(deletedUser.metadata.deleteTimestamp).not.toBeNull();
61
- });
62
- });
63
- test('listUsers should list all non-deleted users in a tenant', async () => {
64
- await runInInjectionContext(injector, async () => {
65
- await subjectService.createUser({
66
- tenantId,
67
- email: 'user1@example.com',
68
- firstName: 'User',
69
- lastName: 'One',
70
- });
71
- const user2 = await subjectService.createUser({
72
- tenantId,
73
- email: 'user2@example.com',
74
- firstName: 'User',
75
- lastName: 'Two',
76
- });
77
- await subjectService.deleteUser(tenantId, user2.id);
78
- const users = await subjectService.listUsers(tenantId);
79
- expect(users).toHaveLength(1);
80
- expect(users[0]?.email).toBe('user1@example.com');
81
- });
82
- });
83
- test('getSystemAccountSubject should create or return system account', async () => {
84
- await runInInjectionContext(injector, async () => {
85
- const sa1 = await subjectService.getSystemAccount(tenantId, 'test-system');
86
- expect(sa1.id).toBeDefined();
87
- expect(sa1.identifier).toBe('test-system');
88
- const sa2 = await subjectService.getSystemAccount(tenantId, 'test-system');
89
- expect(sa2.id).toBe(sa1.id);
90
- });
91
- });
92
- test('updateUser should update user details', async () => {
93
- await runInInjectionContext(injector, async () => {
94
- const user = await subjectService.createUser({ tenantId, email: 'update@example.com', firstName: 'Old', lastName: 'Name' });
95
- await subjectService.updateUser(tenantId, user.id, { firstName: 'New', status: SubjectStatus.Suspended });
96
- const updated = await subjectService.getUser(tenantId, user.id);
97
- expect(updated.firstName).toBe('New');
98
- expect(updated.lastName).toBe('Name');
99
- expect(updated.status).toBe(SubjectStatus.Suspended);
100
- });
101
- });
102
- test('updateUserEmail should update user email', async () => {
103
- await runInInjectionContext(injector, async () => {
104
- const user = await subjectService.createUser({ tenantId, email: 'email@example.com', firstName: 'E', lastName: 'M' });
105
- await subjectService.updateUserEmail(tenantId, user.id, 'new-email@example.com');
106
- const updated = await subjectService.getUserByEmail(tenantId, 'new-email@example.com');
107
- expect(updated.id).toBe(user.id);
108
- await expect(subjectService.updateUserEmail(tenantId, user.id, 'invalid-email')).rejects.toThrow();
109
- });
110
- });
111
- test('service accounts should be manageable', async () => {
112
- await runInInjectionContext(injector, async () => {
113
- const sa = await subjectService.createServiceAccount({ tenantId, displayName: 'SA', description: 'Desc', parent: null });
114
- expect(sa.displayName).toBe('SA');
115
- await subjectService.updateServiceAccount(tenantId, sa.id, { displayName: 'New SA' });
116
- const updated = await subjectService.getServiceAccount(tenantId, sa.id);
117
- expect(updated.displayName).toBe('New SA');
118
- const saBySubject = await subjectService.getServiceAccountBySubject(updated);
119
- expect(saBySubject.id).toBe(sa.id);
120
- const list = await subjectService.listServiceAccounts(tenantId);
121
- expect(list).toHaveLength(1);
122
- await subjectService.deleteServiceAccount(tenantId, sa.id);
123
- expect(await subjectService.exists(tenantId, sa.id)).toBe(false);
124
- });
125
- });
126
- test('deleteUser should remove user', async () => {
127
- await runInInjectionContext(injector, async () => {
128
- const user = await subjectService.createUser({ tenantId, email: 'delete@example.com', firstName: 'D', lastName: 'E' });
129
- await subjectService.deleteUser(tenantId, user.id);
130
- expect(await subjectService.exists(tenantId, user.id)).toBe(false);
131
- });
132
- });
133
- test('getSystemAccountSubject should load or create system account', async () => {
134
- await runInInjectionContext(injector, async () => {
135
- const sa1 = await subjectService.getSystemAccount(tenantId, 'sys1');
136
- const sa2 = await subjectService.getSystemAccount(tenantId, 'sys1');
137
- expect(sa1.id).toBe(sa2.id);
138
- });
139
- });
140
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,120 +0,0 @@
1
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from 'vitest';
2
- import { ActorType, Auditor } from '../../audit/index.js';
3
- import { clearTenantData, setupIntegrationTest } from '../../testing/index.js';
4
- import { SubjectStatus } from '../models/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('Suspended Subject Authentication', () => {
10
- let injector;
11
- let database;
12
- let authenticationService;
13
- let subjectService;
14
- let auditor;
15
- const schema = 'authentication';
16
- const tenantId = crypto.randomUUID();
17
- beforeAll(async () => {
18
- ({ injector, database } = await setupIntegrationTest({
19
- modules: { authentication: true },
20
- authentication: {
21
- ancillaryService: DefaultAuthenticationAncillaryService,
22
- },
23
- }));
24
- authenticationService = await injector.resolveAsync(AuthenticationService);
25
- subjectService = await injector.resolveAsync(SubjectService);
26
- auditor = injector.resolve(Auditor);
27
- });
28
- afterAll(async () => {
29
- await injector?.dispose();
30
- });
31
- beforeEach(async () => {
32
- await clearTenantData(database, schema, ['totp_recovery_code', 'totp', 'password', 'session', 'user', 'service_account', 'system_account', 'subject'], tenantId);
33
- });
34
- test('login should FAIL for suspended user', async () => {
35
- const user = await subjectService.createUser({
36
- tenantId,
37
- email: 'suspended@example.com',
38
- firstName: 'Suspended',
39
- lastName: 'User',
40
- status: SubjectStatus.Suspended,
41
- });
42
- await authenticationService.setPassword(user, 'Strong-Password-2026!');
43
- const loginPromise = authenticationService.login({ tenantId, subject: user.id }, 'Strong-Password-2026!', undefined, auditor.with({ actor: user.id, actorType: ActorType.Subject }));
44
- await expect(loginPromise).rejects.toThrow('Invalid credentials.');
45
- });
46
- test('refresh should FAIL for suspended user', async () => {
47
- const user = await subjectService.createUser({
48
- tenantId,
49
- email: 'suspended-refresh@example.com',
50
- firstName: 'Suspended',
51
- lastName: 'Refresh',
52
- status: SubjectStatus.Active, // Start active to get a token
53
- });
54
- await authenticationService.setPassword(user, 'Strong-Password-2026!');
55
- const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
56
- const loginResult = await authenticationService.login({ tenantId, subject: user.id }, 'Strong-Password-2026!', undefined, userAuditor);
57
- expect(loginResult.type).toBe('success');
58
- // Now suspend the user
59
- await subjectService.updateUser(tenantId, user.id, { status: SubjectStatus.Suspended });
60
- const refreshPromise = authenticationService.refresh(loginResult.result.refreshToken, undefined, {}, userAuditor);
61
- await expect(refreshPromise).rejects.toThrow('Subject is suspended.');
62
- });
63
- test('impersonate should FAIL if target is suspended', async () => {
64
- const admin = await subjectService.createUser({ tenantId, email: 'admin@example.com', firstName: 'A', lastName: 'D' });
65
- const user = await subjectService.createUser({
66
- tenantId,
67
- email: 'suspended-target@example.com',
68
- firstName: 'S',
69
- lastName: 'T',
70
- status: SubjectStatus.Suspended,
71
- });
72
- const adminAuditor = auditor.with({ actor: admin.id, actorType: ActorType.Subject });
73
- const adminToken = await authenticationService.getToken(admin, undefined);
74
- const impersonatePromise = authenticationService.impersonate(adminToken.token, adminToken.refreshToken, user.id, undefined, adminAuditor);
75
- await expect(impersonatePromise).rejects.toThrow('Subject is suspended.');
76
- });
77
- test('changePassword should FAIL for suspended user', async () => {
78
- const user = await subjectService.createUser({
79
- tenantId,
80
- email: 'suspended-change@example.com',
81
- firstName: 'S',
82
- lastName: 'C',
83
- status: SubjectStatus.Suspended,
84
- });
85
- await authenticationService.setPassword(user, 'Old-Password-2026!');
86
- const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
87
- const changePasswordPromise = authenticationService.changePassword({ tenantId, subject: user.id }, 'Old-Password-2026!', 'New-Password-2026!', userAuditor);
88
- await expect(changePasswordPromise).rejects.toThrow('Invalid credentials.');
89
- });
90
- test('resetPassword should FAIL for suspended user', async () => {
91
- const user = await subjectService.createUser({
92
- tenantId,
93
- email: 'suspended-reset@example.com',
94
- firstName: 'S',
95
- lastName: 'R',
96
- status: SubjectStatus.Active,
97
- });
98
- const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
99
- // @ts-ignore - access private for test or use public init
100
- const passwordResetToken = await authenticationService.createPasswordResetToken(user, Date.now() + 100000);
101
- // Suspend after token issue
102
- await subjectService.updateUser(tenantId, user.id, { status: SubjectStatus.Suspended });
103
- const resetPasswordPromise = authenticationService.resetPassword(passwordResetToken.token, 'New-Password-Reset-2026!', userAuditor);
104
- await expect(resetPasswordPromise).rejects.toThrow('Subject is suspended.');
105
- });
106
- test('initPasswordReset should SILENTLY IGNORE suspended user', async () => {
107
- const user = await subjectService.createUser({
108
- tenantId,
109
- email: 'suspended-init-reset@example.com',
110
- firstName: 'S',
111
- lastName: 'IR',
112
- status: SubjectStatus.Suspended,
113
- });
114
- const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
115
- const ancillaryService = await injector.resolveAsync(AuthenticationAncillaryService);
116
- ancillaryService.lastResetData = undefined;
117
- await authenticationService.initPasswordReset({ tenantId, subject: user.id }, undefined, userAuditor);
118
- expect(ancillaryService.lastResetData).toBeUndefined();
119
- });
120
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,123 +0,0 @@
1
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from 'vitest';
2
- import { ActorType, Auditor } from '../../audit/index.js';
3
- import { importHmacKey, importKey } from '../../cryptography/index.js';
4
- import { generateTotpToken } from '../../cryptography/totp.js';
5
- import { clearTenantData, setupIntegrationTest } from '../../testing/index.js';
6
- import { currentTimestampSeconds } from '../../utils/date-time.js';
7
- import { TotpStatus } from '../models/authentication-totp.model.js';
8
- import { AuthenticationService } from '../server/authentication.service.js';
9
- import { SubjectService } from '../server/subject.service.js';
10
- import { DefaultAuthenticationAncillaryService } from './authentication.test-ancillary-service.js';
11
- describe('TOTP Enrollment', () => {
12
- let injector;
13
- let database;
14
- let authenticationService;
15
- let subjectService;
16
- let auditor;
17
- const schema = 'authentication';
18
- const tenantId = crypto.randomUUID();
19
- beforeAll(async () => {
20
- ({ injector, database } = await setupIntegrationTest({
21
- modules: { authentication: true },
22
- authentication: {
23
- ancillaryService: DefaultAuthenticationAncillaryService,
24
- },
25
- }));
26
- authenticationService = await injector.resolveAsync(AuthenticationService);
27
- subjectService = await injector.resolveAsync(SubjectService);
28
- auditor = injector.resolve(Auditor);
29
- });
30
- afterAll(async () => {
31
- await injector?.dispose();
32
- });
33
- beforeEach(async () => {
34
- await clearTenantData(database, schema, ['used_totp_tokens', 'totp_recovery_code', 'totp', 'password', 'session', 'user', 'service_account', 'system_account', 'subject'], tenantId);
35
- });
36
- test('initEnrollTotp should initiate enrollment', async () => {
37
- const user = await subjectService.createUser({
38
- tenantId,
39
- email: 'totp-test@example.com',
40
- firstName: 'Totp',
41
- lastName: 'User',
42
- });
43
- const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
44
- const result = await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
45
- expect(result.secret).toBeDefined();
46
- expect(result.uri).toContain('otpauth://totp/');
47
- expect(result.uri).toContain('secret=' + result.secret);
48
- const totpEntry = await authenticationService.tryGetTotp(tenantId, user.id);
49
- expect(totpEntry).toBeDefined();
50
- expect(totpEntry.status).toBe(TotpStatus.Pending);
51
- });
52
- test('completeEnrollTotp should activate TOTP after verification', async () => {
53
- const user = await subjectService.createUser({
54
- tenantId,
55
- email: 'totp-test-complete@example.com',
56
- firstName: 'Totp',
57
- lastName: 'Complete',
58
- });
59
- const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
60
- const { secret } = await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
61
- // We need to convert base32 secret back to Uint8Array for generateTotpToken if it's base32
62
- // But the utility probably expects Uint8Array or we use the utility's own secret generation
63
- // Actually initEnrollTotp returns base32 secret for the user to enter in app.
64
- // For testing, let's assume we can get the raw secret from the DB or similar.
65
- const totpEntry = await authenticationService.tryGetTotp(tenantId, user.id);
66
- const secretKey = await importKey('raw-secret', totpEntry.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
67
- const token = await generateTotpToken(secretKey, authenticationService.getTotpOptions());
68
- const completeResult = await authenticationService.completeEnrollTotp(tenantId, user.id, token, userAuditor);
69
- expect(completeResult.recoveryCodes).toHaveLength(10);
70
- const activeTotpEntry = await authenticationService.tryGetTotp(tenantId, user.id);
71
- expect(activeTotpEntry.status).toBe(TotpStatus.Active);
72
- });
73
- test('disableTotp should deactivate TOTP', async () => {
74
- const user = await subjectService.createUser({
75
- tenantId,
76
- email: 'totp-test-disable@example.com',
77
- firstName: 'Totp',
78
- lastName: 'Disable',
79
- });
80
- const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
81
- await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
82
- const totpEntry = await authenticationService.tryGetTotp(tenantId, user.id);
83
- const secretKey = await importKey('raw-secret', totpEntry.secret, { name: 'HMAC', hash: authenticationService.getTotpOptions().codeHashAlgorithm }, false, ['sign']);
84
- const token = await generateTotpToken(secretKey, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() - 30 });
85
- await authenticationService.completeEnrollTotp(tenantId, user.id, token, userAuditor);
86
- const token2 = await generateTotpToken(secretKey, authenticationService.getTotpOptions());
87
- await authenticationService.disableTotp(tenantId, user.id, token2, userAuditor);
88
- const disabledTotpEntry = await authenticationService.tryGetTotp(tenantId, user.id);
89
- expect(disabledTotpEntry).toBeUndefined();
90
- });
91
- test('disableTotp should fail with recovery code (decoupled)', async () => {
92
- const user = await subjectService.createUser({
93
- tenantId,
94
- email: 'totp-test-disable-fail@example.com',
95
- firstName: 'Totp',
96
- lastName: 'DisableFail',
97
- });
98
- const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
99
- await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
100
- const totpEntry = (await authenticationService.tryGetTotp(tenantId, user.id));
101
- const secretKey = await importHmacKey('raw-secret', authenticationService.getTotpOptions().codeHashAlgorithm, totpEntry.secret, false);
102
- const token = await generateTotpToken(secretKey, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() - 30 });
103
- const { recoveryCodes } = await authenticationService.completeEnrollTotp(tenantId, user.id, token, userAuditor);
104
- await expect(authenticationService.disableTotp(tenantId, user.id, recoveryCodes[0], userAuditor)).rejects.toThrow();
105
- });
106
- test('disableTotpWithRecoveryCode should deactivate TOTP', async () => {
107
- const user = await subjectService.createUser({
108
- tenantId,
109
- email: 'totp-test-disable-recovery@example.com',
110
- firstName: 'Totp',
111
- lastName: 'DisableRecovery',
112
- });
113
- const userAuditor = auditor.with({ actor: user.id, actorType: ActorType.Subject });
114
- await authenticationService.initEnrollTotp(tenantId, user.id, userAuditor);
115
- const totpEntry = (await authenticationService.tryGetTotp(tenantId, user.id));
116
- const secretKey = await importHmacKey('raw-secret', authenticationService.getTotpOptions().codeHashAlgorithm, totpEntry.secret, false);
117
- const token = await generateTotpToken(secretKey, { ...authenticationService.getTotpOptions(), timestamp: currentTimestampSeconds() - 30 });
118
- const { recoveryCodes } = await authenticationService.completeEnrollTotp(tenantId, user.id, token, userAuditor);
119
- await authenticationService.disableTotpWithRecoveryCode(tenantId, user.id, recoveryCodes[0], userAuditor);
120
- const disabledTotpEntry = await authenticationService.tryGetTotp(tenantId, user.id);
121
- expect(disabledTotpEntry).toBeUndefined();
122
- });
123
- });
@@ -1 +0,0 @@
1
- export {};