@futdevpro/nts-dynamo 1.15.73 → 1.15.75

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 (365) hide show
  1. package/.c8rc.json +26 -26
  2. package/.copilot/patterns.json +7 -7
  3. package/.cursor/rules/__assistant_guide.mdc +30 -30
  4. package/.cursor/rules/_ag_backend-structure.mdc +85 -85
  5. package/.cursor/rules/_ag_backend.mdc +16 -16
  6. package/.cursor/rules/_ag_frontend-structure.mdc +86 -86
  7. package/.cursor/rules/_ag_frontend.mdc +39 -39
  8. package/.cursor/rules/_ag_import-rules.mdc +44 -44
  9. package/.cursor/rules/_ag_naming.mdc +115 -115
  10. package/.cursor/rules/_ag_should-be.mdc +6 -6
  11. package/.cursor/rules/ai_development_guide.md +60 -60
  12. package/.cursor/rules/cursor-rules.md +160 -160
  13. package/.cursor/rules/default-command.mdc +464 -464
  14. package/.cursor/rules/error_code_pattern.md +39 -39
  15. package/.cursor/rules/saved rule mcp server use.md +15 -15
  16. package/.dynamo/logs/cicd-pipeline/output.log +2798 -0
  17. package/.dynamo/logs/cicd-pipeline/status.json +94 -0
  18. package/.vscode/settings.json +10 -10
  19. package/HOWTO.md +15 -15
  20. package/LICENSE +21 -21
  21. package/__documentations/nts-integration-tests-2026-03-17.md +26 -26
  22. package/_specifications/BACKLOG.md +92 -92
  23. package/_specifications/TODO.md +15 -15
  24. package/_specifications/agent.md +138 -138
  25. package/build/_collections/global-settings.const.d.ts.map +1 -1
  26. package/build/_collections/global-settings.const.js +1 -0
  27. package/build/_collections/global-settings.const.js.map +1 -1
  28. package/build/_models/interfaces/global-settings.interface.d.ts +7 -0
  29. package/build/_models/interfaces/global-settings.interface.d.ts.map +1 -1
  30. package/build/_services/core/memory-guard.service.d.ts +5 -0
  31. package/build/_services/core/memory-guard.service.d.ts.map +1 -1
  32. package/build/_services/core/memory-guard.service.js +12 -3
  33. package/build/_services/core/memory-guard.service.js.map +1 -1
  34. package/eslint.config.js +3 -3
  35. package/nodemon.json +24 -24
  36. package/package.json +2 -2
  37. package/pnpm-workspace.yaml +5 -5
  38. package/scripts/run-coverage-tests.js +28 -28
  39. package/spec/support/helpers/spec-reporter-loader.js +359 -359
  40. package/spec/support/helpers/ts-node-helper.js +93 -93
  41. package/spec/support/jasmine.coverage.json +24 -24
  42. package/spec/support/jasmine.json +24 -24
  43. package/src/_collections/archive.util.spec.ts +57 -57
  44. package/src/_collections/archive.util.ts +18 -18
  45. package/src/_collections/atlas-default-db-options.const.ts +9 -9
  46. package/src/_collections/default-fallback-cache-max-age.const.spec.ts +11 -11
  47. package/src/_collections/default-fallback-cache-max-age.const.ts +2 -2
  48. package/src/_collections/default-not-found-page.const.spec.ts +19 -19
  49. package/src/_collections/default-not-found-page.const.ts +22 -22
  50. package/src/_collections/default-socket-path.const.spec.ts +12 -12
  51. package/src/_collections/default-socket-path.const.ts +2 -2
  52. package/src/_collections/get-environment-settings.util.spec.ts +210 -210
  53. package/src/_collections/get-environment-settings.util.ts +48 -48
  54. package/src/_collections/global-settings.const.ts +97 -96
  55. package/src/_collections/sample.env +21 -21
  56. package/src/_collections/star.controller.spec.ts +224 -224
  57. package/src/_collections/star.controller.ts +129 -129
  58. package/src/_enums/data-model-type.enum.ts +14 -14
  59. package/src/_enums/data-service-function.enum.ts +24 -24
  60. package/src/_enums/predefined-data-types.enum.ts +16 -16
  61. package/src/_enums/route-security.enum.ts +12 -12
  62. package/src/_models/control-models/api-call-params.control-model.spec.ts +152 -152
  63. package/src/_models/control-models/api-call-params.control-model.ts +142 -142
  64. package/src/_models/control-models/app-ext-system-controls.control-model.spec.ts +52 -52
  65. package/src/_models/control-models/app-ext-system-controls.control-model.ts +9 -9
  66. package/src/_models/control-models/app-params.control-model.spec.ts +225 -225
  67. package/src/_models/control-models/app-params.control-model.ts +136 -136
  68. package/src/_models/control-models/app-system-controls.control-model.spec.ts +31 -31
  69. package/src/_models/control-models/app-system-controls.control-model.ts +9 -9
  70. package/src/_models/control-models/endpoint-params.control-model.spec.ts +627 -627
  71. package/src/_models/control-models/endpoint-params.control-model.ts +627 -627
  72. package/src/_models/control-models/http-settings.control-model.spec.ts +77 -77
  73. package/src/_models/control-models/http-settings.control-model.ts +37 -37
  74. package/src/_models/control-models/system-control.control-model.spec.ts +27 -27
  75. package/src/_models/control-models/system-control.control-model.ts +12 -12
  76. package/src/_models/interfaces/certification-settings.interface.ts +7 -7
  77. package/src/_models/interfaces/environment-settings.interface.ts +59 -59
  78. package/src/_models/interfaces/global-log-settings.interface.ts +171 -171
  79. package/src/_models/interfaces/global-service-settings.interface.ts +47 -47
  80. package/src/_models/interfaces/global-settings.interface.ts +7 -0
  81. package/src/_models/interfaces/routing-module-settings.interface.ts +21 -21
  82. package/src/_models/interfaces/static-client-settings.interface.spec.ts +29 -29
  83. package/src/_models/interfaces/static-client-settings.interface.ts +28 -28
  84. package/src/_models/types/db-update.type.ts +100 -100
  85. package/src/_modules/ai/_models/ai-input-interfaces.ts +117 -117
  86. package/src/_modules/ai/_models/ai-test-generation-result.interface.ts +16 -16
  87. package/src/_modules/ai/_modules/anthropic/_services/aai-user-key.control-service.ts +138 -138
  88. package/src/_modules/ai/_modules/anthropic/index.ts +5 -5
  89. package/src/_modules/ai/_modules/document-ai/_collections/dai-chunking.util.spec.ts +242 -242
  90. package/src/_modules/ai/_modules/document-ai/_collections/dai-chunking.util.ts +639 -639
  91. package/src/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.spec.ts +295 -295
  92. package/src/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.ts +518 -518
  93. package/src/_modules/ai/_modules/document-ai/_collections/dai-document.util.spec.ts +209 -209
  94. package/src/_modules/ai/_modules/document-ai/_collections/dai-document.util.ts +85 -85
  95. package/src/_modules/ai/_modules/document-ai/_enums/dai-compare-result-type.enum.ts +7 -7
  96. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-doc-chunk.data-model.ts +146 -146
  97. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-doc-page.data-model.ts +162 -162
  98. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-document.data-model.ts +99 -99
  99. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-code-chunk.interface.ts +68 -68
  100. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-doc-chunk-compare-result.interface.ts +18 -18
  101. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-doc-page-compare-result.interface.ts +19 -19
  102. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-document-compare-result.interface.ts +25 -25
  103. package/src/_modules/ai/_modules/document-ai/index.ts +30 -30
  104. package/src/_modules/ai/_modules/fdp-ai/_services/fdpai-user-key.control-service.ts +189 -189
  105. package/src/_modules/ai/_modules/fdp-ai/index.ts +5 -5
  106. package/src/_modules/ai/_modules/open-ai/_collections/oai-global-settings.const.ts +9 -9
  107. package/src/_modules/ai/_modules/open-ai/_collections/oai-llm-predefined-requests-hu.conts.ts +82 -82
  108. package/src/_modules/ai/_modules/open-ai/_collections/oai-llm-predefined-requests.conts.ts +75 -75
  109. package/src/_modules/ai/_modules/open-ai/_enums/oai-gpt-message-role.enum.ts +45 -45
  110. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-global-settings.interface.ts +7 -7
  111. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-gpt-message.interface.ts +7 -7
  112. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-llm-predefined-requests.interface.ts +57 -57
  113. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-doc-chunk-data.service.ts +292 -292
  114. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-document.data-service.spec.ts +342 -342
  115. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-vector-data.service.spec.ts +550 -550
  116. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-vector-data.service.ts +630 -630
  117. package/src/_modules/ai/_modules/open-ai/_services/oai-embedding.control-service.spec.ts +332 -332
  118. package/src/_modules/ai/_modules/open-ai/_services/oai-llm-chat.service-base.spec.ts +462 -462
  119. package/src/_modules/ai/_modules/open-ai/_services/oai-llm-chat.service-base.ts +634 -634
  120. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.spec.ts +489 -489
  121. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.tools.spec.ts +173 -173
  122. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.ts +1033 -1033
  123. package/src/_modules/ai/_modules/open-ai/_services/oai-user-key.control-service.ts +157 -157
  124. package/src/_modules/ai/_services/ai-embedding-mock.service.spec.ts +115 -115
  125. package/src/_modules/ai/_services/ai-embedding-mock.service.ts +212 -212
  126. package/src/_modules/ai/_services/ai-embedding-provider.registry.spec.ts +110 -110
  127. package/src/_modules/ai/_services/ai-embedding-provider.registry.ts +110 -110
  128. package/src/_modules/ai/_services/ai-embedding.service-base.spec.ts +98 -98
  129. package/src/_modules/ai/_services/ai-embedding.service-base.ts +48 -48
  130. package/src/_modules/ai/_services/ai-llm-chat.service-base.spec.ts +229 -229
  131. package/src/_modules/ai/_services/ai-llm-chat.service-base.ts +68 -68
  132. package/src/_modules/ai/_services/ai-llm.service-base.spec.ts +250 -250
  133. package/src/_modules/ai/_services/ai-llm.service-base.ts +519 -519
  134. package/src/_modules/ai/_services/ai-provider.service-base.spec.ts +158 -158
  135. package/src/_modules/ai/_services/ai-user-key.service-base.ts +59 -59
  136. package/src/_modules/ai/_services/lmstudio-embedding.control-service.spec.ts +197 -197
  137. package/src/_modules/ai/_services/lmstudio-embedding.control-service.ts +371 -371
  138. package/src/_modules/ai/index.ts +23 -23
  139. package/src/_modules/assistant/_collections/ass-global-settings.const.ts +13 -13
  140. package/src/_modules/assistant/_collections/ass.util.spec.ts +176 -176
  141. package/src/_modules/assistant/_collections/ass.util.ts +50 -50
  142. package/src/_modules/assistant/_models/ass-global-settings.interface.ts +15 -15
  143. package/src/_modules/assistant/_services/ass-io.control-service.spec.ts +140 -140
  144. package/src/_modules/assistant/_services/ass-main.control-service.spec.ts +192 -192
  145. package/src/_modules/assistant/_services/ass-main.control-service.ts +107 -107
  146. package/src/_modules/bot/_collections/bot-default-commands.const.ts +12 -12
  147. package/src/_modules/bot/_collections/bot-global-settings.const.ts +39 -39
  148. package/src/_modules/bot/_models/bot-channel-wrapper.interface.ts +62 -62
  149. package/src/_modules/bot/_models/bot-command.interface.ts +8 -8
  150. package/src/_modules/bot/_models/bot-global-settings.interface.ts +96 -96
  151. package/src/_modules/bot/_models/bot-last-mention-date.interface.ts +6 -6
  152. package/src/_modules/bot/_models/bot-last-message-date.interface.ts +5 -5
  153. package/src/_modules/bot/_models/bot-user-wrapper.interface.ts +41 -41
  154. package/src/_modules/bot/_modules/discord-bot/_models/dib-platform.types.ts +9 -9
  155. package/src/_modules/bot/_modules/discord-bot/_services/dib-messaging-provider.control-service.spec.ts +431 -431
  156. package/src/_modules/bot/_modules/dynamo-bot/_collections/dyb-operations.util.spec.ts +160 -160
  157. package/src/_modules/bot/_modules/dynamo-bot/_collections/dyb-operations.util.ts +55 -55
  158. package/src/_modules/bot/_modules/dynamo-bot/_models/dyb-platform.types.ts +15 -15
  159. package/src/_modules/bot/_modules/dynamo-bot/_services/dyb-messaging-provider.control-service.spec.ts +374 -374
  160. package/src/_modules/bot/_modules/dynamo-bot/_services/dyb-messaging-provider.control-service.ts +447 -447
  161. package/src/_modules/bot/_modules/dynamo-bot/index.ts +15 -15
  162. package/src/_modules/bot/_modules/slack-bot/_models/slb-platform.types.ts +9 -9
  163. package/src/_modules/bot/_modules/slack-bot/_services/slb-messaging-provider.control-service.spec.ts +344 -344
  164. package/src/_modules/bot/_modules/slack-bot/_services/slb-messaging-provider.control-service.ts +197 -197
  165. package/src/_modules/bot/_modules/teams-bot/_models/teb-platform.types.ts +9 -9
  166. package/src/_modules/bot/_modules/teams-bot/_services/teb-messaging-provider.control-service.spec.ts +345 -345
  167. package/src/_modules/bot/_modules/teams-bot/_services/teb-messaging-provider.control-service.ts +197 -197
  168. package/src/_modules/bot/_services/bot-commands.control-service.spec.ts +116 -116
  169. package/src/_modules/bot/_services/bot-io.control-service.spec.ts +285 -285
  170. package/src/_modules/bot/_services/bot-main.control-service.spec.ts +208 -208
  171. package/src/_modules/bot/_services/bot-messaging-provider.service-base.spec.ts +349 -349
  172. package/src/_modules/bot/_services/bot-routines.control-service.spec.ts +111 -111
  173. package/src/_modules/custom-data/custom-data.controller.spec.ts +49 -49
  174. package/src/_modules/custom-data/custom-data.controller.ts +67 -67
  175. package/src/_modules/custom-data/custom-data.data-service.spec.ts +54 -54
  176. package/src/_modules/custom-data/custom-data.data-service.ts +21 -21
  177. package/src/_modules/custom-data/get-custom-data-routing-module.util.spec.ts +28 -28
  178. package/src/_modules/custom-data/get-custom-data-routing-module.util.ts +24 -24
  179. package/src/_modules/custom-data/index.ts +9 -9
  180. package/src/_modules/data-readers/_collections/dynts-sqlite-reader.util.spec.ts +161 -161
  181. package/src/_modules/data-readers/_collections/dynts-sqlite-reader.util.ts +203 -203
  182. package/src/_modules/data-readers/_models/interfaces/dynts-sqlite-reader.interface.ts +33 -33
  183. package/src/_modules/data-readers/index.ts +11 -11
  184. package/src/_modules/defaults/_collections/default-endpoints.util.ts +487 -487
  185. package/src/_modules/defaults/_models/default-user.data-model.ts +72 -72
  186. package/src/_modules/defaults/_services/default-auth.service.spec.ts +269 -269
  187. package/src/_modules/defaults/_services/default-auth.service.ts +177 -177
  188. package/src/_modules/defaults/_services/default-socket-events.service.spec.ts +42 -42
  189. package/src/_modules/defaults/_services/default-socket-events.service.ts +61 -61
  190. package/src/_modules/defaults/_services/default-user.data-service.spec.ts +187 -187
  191. package/src/_modules/defaults/_services/default-user.data-service.ts +98 -98
  192. package/src/_modules/defaults/index.ts +17 -17
  193. package/src/_modules/discord-assistant/_collections/dias-global-settings.const.ts +19 -19
  194. package/src/_modules/discord-assistant/_collections/dias.util.spec.ts +366 -366
  195. package/src/_modules/discord-assistant/_collections/dias.util.ts +132 -132
  196. package/src/_modules/discord-assistant/_models/dias-global-settings.interface.ts +19 -19
  197. package/src/_modules/discord-assistant/_models/dias-knowledge.data-model.ts +52 -52
  198. package/src/_modules/discord-assistant/_services/dias-chunk.data-service.ts +177 -177
  199. package/src/_modules/discord-assistant/_services/dias-io.control-service.spec.ts +108 -108
  200. package/src/_modules/discord-assistant/_services/dias-io.control-service.ts +69 -69
  201. package/src/_modules/discord-assistant/_services/dias-main.control-service.spec.ts +22 -22
  202. package/src/_modules/discord-assistant/_services/dias-main.control-service.ts +27 -27
  203. package/src/_modules/discord-assistant/_services/dias.service-base.spec.ts +195 -195
  204. package/src/_modules/discord-assistant/_services/dias.service-base.ts +76 -76
  205. package/src/_modules/discord-assistant/index.ts +38 -38
  206. package/src/_modules/discord-assistant-voiced/_services/dias-discord-bot.control-service.spec.ts +34 -34
  207. package/src/_modules/discord-assistant-voiced/_services/dias-discord-bot.control-service.ts +11 -11
  208. package/src/_modules/discord-assistant-voiced/index.ts +36 -36
  209. package/src/_modules/discord-bot/_collections/dibo-default-commands.const.ts +16 -16
  210. package/src/_modules/discord-bot/_collections/dibo-global-settings.conts.ts +55 -55
  211. package/src/_modules/discord-bot/_collections/dibo-operations.util.spec.ts +214 -214
  212. package/src/_modules/discord-bot/_collections/dibo-operations.util.ts +387 -387
  213. package/src/_modules/discord-bot/_models/dibo-command.interface.ts +12 -12
  214. package/src/_modules/discord-bot/_models/dibo-global-settings.interface.ts +98 -98
  215. package/src/_modules/discord-bot/_models/dibo-last-mention-date.inteface.ts +7 -7
  216. package/src/_modules/discord-bot/_models/dibo-last-message-date.interface.ts +6 -6
  217. package/src/_modules/discord-bot/_services/dibo-commands.control-service.spec.ts +154 -154
  218. package/src/_modules/discord-bot/_services/dibo-commands.control-service.ts +153 -153
  219. package/src/_modules/discord-bot/_services/dibo-io.control-service.spec.ts +264 -264
  220. package/src/_modules/discord-bot/_services/dibo-io.control-service.ts +306 -306
  221. package/src/_modules/discord-bot/_services/dibo-main.control-service.spec.ts +408 -408
  222. package/src/_modules/discord-bot/_services/dibo-main.control-service.ts +487 -487
  223. package/src/_modules/discord-bot/_services/dibo-routines.control-service.spec.ts +105 -105
  224. package/src/_modules/discord-bot/index.ts +36 -36
  225. package/src/_modules/local-vector-search/_enums/lvs-search-mode.enum.ts +35 -35
  226. package/src/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.ts +59 -59
  227. package/src/_modules/local-vector-search/_models/lvs-search-result.interface.ts +17 -17
  228. package/src/_modules/local-vector-search/_services/lvs-doc-chunk-data.service.spec.ts +418 -418
  229. package/src/_modules/local-vector-search/_services/lvs-doc-chunk-data.service.ts +276 -276
  230. package/src/_modules/local-vector-search/_services/lvs-local-vector-search.data-service.spec.ts +480 -480
  231. package/src/_modules/local-vector-search/_services/lvs-local-vector-search.data-service.ts +416 -416
  232. package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.spec.ts +198 -198
  233. package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.ts +146 -146
  234. package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.spec.ts +167 -167
  235. package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.ts +106 -106
  236. package/src/_modules/local-vector-search/_services/lvs-vector-pool.control-service.spec.ts +507 -507
  237. package/src/_modules/local-vector-search/_services/lvs-vector-pool.control-service.ts +272 -272
  238. package/src/_modules/local-vector-search/index.ts +16 -16
  239. package/src/_modules/logs/index.ts +11 -11
  240. package/src/_modules/mcp/_models/interfaces/dynts-mcp.interface.ts +111 -111
  241. package/src/_modules/mcp/_services/dynts-mcp-server.service-base.spec.ts +142 -142
  242. package/src/_modules/mcp/_services/dynts-mcp-server.service-base.ts +120 -120
  243. package/src/_modules/mcp/_services/dynts-mcp.adapter.ts +168 -168
  244. package/src/_modules/mcp/index.ts +13 -13
  245. package/src/_modules/messaging/README.md +354 -354
  246. package/src/_modules/messaging/_collections/get-messaging-routing-module.util.ts +26 -26
  247. package/src/_modules/messaging/_collections/msg-global-settings.const.ts +22 -22
  248. package/src/_modules/messaging/_collections/msg.util.spec.ts +226 -226
  249. package/src/_modules/messaging/_models/msg-global-settings.interface.ts +37 -37
  250. package/src/_modules/messaging/_services/msg-conversation.data-service.ts +146 -146
  251. package/src/_modules/messaging/_services/msg-events.service.spec.ts +219 -219
  252. package/src/_modules/messaging/_services/msg-events.service.ts +267 -267
  253. package/src/_modules/messaging/_services/msg-integration.control-service.ts +179 -179
  254. package/src/_modules/messaging/_services/msg-main.control-service.spec.ts +147 -147
  255. package/src/_modules/messaging/_services/msg-main.control-service.ts +571 -571
  256. package/src/_modules/messaging/_services/msg-message.data-service.ts +129 -129
  257. package/src/_modules/messaging/_services/msg.controller.spec.ts +201 -201
  258. package/src/_modules/messaging/index.ts +30 -30
  259. package/src/_modules/mock/app-extended-server.mock.ts +201 -201
  260. package/src/_modules/mock/app-integration-test.mock.ts +51 -51
  261. package/src/_modules/mock/app-params.mock.spec.ts +21 -21
  262. package/src/_modules/mock/app-params.mock.ts +9 -9
  263. package/src/_modules/mock/app-server.mock.ts +188 -188
  264. package/src/_modules/mock/auth-service.mock.spec.ts +47 -47
  265. package/src/_modules/mock/auth-service.mock.ts +28 -28
  266. package/src/_modules/mock/controller.mock.spec.ts +26 -26
  267. package/src/_modules/mock/controller.mock.ts +16 -16
  268. package/src/_modules/mock/data-model.mock.spec.ts +111 -111
  269. package/src/_modules/mock/data-model.mock.ts +82 -82
  270. package/src/_modules/mock/email-service-collection.mock.spec.ts +24 -24
  271. package/src/_modules/mock/email-service-collection.mock.ts +15 -15
  272. package/src/_modules/mock/email-service.mock.spec.ts +17 -17
  273. package/src/_modules/mock/email-service.mock.ts +20 -20
  274. package/src/_modules/mock/email-template.mock.html +14 -14
  275. package/src/_modules/mock/endpoint.mock.ts +91 -91
  276. package/src/_modules/mock/socket-client.mock.spec.ts +40 -40
  277. package/src/_modules/mock/socket-client.mock.ts +45 -45
  278. package/src/_modules/mock/socket-server.mock.spec.ts +44 -44
  279. package/src/_modules/mock/socket-server.mock.ts +46 -46
  280. package/src/_modules/oauth2/_routes/oauth2.controller.spec.ts +107 -107
  281. package/src/_modules/oauth2/_routes/oauth2.controller.ts +98 -98
  282. package/src/_modules/oauth2/_services/oauth2.auth-service.spec.ts +254 -254
  283. package/src/_modules/oauth2/_services/oauth2.auth-service.ts +232 -232
  284. package/src/_modules/oauth2/_services/oauth2.control-service.spec.ts +585 -585
  285. package/src/_modules/oauth2/_services/oauth2.control-service.ts +653 -653
  286. package/src/_modules/oauth2/index.ts +17 -17
  287. package/src/_modules/scoped-config/_enums/dynts-scoped-config-level.enum.ts +22 -22
  288. package/src/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.ts +81 -81
  289. package/src/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.ts +107 -107
  290. package/src/_modules/scoped-config/_services/dynts-scoped-config.control-service.spec.ts +306 -306
  291. package/src/_modules/scoped-config/_services/dynts-scoped-config.control-service.ts +295 -295
  292. package/src/_modules/scoped-config/_services/dynts-scoped-config.data-service.spec.ts +118 -118
  293. package/src/_modules/scoped-config/_services/dynts-scoped-config.data-service.ts +105 -105
  294. package/src/_modules/scoped-config/index.ts +17 -17
  295. package/src/_modules/server/errors/errors.control-service.spec.ts +238 -238
  296. package/src/_modules/server/errors/errors.control-service.ts +100 -100
  297. package/src/_modules/server/errors/errors.controller.spec.ts +241 -241
  298. package/src/_modules/server/errors/errors.controller.ts +489 -489
  299. package/src/_modules/server/errors/errors.data-service.spec.ts +480 -480
  300. package/src/_modules/server/index.ts +30 -30
  301. package/src/_modules/server/server-status/server-status-snapshot.control-service.spec.ts +70 -70
  302. package/src/_modules/server/server-status/server-status-snapshot.control-service.ts +17 -17
  303. package/src/_modules/server/server-status/server-status-snapshot.data-service.spec.ts +77 -77
  304. package/src/_modules/server/server-status/server-status-snapshot.data-service.ts +37 -37
  305. package/src/_modules/server/server-status/server-status.control-service.spec.ts +576 -576
  306. package/src/_modules/server/server-status/server-status.control-service.ts +396 -396
  307. package/src/_modules/server/server-status/server-status.controller.spec.ts +240 -240
  308. package/src/_modules/server/server-status/server-status.controller.ts +253 -253
  309. package/src/_modules/socket/_enums/socket-security.enum.ts +11 -11
  310. package/src/_modules/socket/_models/socket-client-service-params.control-model.spec.ts +32 -32
  311. package/src/_modules/socket/_models/socket-client-service-params.control-model.ts +22 -22
  312. package/src/_modules/socket/_models/socket-presence.control-model.spec.ts +164 -164
  313. package/src/_modules/socket/_models/socket-presence.control-model.ts +210 -210
  314. package/src/_modules/socket/_models/socket-server-service-params.control-model.spec.ts +46 -46
  315. package/src/_modules/socket/_models/socket-server-service-params.control-model.ts +22 -22
  316. package/src/_modules/socket/_services/socket-client.service.spec.ts +15 -15
  317. package/src/_modules/socket/_services/socket-client.service.ts +260 -260
  318. package/src/_modules/socket/_services/socket-server.service.spec.ts +11 -11
  319. package/src/_modules/socket/app-extended.integration.spec.ts +85 -85
  320. package/src/_modules/socket/app-extended.server.ts +630 -630
  321. package/src/_modules/socket/index.ts +42 -42
  322. package/src/_modules/test/get-test-routing-module.util.spec.ts +28 -28
  323. package/src/_modules/test/get-test-routing-module.util.ts +23 -23
  324. package/src/_modules/test/index.ts +11 -11
  325. package/src/_modules/test/test.controller.spec.ts +72 -72
  326. package/src/_modules/test/test.controller.ts +115 -115
  327. package/src/_modules/usage/get-usage-routing-module.util.ts +22 -22
  328. package/src/_modules/usage/index.ts +15 -15
  329. package/src/_modules/usage/usage.controller.spec.ts +81 -81
  330. package/src/_modules/usage/usage.controller.ts +126 -126
  331. package/src/_modules/usage/usage.data-service.spec.ts +332 -332
  332. package/src/_modules/usage/usage.data-service.ts +185 -185
  333. package/src/_services/base/api.service-base.spec.ts +125 -125
  334. package/src/_services/base/api.service-base.ts +74 -74
  335. package/src/_services/base/archive-data.service.spec.ts +196 -196
  336. package/src/_services/base/archive-data.service.ts +216 -216
  337. package/src/_services/base/data.service.spec.ts +674 -674
  338. package/src/_services/base/data.service.ts +2719 -2719
  339. package/src/_services/base/db.service.spec.ts +73 -73
  340. package/src/_services/base/db.service.ts +1575 -1575
  341. package/src/_services/base/singleton.service-base.spec.ts +28 -28
  342. package/src/_services/base/singleton.service-base.ts +24 -24
  343. package/src/_services/base/singleton.service.spec.ts +114 -114
  344. package/src/_services/base/singleton.service.ts +38 -38
  345. package/src/_services/core/api.service.spec.ts +140 -140
  346. package/src/_services/core/auth.service.spec.ts +159 -159
  347. package/src/_services/core/auth.service.ts +174 -174
  348. package/src/_services/core/email.service.spec.ts +85 -85
  349. package/src/_services/core/email.service.ts +742 -742
  350. package/src/_services/core/global.service.spec.ts +292 -292
  351. package/src/_services/core/global.service.ts +475 -475
  352. package/src/_services/core/memory-guard.service.spec.ts +18 -0
  353. package/src/_services/core/memory-guard.service.ts +14 -3
  354. package/src/_services/core/service-collection.service.spec.ts +46 -46
  355. package/src/_services/core/service-collection.service.ts +6 -6
  356. package/src/_services/route/controller.service.spec.ts +53 -53
  357. package/src/_services/route/controller.service.ts +148 -148
  358. package/src/_services/route/routing-module.service.spec.ts +98 -98
  359. package/src/_services/route/routing-module.service.ts +330 -330
  360. package/src/_services/server/app.server.ts +1747 -1747
  361. package/src/_services/shared.static-service.spec.ts +99 -99
  362. package/src/_services/shared.static-service.ts +78 -78
  363. package/src/index.ts +96 -96
  364. package/tsconfig.app.json +12 -12
  365. package/tsconfig.json +42 -42
@@ -1,654 +1,654 @@
1
- import { Request, Response } from 'express';
2
- import { cryptoJs } from 'crypto-js';
3
- import { DyFM_Error, DyFM_Log } from '@futdevpro/fsm-dynamo';
4
- import { DyNTS_SingletonService } from '../../../_services/base/singleton.service';
5
- import { DyNTS_global_settings } from '../../../_collections/global-settings.const';
6
- import { DyNTS_OAuth2_AuthService } from './oauth2.auth-service';
7
-
8
- /**
9
- * OAuth2 Control Service implementation
10
- *
11
- * This service handles OAuth2 specific business logic and token management
12
- *
13
- * @example
14
- * const oauth2Service = DyNTS_OAuth2_ControlService.getInstance();
15
- * await oauth2Service.handleAuthorizationRequest(req, res);
16
- */
17
- export class DyNTS_OAuth2_ControlService extends DyNTS_SingletonService {
18
- static getInstance(): DyNTS_OAuth2_ControlService {
19
- return DyNTS_OAuth2_ControlService.getSingletonInstance();
20
- }
21
-
22
- readonly serviceName: string = 'OAuth2ControlService';
23
-
24
- private readonly authService: DyNTS_OAuth2_AuthService = DyNTS_OAuth2_AuthService.getInstance();
25
- private readonly authorizationCodes: Map<string, { clientId: string; scope: string; expiresAt: number }> = new Map();
26
- private readonly accessTokens: Map<string, { clientId: string; scope: string; expiresAt: number }> = new Map();
27
- private readonly refreshTokens: Map<string, { clientId: string; scope: string; accessToken: string }> = new Map();
28
- private readonly clients: Map<string, {
29
- clientId: string;
30
- clientSecret: string;
31
- redirectUris: string[];
32
- allowedScopes: string[];
33
- isActive: boolean;
34
- }> = new Map();
35
- private readonly users: Map<string, {
36
- username: string;
37
- password: string; // In a real implementation, this would be a hashed password
38
- scopes: string[];
39
- }> = new Map();
40
-
41
- /**
42
- * Handles the OAuth2 authorization request
43
- * @param req Express Request object
44
- * @param res Express Response object
45
- */
46
- async handleAuthorizationRequest(req: Request, res: Response): Promise<void> {
47
- try {
48
- const { response_type, client_id, redirect_uri, scope, state } = req.query;
49
-
50
- // Validate required parameters
51
- if (!response_type || !client_id || !redirect_uri) {
52
- throw new DyFM_Error({
53
- status: 400,
54
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HA0`,
55
- addECToUserMsg: true,
56
- message: 'Missing required OAuth2 parameters',
57
- userMessage: 'Invalid authorization request',
58
- issuerService: this.serviceName,
59
- });
60
- }
61
-
62
- // Validate client_id against registered clients
63
- if (!this.isValidClient(client_id as string)) {
64
- throw new DyFM_Error({
65
- status: 400,
66
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HA2`,
67
- addECToUserMsg: true,
68
- message: 'Invalid client_id',
69
- userMessage: 'Invalid authorization request',
70
- issuerService: this.serviceName,
71
- });
72
- }
73
-
74
- // Validate redirect_uri against registered redirect URIs
75
- if (!this.isValidRedirectUri(client_id as string, redirect_uri as string)) {
76
- throw new DyFM_Error({
77
- status: 400,
78
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HA3`,
79
- addECToUserMsg: true,
80
- message: 'Invalid redirect_uri',
81
- userMessage: 'Invalid authorization request',
82
- issuerService: this.serviceName,
83
- });
84
- }
85
-
86
- // Validate scope against allowed scopes
87
- if (!this.isValidScope(client_id as string, scope as string)) {
88
- throw new DyFM_Error({
89
- status: 400,
90
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HA4`,
91
- addECToUserMsg: true,
92
- message: 'Invalid scope',
93
- userMessage: 'Invalid authorization request',
94
- issuerService: this.serviceName,
95
- });
96
- }
97
-
98
- // For authorization code flow
99
- if (response_type === 'code') {
100
- const authorizationCode = await this.generateAuthorizationCode(client_id as string, scope as string);
101
-
102
- // Redirect with authorization code
103
- const redirectUrl = new URL(redirect_uri as string);
104
- redirectUrl.searchParams.append('code', authorizationCode);
105
- if (state) redirectUrl.searchParams.append('state', state as string);
106
-
107
- res.redirect(redirectUrl.toString());
108
- return;
109
- }
110
-
111
- // For implicit flow
112
- if (response_type === 'token') {
113
- const accessToken = await this.generateAccessToken(client_id as string, scope as string);
114
-
115
- // Redirect with access token
116
- const redirectUrl = new URL(redirect_uri as string);
117
- redirectUrl.hash = `access_token=${accessToken}`;
118
- if (state) redirectUrl.hash += `&state=${state}`;
119
-
120
- res.redirect(redirectUrl.toString());
121
- return;
122
- }
123
-
124
- throw new DyFM_Error({
125
- status: 400,
126
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HA1`,
127
- addECToUserMsg: true,
128
- message: 'Unsupported response_type',
129
- userMessage: 'Invalid authorization request',
130
- issuerService: this.serviceName,
131
- });
132
- } catch (error) {
133
- DyFM_Log.error('Authorization request failed', error);
134
- throw error;
135
- }
136
- }
137
-
138
- /**
139
- * Validates if the client is registered and active
140
- * @param clientId The client ID to validate
141
- * @returns true if the client is valid
142
- */
143
- private isValidClient(clientId: string): boolean {
144
- const client = this.clients.get(clientId);
145
- return client?.isActive ?? false;
146
- }
147
-
148
- /**
149
- * Validates if the redirect URI is registered for the client
150
- * @param clientId The client ID
151
- * @param redirectUri The redirect URI to validate
152
- * @returns true if the redirect URI is valid
153
- */
154
- private isValidRedirectUri(clientId: string, redirectUri: string): boolean {
155
- const client = this.clients.get(clientId);
156
- if (!client) return false;
157
-
158
- // Check if the redirect URI matches any of the registered URIs
159
- return client.redirectUris.some(uri => {
160
- // Simple exact match for now
161
- // TODO: Implement more sophisticated URI matching (e.g., wildcards, regex)
162
- return uri === redirectUri;
163
- });
164
- }
165
-
166
- /**
167
- * Validates if the scope is allowed for the client
168
- * @param clientId The client ID
169
- * @param scope The scope to validate
170
- * @returns true if the scope is valid
171
- */
172
- private isValidScope(clientId: string, scope: string): boolean {
173
- const client = this.clients.get(clientId);
174
- if (!client) return false;
175
-
176
- // If no scope is requested, it's valid
177
- if (!scope) return true;
178
-
179
- // Split scope string into individual scopes
180
- const requestedScopes = scope.split(' ');
181
-
182
- // Check if all requested scopes are allowed
183
- return requestedScopes.every(s => client.allowedScopes.includes(s));
184
- }
185
-
186
- /**
187
- * Handles the OAuth2 token request
188
- * @param req Express Request object
189
- * @param res Express Response object
190
- */
191
- async handleTokenRequest(req: Request, res: Response): Promise<void> {
192
- try {
193
- const { grant_type, code, refresh_token, client_id, client_secret, username, password } = req.body;
194
-
195
- // Validate required parameters
196
- if (!grant_type || !client_id || !client_secret) {
197
- throw new DyFM_Error({
198
- status: 400,
199
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT0`,
200
- addECToUserMsg: true,
201
- message: 'Missing required OAuth2 parameters',
202
- userMessage: 'Invalid token request',
203
- issuerService: this.serviceName,
204
- });
205
- }
206
-
207
- // Validate client credentials
208
- if (!this.validateClientCredentials(client_id, client_secret)) {
209
- throw new DyFM_Error({
210
- status: 401,
211
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT4`,
212
- addECToUserMsg: true,
213
- message: 'Invalid client credentials',
214
- userMessage: 'Invalid token request',
215
- issuerService: this.serviceName,
216
- });
217
- }
218
-
219
- switch (grant_type) {
220
- case 'authorization_code':
221
- if (!code) {
222
- throw new DyFM_Error({
223
- status: 400,
224
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT1`,
225
- addECToUserMsg: true,
226
- message: 'Missing authorization code',
227
- userMessage: 'Invalid token request',
228
- issuerService: this.serviceName,
229
- });
230
- }
231
-
232
- // Validate authorization code
233
- const authCodeData = this.authorizationCodes.get(code);
234
- if (!authCodeData || authCodeData.expiresAt < Date.now()) {
235
- throw new DyFM_Error({
236
- status: 400,
237
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT5`,
238
- addECToUserMsg: true,
239
- message: 'Invalid or expired authorization code',
240
- userMessage: 'Invalid token request',
241
- issuerService: this.serviceName,
242
- });
243
- }
244
-
245
- // Remove used authorization code
246
- this.authorizationCodes.delete(code);
247
-
248
- const accessToken = await this.generateAccessToken(client_id, authCodeData.scope);
249
- const refreshToken = await this.generateRefreshToken(client_id);
250
-
251
- // Store refresh token with access token reference
252
- this.refreshTokens.set(refreshToken, {
253
- clientId: client_id,
254
- scope: authCodeData.scope,
255
- accessToken
256
- });
257
-
258
- res.json({
259
- access_token: accessToken,
260
- token_type: 'Bearer',
261
- expires_in: 3600,
262
- refresh_token: refreshToken,
263
- scope: authCodeData.scope
264
- });
265
- break;
266
-
267
- case 'refresh_token':
268
- if (!refresh_token) {
269
- throw new DyFM_Error({
270
- status: 400,
271
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT2`,
272
- addECToUserMsg: true,
273
- message: 'Missing refresh token',
274
- userMessage: 'Invalid token request',
275
- issuerService: this.serviceName,
276
- });
277
- }
278
-
279
- // Validate refresh token
280
- const refreshTokenData = this.refreshTokens.get(refresh_token);
281
- if (!refreshTokenData) {
282
- throw new DyFM_Error({
283
- status: 400,
284
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT6`,
285
- addECToUserMsg: true,
286
- message: 'Invalid refresh token',
287
- userMessage: 'Invalid token request',
288
- issuerService: this.serviceName,
289
- });
290
- }
291
-
292
- // Revoke old access token
293
- this.accessTokens.delete(refreshTokenData.accessToken);
294
-
295
- // Generate new access token
296
- const newAccessToken = await this.generateAccessToken(client_id, refreshTokenData.scope);
297
- const newRefreshToken = await this.generateRefreshToken(client_id);
298
-
299
- // Store new refresh token
300
- this.refreshTokens.set(newRefreshToken, {
301
- clientId: client_id,
302
- scope: refreshTokenData.scope,
303
- accessToken: newAccessToken
304
- });
305
-
306
- res.json({
307
- access_token: newAccessToken,
308
- token_type: 'Bearer',
309
- expires_in: 3600,
310
- refresh_token: newRefreshToken,
311
- scope: refreshTokenData.scope
312
- });
313
- break;
314
-
315
- case 'client_credentials':
316
- const clientAccessToken = await this.generateAccessToken(client_id, '');
317
- res.json({
318
- access_token: clientAccessToken,
319
- token_type: 'Bearer',
320
- expires_in: 3600
321
- });
322
- break;
323
-
324
- case 'password':
325
- if (!username || !password) {
326
- throw new DyFM_Error({
327
- status: 400,
328
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT7`,
329
- addECToUserMsg: true,
330
- message: 'Missing username or password',
331
- userMessage: 'Invalid token request',
332
- issuerService: this.serviceName,
333
- });
334
- }
335
-
336
- // Authenticate user
337
- const userScopes = this.authenticateUser(username, password);
338
- if (!userScopes) {
339
- throw new DyFM_Error({
340
- status: 401,
341
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT8`,
342
- addECToUserMsg: true,
343
- message: 'Invalid username or password',
344
- userMessage: 'Invalid token request',
345
- issuerService: this.serviceName,
346
- });
347
- }
348
-
349
- // Generate access token
350
- const userAccessToken = await this.generateAccessToken(client_id, userScopes.join(' '));
351
- const userRefreshToken = await this.generateRefreshToken(client_id);
352
-
353
- // Store refresh token with access token reference
354
- this.refreshTokens.set(userRefreshToken, {
355
- clientId: client_id,
356
- scope: userScopes.join(' '),
357
- accessToken: userAccessToken
358
- });
359
-
360
- res.json({
361
- access_token: userAccessToken,
362
- token_type: 'Bearer',
363
- expires_in: 3600,
364
- refresh_token: userRefreshToken,
365
- scope: userScopes.join(' ')
366
- });
367
- break;
368
-
369
- default:
370
- throw new DyFM_Error({
371
- status: 400,
372
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT3`,
373
- addECToUserMsg: true,
374
- message: 'Unsupported grant_type',
375
- userMessage: 'Invalid token request',
376
- issuerService: this.serviceName,
377
- });
378
- }
379
- } catch (error) {
380
- DyFM_Log.error('Token request failed', error);
381
- throw error;
382
- }
383
- }
384
-
385
- /**
386
- * Validates client credentials
387
- * @param clientId The client ID
388
- * @param clientSecret The client secret
389
- * @returns true if the credentials are valid
390
- */
391
- private validateClientCredentials(clientId: string, clientSecret: string): boolean {
392
- const client = this.clients.get(clientId);
393
- return (client?.clientSecret === clientSecret) && (client?.isActive ?? false);
394
- }
395
-
396
- /**
397
- * Handles the OAuth2 userinfo request
398
- * @param req Express Request object
399
- * @param res Express Response object
400
- */
401
- async handleUserInfoRequest(req: Request, res: Response): Promise<void> {
402
- try {
403
- const token = this.authService.getTokenFromRequest(req);
404
-
405
- // Validate token
406
- const tokenData = this.accessTokens.get(token);
407
- if (!tokenData || tokenData.expiresAt < Date.now()) {
408
- throw new DyFM_Error({
409
- status: 401,
410
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HU0`,
411
- addECToUserMsg: true,
412
- message: 'Invalid or expired access token',
413
- userMessage: 'Invalid token',
414
- issuerService: this.serviceName,
415
- });
416
- }
417
-
418
- // Extract user information based on token scope
419
- const userInfo = await this.getUserInfoFromToken(token);
420
-
421
- res.json(userInfo);
422
- } catch (error) {
423
- DyFM_Log.error('Userinfo request failed', error);
424
- throw error;
425
- }
426
- }
427
-
428
- /**
429
- * Gets user information from the token
430
- * @param token The access token
431
- * @returns The user information object
432
- */
433
- private async getUserInfoFromToken(token: string): Promise<any> {
434
- const tokenData = this.accessTokens.get(token);
435
- if (!tokenData) {
436
- throw new DyFM_Error({
437
- status: 401,
438
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HU1`,
439
- addECToUserMsg: true,
440
- message: 'Invalid access token',
441
- userMessage: 'Invalid token',
442
- issuerService: this.serviceName,
443
- });
444
- }
445
-
446
- // TODO: Implement user information retrieval from database/storage
447
- // For now, return mock user information
448
- return {
449
- sub: 'user123',
450
- name: 'John Doe',
451
- email: 'john.doe@example.com',
452
- // Add other user information based on scope
453
- ...(tokenData.scope.includes('profile') && {
454
- given_name: 'John',
455
- family_name: 'Doe',
456
- picture: 'https://example.com/john.jpg'
457
- }),
458
- ...(tokenData.scope.includes('email') && {
459
- email_verified: true
460
- })
461
- };
462
- }
463
-
464
- /**
465
- * Handles the OAuth2 token revocation request
466
- * @param req Express Request object
467
- * @param res Express Response object
468
- */
469
- async handleTokenRevocation(req: Request, res: Response): Promise<void> {
470
- try {
471
- const { token, token_type_hint } = req.body;
472
-
473
- if (!token) {
474
- throw new DyFM_Error({
475
- status: 400,
476
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HR0`,
477
- addECToUserMsg: true,
478
- message: 'Missing token',
479
- userMessage: 'Invalid revocation request',
480
- issuerService: this.serviceName,
481
- });
482
- }
483
-
484
- // Try to revoke the token based on token_type_hint
485
- let revoked = false;
486
-
487
- if (!token_type_hint || token_type_hint === 'access_token') {
488
- // Try to revoke as access token
489
- if (this.accessTokens.delete(token)) {
490
- revoked = true;
491
- }
492
- }
493
-
494
- if (!revoked && (!token_type_hint || token_type_hint === 'refresh_token')) {
495
- // Try to revoke as refresh token
496
- const refreshTokenData = this.refreshTokens.get(token);
497
- if (refreshTokenData) {
498
- // Also revoke the associated access token
499
- this.accessTokens.delete(refreshTokenData.accessToken);
500
- this.refreshTokens.delete(token);
501
- revoked = true;
502
- }
503
- }
504
-
505
- if (!revoked) {
506
- // Token not found or already revoked
507
- throw new DyFM_Error({
508
- status: 400,
509
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HR1`,
510
- addECToUserMsg: true,
511
- message: 'Token not found or already revoked',
512
- userMessage: 'Invalid revocation request',
513
- issuerService: this.serviceName,
514
- });
515
- }
516
-
517
- res.status(200).send();
518
- } catch (error) {
519
- DyFM_Log.error('Token revocation failed', error);
520
- throw error;
521
- }
522
- }
523
-
524
- /**
525
- * Generates an authorization code
526
- * @param clientId The client ID
527
- * @param scope The requested scope
528
- * @returns The generated authorization code
529
- */
530
- private async generateAuthorizationCode(clientId: string, scope: string): Promise<string> {
531
- //const code = randomBytes(32).toString('hex');
532
- const code = cryptoJs.lib.WordArray.random(32).toString();
533
- const expiresAt = Date.now() + 600000; // 10 minutes expiration
534
-
535
- this.authorizationCodes.set(code, {
536
- clientId,
537
- scope,
538
- expiresAt
539
- });
540
-
541
- return code;
542
- }
543
-
544
- /**
545
- * Generates an access token
546
- * @param clientId The client ID
547
- * @param scope The requested scope
548
- * @returns The generated access token
549
- */
550
- private async generateAccessToken(clientId: string, scope: string): Promise<string> {
551
- //const token = randomBytes(32).toString('hex');
552
- const token = cryptoJs.lib.WordArray.random(32).toString();
553
- const expiresAt = Date.now() + 3600000; // 1 hour expiration
554
-
555
- this.accessTokens.set(token, {
556
- clientId,
557
- scope,
558
- expiresAt
559
- });
560
-
561
- return token;
562
- }
563
-
564
- /**
565
- * Generates a refresh token
566
- * @param clientId The client ID
567
- * @returns The generated refresh token
568
- */
569
- private async generateRefreshToken(clientId: string): Promise<string> {
570
- //const token = randomBytes(32).toString('hex');
571
- const token = cryptoJs.lib.WordArray.random(32).toString();
572
-
573
- this.refreshTokens.set(token, {
574
- clientId,
575
- scope: '',
576
- accessToken: ''
577
- });
578
-
579
- return token;
580
- }
581
-
582
- /**
583
- * Gets the access token data
584
- * @param token The access token
585
- * @returns The access token data or undefined if not found
586
- */
587
- getAccessTokenData(token: string): { clientId: string; scope: string; expiresAt: number } | undefined {
588
- return this.accessTokens.get(token);
589
- }
590
-
591
- /**
592
- * Registers a new OAuth2 client
593
- * @param clientId The client ID
594
- * @param clientSecret The client secret
595
- * @param redirectUris The allowed redirect URIs
596
- * @param allowedScopes The allowed scopes
597
- * @returns true if the client was registered successfully
598
- */
599
- registerClient(
600
- clientId: string,
601
- clientSecret: string,
602
- redirectUris: string[],
603
- allowedScopes: string[]
604
- ): boolean {
605
- if (this.clients.has(clientId)) {
606
- return false;
607
- }
608
-
609
- this.clients.set(clientId, {
610
- clientId,
611
- clientSecret,
612
- redirectUris,
613
- allowedScopes,
614
- isActive: true
615
- });
616
-
617
- return true;
618
- }
619
-
620
- /**
621
- * Authenticates a user with username and password
622
- * @param username The username
623
- * @param password The password
624
- * @returns The user's scopes if authentication is successful, undefined otherwise
625
- */
626
- private authenticateUser(username: string, password: string): string[] | undefined {
627
- const user = this.users.get(username);
628
- if (!user || user.password !== password) { // In a real implementation, compare hashed passwords
629
- return undefined;
630
- }
631
- return user.scopes;
632
- }
633
-
634
- /**
635
- * Registers a new user
636
- * @param username The username
637
- * @param password The password
638
- * @param scopes The user's scopes
639
- * @returns true if the user was registered successfully
640
- */
641
- registerUser(username: string, password: string, scopes: string[]): boolean {
642
- if (this.users.has(username)) {
643
- return false;
644
- }
645
-
646
- this.users.set(username, {
647
- username,
648
- password, // In a real implementation, hash the password
649
- scopes
650
- });
651
-
652
- return true;
653
- }
1
+ import { Request, Response } from 'express';
2
+ import { cryptoJs } from 'crypto-js';
3
+ import { DyFM_Error, DyFM_Log } from '@futdevpro/fsm-dynamo';
4
+ import { DyNTS_SingletonService } from '../../../_services/base/singleton.service';
5
+ import { DyNTS_global_settings } from '../../../_collections/global-settings.const';
6
+ import { DyNTS_OAuth2_AuthService } from './oauth2.auth-service';
7
+
8
+ /**
9
+ * OAuth2 Control Service implementation
10
+ *
11
+ * This service handles OAuth2 specific business logic and token management
12
+ *
13
+ * @example
14
+ * const oauth2Service = DyNTS_OAuth2_ControlService.getInstance();
15
+ * await oauth2Service.handleAuthorizationRequest(req, res);
16
+ */
17
+ export class DyNTS_OAuth2_ControlService extends DyNTS_SingletonService {
18
+ static getInstance(): DyNTS_OAuth2_ControlService {
19
+ return DyNTS_OAuth2_ControlService.getSingletonInstance();
20
+ }
21
+
22
+ readonly serviceName: string = 'OAuth2ControlService';
23
+
24
+ private readonly authService: DyNTS_OAuth2_AuthService = DyNTS_OAuth2_AuthService.getInstance();
25
+ private readonly authorizationCodes: Map<string, { clientId: string; scope: string; expiresAt: number }> = new Map();
26
+ private readonly accessTokens: Map<string, { clientId: string; scope: string; expiresAt: number }> = new Map();
27
+ private readonly refreshTokens: Map<string, { clientId: string; scope: string; accessToken: string }> = new Map();
28
+ private readonly clients: Map<string, {
29
+ clientId: string;
30
+ clientSecret: string;
31
+ redirectUris: string[];
32
+ allowedScopes: string[];
33
+ isActive: boolean;
34
+ }> = new Map();
35
+ private readonly users: Map<string, {
36
+ username: string;
37
+ password: string; // In a real implementation, this would be a hashed password
38
+ scopes: string[];
39
+ }> = new Map();
40
+
41
+ /**
42
+ * Handles the OAuth2 authorization request
43
+ * @param req Express Request object
44
+ * @param res Express Response object
45
+ */
46
+ async handleAuthorizationRequest(req: Request, res: Response): Promise<void> {
47
+ try {
48
+ const { response_type, client_id, redirect_uri, scope, state } = req.query;
49
+
50
+ // Validate required parameters
51
+ if (!response_type || !client_id || !redirect_uri) {
52
+ throw new DyFM_Error({
53
+ status: 400,
54
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HA0`,
55
+ addECToUserMsg: true,
56
+ message: 'Missing required OAuth2 parameters',
57
+ userMessage: 'Invalid authorization request',
58
+ issuerService: this.serviceName,
59
+ });
60
+ }
61
+
62
+ // Validate client_id against registered clients
63
+ if (!this.isValidClient(client_id as string)) {
64
+ throw new DyFM_Error({
65
+ status: 400,
66
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HA2`,
67
+ addECToUserMsg: true,
68
+ message: 'Invalid client_id',
69
+ userMessage: 'Invalid authorization request',
70
+ issuerService: this.serviceName,
71
+ });
72
+ }
73
+
74
+ // Validate redirect_uri against registered redirect URIs
75
+ if (!this.isValidRedirectUri(client_id as string, redirect_uri as string)) {
76
+ throw new DyFM_Error({
77
+ status: 400,
78
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HA3`,
79
+ addECToUserMsg: true,
80
+ message: 'Invalid redirect_uri',
81
+ userMessage: 'Invalid authorization request',
82
+ issuerService: this.serviceName,
83
+ });
84
+ }
85
+
86
+ // Validate scope against allowed scopes
87
+ if (!this.isValidScope(client_id as string, scope as string)) {
88
+ throw new DyFM_Error({
89
+ status: 400,
90
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HA4`,
91
+ addECToUserMsg: true,
92
+ message: 'Invalid scope',
93
+ userMessage: 'Invalid authorization request',
94
+ issuerService: this.serviceName,
95
+ });
96
+ }
97
+
98
+ // For authorization code flow
99
+ if (response_type === 'code') {
100
+ const authorizationCode = await this.generateAuthorizationCode(client_id as string, scope as string);
101
+
102
+ // Redirect with authorization code
103
+ const redirectUrl = new URL(redirect_uri as string);
104
+ redirectUrl.searchParams.append('code', authorizationCode);
105
+ if (state) redirectUrl.searchParams.append('state', state as string);
106
+
107
+ res.redirect(redirectUrl.toString());
108
+ return;
109
+ }
110
+
111
+ // For implicit flow
112
+ if (response_type === 'token') {
113
+ const accessToken = await this.generateAccessToken(client_id as string, scope as string);
114
+
115
+ // Redirect with access token
116
+ const redirectUrl = new URL(redirect_uri as string);
117
+ redirectUrl.hash = `access_token=${accessToken}`;
118
+ if (state) redirectUrl.hash += `&state=${state}`;
119
+
120
+ res.redirect(redirectUrl.toString());
121
+ return;
122
+ }
123
+
124
+ throw new DyFM_Error({
125
+ status: 400,
126
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HA1`,
127
+ addECToUserMsg: true,
128
+ message: 'Unsupported response_type',
129
+ userMessage: 'Invalid authorization request',
130
+ issuerService: this.serviceName,
131
+ });
132
+ } catch (error) {
133
+ DyFM_Log.error('Authorization request failed', error);
134
+ throw error;
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Validates if the client is registered and active
140
+ * @param clientId The client ID to validate
141
+ * @returns true if the client is valid
142
+ */
143
+ private isValidClient(clientId: string): boolean {
144
+ const client = this.clients.get(clientId);
145
+ return client?.isActive ?? false;
146
+ }
147
+
148
+ /**
149
+ * Validates if the redirect URI is registered for the client
150
+ * @param clientId The client ID
151
+ * @param redirectUri The redirect URI to validate
152
+ * @returns true if the redirect URI is valid
153
+ */
154
+ private isValidRedirectUri(clientId: string, redirectUri: string): boolean {
155
+ const client = this.clients.get(clientId);
156
+ if (!client) return false;
157
+
158
+ // Check if the redirect URI matches any of the registered URIs
159
+ return client.redirectUris.some(uri => {
160
+ // Simple exact match for now
161
+ // TODO: Implement more sophisticated URI matching (e.g., wildcards, regex)
162
+ return uri === redirectUri;
163
+ });
164
+ }
165
+
166
+ /**
167
+ * Validates if the scope is allowed for the client
168
+ * @param clientId The client ID
169
+ * @param scope The scope to validate
170
+ * @returns true if the scope is valid
171
+ */
172
+ private isValidScope(clientId: string, scope: string): boolean {
173
+ const client = this.clients.get(clientId);
174
+ if (!client) return false;
175
+
176
+ // If no scope is requested, it's valid
177
+ if (!scope) return true;
178
+
179
+ // Split scope string into individual scopes
180
+ const requestedScopes = scope.split(' ');
181
+
182
+ // Check if all requested scopes are allowed
183
+ return requestedScopes.every(s => client.allowedScopes.includes(s));
184
+ }
185
+
186
+ /**
187
+ * Handles the OAuth2 token request
188
+ * @param req Express Request object
189
+ * @param res Express Response object
190
+ */
191
+ async handleTokenRequest(req: Request, res: Response): Promise<void> {
192
+ try {
193
+ const { grant_type, code, refresh_token, client_id, client_secret, username, password } = req.body;
194
+
195
+ // Validate required parameters
196
+ if (!grant_type || !client_id || !client_secret) {
197
+ throw new DyFM_Error({
198
+ status: 400,
199
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT0`,
200
+ addECToUserMsg: true,
201
+ message: 'Missing required OAuth2 parameters',
202
+ userMessage: 'Invalid token request',
203
+ issuerService: this.serviceName,
204
+ });
205
+ }
206
+
207
+ // Validate client credentials
208
+ if (!this.validateClientCredentials(client_id, client_secret)) {
209
+ throw new DyFM_Error({
210
+ status: 401,
211
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT4`,
212
+ addECToUserMsg: true,
213
+ message: 'Invalid client credentials',
214
+ userMessage: 'Invalid token request',
215
+ issuerService: this.serviceName,
216
+ });
217
+ }
218
+
219
+ switch (grant_type) {
220
+ case 'authorization_code':
221
+ if (!code) {
222
+ throw new DyFM_Error({
223
+ status: 400,
224
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT1`,
225
+ addECToUserMsg: true,
226
+ message: 'Missing authorization code',
227
+ userMessage: 'Invalid token request',
228
+ issuerService: this.serviceName,
229
+ });
230
+ }
231
+
232
+ // Validate authorization code
233
+ const authCodeData = this.authorizationCodes.get(code);
234
+ if (!authCodeData || authCodeData.expiresAt < Date.now()) {
235
+ throw new DyFM_Error({
236
+ status: 400,
237
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT5`,
238
+ addECToUserMsg: true,
239
+ message: 'Invalid or expired authorization code',
240
+ userMessage: 'Invalid token request',
241
+ issuerService: this.serviceName,
242
+ });
243
+ }
244
+
245
+ // Remove used authorization code
246
+ this.authorizationCodes.delete(code);
247
+
248
+ const accessToken = await this.generateAccessToken(client_id, authCodeData.scope);
249
+ const refreshToken = await this.generateRefreshToken(client_id);
250
+
251
+ // Store refresh token with access token reference
252
+ this.refreshTokens.set(refreshToken, {
253
+ clientId: client_id,
254
+ scope: authCodeData.scope,
255
+ accessToken
256
+ });
257
+
258
+ res.json({
259
+ access_token: accessToken,
260
+ token_type: 'Bearer',
261
+ expires_in: 3600,
262
+ refresh_token: refreshToken,
263
+ scope: authCodeData.scope
264
+ });
265
+ break;
266
+
267
+ case 'refresh_token':
268
+ if (!refresh_token) {
269
+ throw new DyFM_Error({
270
+ status: 400,
271
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT2`,
272
+ addECToUserMsg: true,
273
+ message: 'Missing refresh token',
274
+ userMessage: 'Invalid token request',
275
+ issuerService: this.serviceName,
276
+ });
277
+ }
278
+
279
+ // Validate refresh token
280
+ const refreshTokenData = this.refreshTokens.get(refresh_token);
281
+ if (!refreshTokenData) {
282
+ throw new DyFM_Error({
283
+ status: 400,
284
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT6`,
285
+ addECToUserMsg: true,
286
+ message: 'Invalid refresh token',
287
+ userMessage: 'Invalid token request',
288
+ issuerService: this.serviceName,
289
+ });
290
+ }
291
+
292
+ // Revoke old access token
293
+ this.accessTokens.delete(refreshTokenData.accessToken);
294
+
295
+ // Generate new access token
296
+ const newAccessToken = await this.generateAccessToken(client_id, refreshTokenData.scope);
297
+ const newRefreshToken = await this.generateRefreshToken(client_id);
298
+
299
+ // Store new refresh token
300
+ this.refreshTokens.set(newRefreshToken, {
301
+ clientId: client_id,
302
+ scope: refreshTokenData.scope,
303
+ accessToken: newAccessToken
304
+ });
305
+
306
+ res.json({
307
+ access_token: newAccessToken,
308
+ token_type: 'Bearer',
309
+ expires_in: 3600,
310
+ refresh_token: newRefreshToken,
311
+ scope: refreshTokenData.scope
312
+ });
313
+ break;
314
+
315
+ case 'client_credentials':
316
+ const clientAccessToken = await this.generateAccessToken(client_id, '');
317
+ res.json({
318
+ access_token: clientAccessToken,
319
+ token_type: 'Bearer',
320
+ expires_in: 3600
321
+ });
322
+ break;
323
+
324
+ case 'password':
325
+ if (!username || !password) {
326
+ throw new DyFM_Error({
327
+ status: 400,
328
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT7`,
329
+ addECToUserMsg: true,
330
+ message: 'Missing username or password',
331
+ userMessage: 'Invalid token request',
332
+ issuerService: this.serviceName,
333
+ });
334
+ }
335
+
336
+ // Authenticate user
337
+ const userScopes = this.authenticateUser(username, password);
338
+ if (!userScopes) {
339
+ throw new DyFM_Error({
340
+ status: 401,
341
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT8`,
342
+ addECToUserMsg: true,
343
+ message: 'Invalid username or password',
344
+ userMessage: 'Invalid token request',
345
+ issuerService: this.serviceName,
346
+ });
347
+ }
348
+
349
+ // Generate access token
350
+ const userAccessToken = await this.generateAccessToken(client_id, userScopes.join(' '));
351
+ const userRefreshToken = await this.generateRefreshToken(client_id);
352
+
353
+ // Store refresh token with access token reference
354
+ this.refreshTokens.set(userRefreshToken, {
355
+ clientId: client_id,
356
+ scope: userScopes.join(' '),
357
+ accessToken: userAccessToken
358
+ });
359
+
360
+ res.json({
361
+ access_token: userAccessToken,
362
+ token_type: 'Bearer',
363
+ expires_in: 3600,
364
+ refresh_token: userRefreshToken,
365
+ scope: userScopes.join(' ')
366
+ });
367
+ break;
368
+
369
+ default:
370
+ throw new DyFM_Error({
371
+ status: 400,
372
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HT3`,
373
+ addECToUserMsg: true,
374
+ message: 'Unsupported grant_type',
375
+ userMessage: 'Invalid token request',
376
+ issuerService: this.serviceName,
377
+ });
378
+ }
379
+ } catch (error) {
380
+ DyFM_Log.error('Token request failed', error);
381
+ throw error;
382
+ }
383
+ }
384
+
385
+ /**
386
+ * Validates client credentials
387
+ * @param clientId The client ID
388
+ * @param clientSecret The client secret
389
+ * @returns true if the credentials are valid
390
+ */
391
+ private validateClientCredentials(clientId: string, clientSecret: string): boolean {
392
+ const client = this.clients.get(clientId);
393
+ return (client?.clientSecret === clientSecret) && (client?.isActive ?? false);
394
+ }
395
+
396
+ /**
397
+ * Handles the OAuth2 userinfo request
398
+ * @param req Express Request object
399
+ * @param res Express Response object
400
+ */
401
+ async handleUserInfoRequest(req: Request, res: Response): Promise<void> {
402
+ try {
403
+ const token = this.authService.getTokenFromRequest(req);
404
+
405
+ // Validate token
406
+ const tokenData = this.accessTokens.get(token);
407
+ if (!tokenData || tokenData.expiresAt < Date.now()) {
408
+ throw new DyFM_Error({
409
+ status: 401,
410
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HU0`,
411
+ addECToUserMsg: true,
412
+ message: 'Invalid or expired access token',
413
+ userMessage: 'Invalid token',
414
+ issuerService: this.serviceName,
415
+ });
416
+ }
417
+
418
+ // Extract user information based on token scope
419
+ const userInfo = await this.getUserInfoFromToken(token);
420
+
421
+ res.json(userInfo);
422
+ } catch (error) {
423
+ DyFM_Log.error('Userinfo request failed', error);
424
+ throw error;
425
+ }
426
+ }
427
+
428
+ /**
429
+ * Gets user information from the token
430
+ * @param token The access token
431
+ * @returns The user information object
432
+ */
433
+ private async getUserInfoFromToken(token: string): Promise<any> {
434
+ const tokenData = this.accessTokens.get(token);
435
+ if (!tokenData) {
436
+ throw new DyFM_Error({
437
+ status: 401,
438
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HU1`,
439
+ addECToUserMsg: true,
440
+ message: 'Invalid access token',
441
+ userMessage: 'Invalid token',
442
+ issuerService: this.serviceName,
443
+ });
444
+ }
445
+
446
+ // TODO: Implement user information retrieval from database/storage
447
+ // For now, return mock user information
448
+ return {
449
+ sub: 'user123',
450
+ name: 'John Doe',
451
+ email: 'john.doe@example.com',
452
+ // Add other user information based on scope
453
+ ...(tokenData.scope.includes('profile') && {
454
+ given_name: 'John',
455
+ family_name: 'Doe',
456
+ picture: 'https://example.com/john.jpg'
457
+ }),
458
+ ...(tokenData.scope.includes('email') && {
459
+ email_verified: true
460
+ })
461
+ };
462
+ }
463
+
464
+ /**
465
+ * Handles the OAuth2 token revocation request
466
+ * @param req Express Request object
467
+ * @param res Express Response object
468
+ */
469
+ async handleTokenRevocation(req: Request, res: Response): Promise<void> {
470
+ try {
471
+ const { token, token_type_hint } = req.body;
472
+
473
+ if (!token) {
474
+ throw new DyFM_Error({
475
+ status: 400,
476
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HR0`,
477
+ addECToUserMsg: true,
478
+ message: 'Missing token',
479
+ userMessage: 'Invalid revocation request',
480
+ issuerService: this.serviceName,
481
+ });
482
+ }
483
+
484
+ // Try to revoke the token based on token_type_hint
485
+ let revoked = false;
486
+
487
+ if (!token_type_hint || token_type_hint === 'access_token') {
488
+ // Try to revoke as access token
489
+ if (this.accessTokens.delete(token)) {
490
+ revoked = true;
491
+ }
492
+ }
493
+
494
+ if (!revoked && (!token_type_hint || token_type_hint === 'refresh_token')) {
495
+ // Try to revoke as refresh token
496
+ const refreshTokenData = this.refreshTokens.get(token);
497
+ if (refreshTokenData) {
498
+ // Also revoke the associated access token
499
+ this.accessTokens.delete(refreshTokenData.accessToken);
500
+ this.refreshTokens.delete(token);
501
+ revoked = true;
502
+ }
503
+ }
504
+
505
+ if (!revoked) {
506
+ // Token not found or already revoked
507
+ throw new DyFM_Error({
508
+ status: 400,
509
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OA2-HR1`,
510
+ addECToUserMsg: true,
511
+ message: 'Token not found or already revoked',
512
+ userMessage: 'Invalid revocation request',
513
+ issuerService: this.serviceName,
514
+ });
515
+ }
516
+
517
+ res.status(200).send();
518
+ } catch (error) {
519
+ DyFM_Log.error('Token revocation failed', error);
520
+ throw error;
521
+ }
522
+ }
523
+
524
+ /**
525
+ * Generates an authorization code
526
+ * @param clientId The client ID
527
+ * @param scope The requested scope
528
+ * @returns The generated authorization code
529
+ */
530
+ private async generateAuthorizationCode(clientId: string, scope: string): Promise<string> {
531
+ //const code = randomBytes(32).toString('hex');
532
+ const code = cryptoJs.lib.WordArray.random(32).toString();
533
+ const expiresAt = Date.now() + 600000; // 10 minutes expiration
534
+
535
+ this.authorizationCodes.set(code, {
536
+ clientId,
537
+ scope,
538
+ expiresAt
539
+ });
540
+
541
+ return code;
542
+ }
543
+
544
+ /**
545
+ * Generates an access token
546
+ * @param clientId The client ID
547
+ * @param scope The requested scope
548
+ * @returns The generated access token
549
+ */
550
+ private async generateAccessToken(clientId: string, scope: string): Promise<string> {
551
+ //const token = randomBytes(32).toString('hex');
552
+ const token = cryptoJs.lib.WordArray.random(32).toString();
553
+ const expiresAt = Date.now() + 3600000; // 1 hour expiration
554
+
555
+ this.accessTokens.set(token, {
556
+ clientId,
557
+ scope,
558
+ expiresAt
559
+ });
560
+
561
+ return token;
562
+ }
563
+
564
+ /**
565
+ * Generates a refresh token
566
+ * @param clientId The client ID
567
+ * @returns The generated refresh token
568
+ */
569
+ private async generateRefreshToken(clientId: string): Promise<string> {
570
+ //const token = randomBytes(32).toString('hex');
571
+ const token = cryptoJs.lib.WordArray.random(32).toString();
572
+
573
+ this.refreshTokens.set(token, {
574
+ clientId,
575
+ scope: '',
576
+ accessToken: ''
577
+ });
578
+
579
+ return token;
580
+ }
581
+
582
+ /**
583
+ * Gets the access token data
584
+ * @param token The access token
585
+ * @returns The access token data or undefined if not found
586
+ */
587
+ getAccessTokenData(token: string): { clientId: string; scope: string; expiresAt: number } | undefined {
588
+ return this.accessTokens.get(token);
589
+ }
590
+
591
+ /**
592
+ * Registers a new OAuth2 client
593
+ * @param clientId The client ID
594
+ * @param clientSecret The client secret
595
+ * @param redirectUris The allowed redirect URIs
596
+ * @param allowedScopes The allowed scopes
597
+ * @returns true if the client was registered successfully
598
+ */
599
+ registerClient(
600
+ clientId: string,
601
+ clientSecret: string,
602
+ redirectUris: string[],
603
+ allowedScopes: string[]
604
+ ): boolean {
605
+ if (this.clients.has(clientId)) {
606
+ return false;
607
+ }
608
+
609
+ this.clients.set(clientId, {
610
+ clientId,
611
+ clientSecret,
612
+ redirectUris,
613
+ allowedScopes,
614
+ isActive: true
615
+ });
616
+
617
+ return true;
618
+ }
619
+
620
+ /**
621
+ * Authenticates a user with username and password
622
+ * @param username The username
623
+ * @param password The password
624
+ * @returns The user's scopes if authentication is successful, undefined otherwise
625
+ */
626
+ private authenticateUser(username: string, password: string): string[] | undefined {
627
+ const user = this.users.get(username);
628
+ if (!user || user.password !== password) { // In a real implementation, compare hashed passwords
629
+ return undefined;
630
+ }
631
+ return user.scopes;
632
+ }
633
+
634
+ /**
635
+ * Registers a new user
636
+ * @param username The username
637
+ * @param password The password
638
+ * @param scopes The user's scopes
639
+ * @returns true if the user was registered successfully
640
+ */
641
+ registerUser(username: string, password: string, scopes: string[]): boolean {
642
+ if (this.users.has(username)) {
643
+ return false;
644
+ }
645
+
646
+ this.users.set(username, {
647
+ username,
648
+ password, // In a real implementation, hash the password
649
+ scopes
650
+ });
651
+
652
+ return true;
653
+ }
654
654
  }