@digitaldefiance/node-express-suite 1.0.21 → 1.0.23

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 (633) hide show
  1. package/README.md +9 -0
  2. package/package.json +27 -32
  3. package/src/application-base.ts +492 -0
  4. package/src/application.ts +254 -0
  5. package/src/backup-code.ts +336 -0
  6. package/src/constants.ts +69 -0
  7. package/src/controllers/base.ts +440 -0
  8. package/{dist/controllers/index.d.ts → src/controllers/index.ts} +0 -1
  9. package/src/controllers/user.ts +1451 -0
  10. package/src/decorators/base-controller.ts +61 -0
  11. package/src/decorators/controller.ts +109 -0
  12. package/{dist/decorators/index.d.ts → src/decorators/index.ts} +0 -1
  13. package/src/decorators/zod-validation.ts +57 -0
  14. package/src/defaults.ts +94 -0
  15. package/src/documents/base.ts +7 -0
  16. package/src/documents/email-token.ts +14 -0
  17. package/{dist/documents/index.d.ts → src/documents/index.ts} +0 -1
  18. package/{dist/documents/mnemonic.d.ts → src/documents/mnemonic.ts} +5 -2
  19. package/{dist/documents/role.d.ts → src/documents/role.ts} +5 -2
  20. package/src/documents/used-direct-login-token.ts +7 -0
  21. package/{dist/documents/user-role.d.ts → src/documents/user-role.ts} +5 -2
  22. package/{dist/documents/user.d.ts → src/documents/user.ts} +4 -2
  23. package/src/enumerations/base-model-name.ts +41 -0
  24. package/{dist/enumerations/index.d.ts → src/enumerations/index.ts} +0 -1
  25. package/src/enumerations/length-encoding-type.ts +6 -0
  26. package/src/enumerations/schema-collection.ts +33 -0
  27. package/src/enumerations/symmetric-error-type.ts +4 -0
  28. package/src/environment.ts +770 -0
  29. package/src/errors/express-validation.ts +21 -0
  30. package/{dist/errors/index.d.ts → src/errors/index.ts} +0 -1
  31. package/src/errors/invalid-backup-code-version.ts +14 -0
  32. package/src/errors/invalid-jwt-token.ts +10 -0
  33. package/src/errors/invalid-model.ts +11 -0
  34. package/src/errors/invalid-new-password.ts +18 -0
  35. package/src/errors/invalid-password.ts +13 -0
  36. package/src/errors/missing-validated-data.ts +36 -0
  37. package/src/errors/mnemonic-or-password-required.ts +12 -0
  38. package/src/errors/model-not-registered.ts +11 -0
  39. package/src/errors/mongoose-validation.ts +34 -0
  40. package/src/errors/symmetric.ts +41 -0
  41. package/src/errors/token-expired.ts +10 -0
  42. package/src/get-language.ts +53 -0
  43. package/src/get-timezone.ts +45 -0
  44. package/{dist/index.d.ts → src/index.ts} +3 -2
  45. package/{dist/interfaces/api-error-response.d.ts → src/interfaces/api-error-response.ts} +2 -2
  46. package/src/interfaces/api-express-validation-error-response.ts +8 -0
  47. package/src/interfaces/api-message-response.ts +3 -0
  48. package/{dist/interfaces/api-mongo-validation-error-response.d.ts → src/interfaces/api-mongo-validation-error-response.ts} +2 -2
  49. package/{dist/interfaces/api-responses/backup-codes-response.d.ts → src/interfaces/api-responses/backup-codes-response.ts} +2 -2
  50. package/{dist/interfaces/api-responses/challenge-response.d.ts → src/interfaces/api-responses/challenge-response.ts} +3 -3
  51. package/{dist/interfaces/api-responses/code-count-response.d.ts → src/interfaces/api-responses/code-count-response.ts} +2 -2
  52. package/{dist/interfaces/api-responses/index.d.ts → src/interfaces/api-responses/index.ts} +0 -1
  53. package/{dist/interfaces/api-responses/login-response.d.ts → src/interfaces/api-responses/login-response.ts} +4 -4
  54. package/{dist/interfaces/api-responses/mnemonic-response.d.ts → src/interfaces/api-responses/mnemonic-response.ts} +2 -2
  55. package/{dist/interfaces/api-responses/registration-response.d.ts → src/interfaces/api-responses/registration-response.ts} +3 -3
  56. package/{dist/interfaces/api-responses/request-user-response.d.ts → src/interfaces/api-responses/request-user-response.ts} +2 -2
  57. package/{dist/interfaces/application.d.ts → src/interfaces/application.ts} +7 -7
  58. package/src/interfaces/backend-objects/email-token.ts +11 -0
  59. package/{dist/interfaces/backend-objects/index.d.ts → src/interfaces/backend-objects/index.ts} +0 -1
  60. package/{dist/interfaces/backend-objects/request-user.d.ts → src/interfaces/backend-objects/request-user.ts} +7 -2
  61. package/{dist/interfaces/backend-objects/role.d.ts → src/interfaces/backend-objects/role.ts} +1 -1
  62. package/src/interfaces/backend-objects/user.ts +9 -0
  63. package/src/interfaces/checksum-config.ts +4 -0
  64. package/src/interfaces/checksum-consts.ts +13 -0
  65. package/{dist/interfaces/constants.d.ts → src/interfaces/constants.ts} +5 -5
  66. package/src/interfaces/create-user-basics.ts +17 -0
  67. package/src/interfaces/csp-config.ts +35 -0
  68. package/src/interfaces/deep-partial.ts +3 -0
  69. package/{dist/interfaces/discriminator-collections.d.ts → src/interfaces/discriminator-collections.ts} +3 -3
  70. package/src/interfaces/email-service.ts +8 -0
  71. package/src/interfaces/environment-mongo.ts +76 -0
  72. package/src/interfaces/environment.ts +181 -0
  73. package/src/interfaces/failable-result.ts +6 -0
  74. package/src/interfaces/fec-consts.ts +4 -0
  75. package/src/interfaces/handleable-error-options.ts +6 -0
  76. package/{dist/interfaces/index.d.ts → src/interfaces/index.ts} +0 -1
  77. package/src/interfaces/jwt-consts.ts +23 -0
  78. package/src/interfaces/jwt-sign-response.ts +19 -0
  79. package/src/interfaces/mongo-errors.ts +5 -0
  80. package/src/interfaces/request-user.ts +50 -0
  81. package/src/interfaces/required-string-keys.ts +26 -0
  82. package/src/interfaces/schema.ts +31 -0
  83. package/src/interfaces/server-init-result.ts +37 -0
  84. package/src/interfaces/status-code-response.ts +7 -0
  85. package/src/interfaces/symmetric-encryption-results.d.ts +5 -0
  86. package/src/interfaces/symmetric-encryption-results.d.ts.map +1 -0
  87. package/src/interfaces/symmetric-encryption-results.js.map +1 -0
  88. package/src/interfaces/symmetric-encryption-results.ts +4 -0
  89. package/{dist/interfaces/token-response.d.ts → src/interfaces/token-response.ts} +2 -2
  90. package/src/middlewares/authenticate-crypto.ts +243 -0
  91. package/src/middlewares/authenticate-token.ts +152 -0
  92. package/src/middlewares/cleanup-crypto.ts +40 -0
  93. package/{dist/middlewares/index.d.ts → src/middlewares/index.ts} +0 -1
  94. package/src/middlewares/set-global-context-language.ts +24 -0
  95. package/src/middlewares.ts +120 -0
  96. package/src/model-registry.ts +75 -0
  97. package/src/models/email-token.ts +19 -0
  98. package/{dist/models/index.d.ts → src/models/index.ts} +0 -1
  99. package/src/models/mnemonic.ts +19 -0
  100. package/src/models/role.ts +19 -0
  101. package/src/models/used-direct-login-token.ts +23 -0
  102. package/src/models/user-role.ts +17 -0
  103. package/src/models/user.ts +19 -0
  104. package/src/registry/email-service-registry.ts +24 -0
  105. package/{dist/registry/index.d.ts → src/registry/index.ts} +0 -1
  106. package/src/routers/api.ts +151 -0
  107. package/src/routers/app.ts +258 -0
  108. package/src/routers/base.ts +17 -0
  109. package/{dist/routers/index.d.ts → src/routers/index.ts} +0 -1
  110. package/src/schemas/email-token.ts +91 -0
  111. package/{dist/schemas/index.d.ts → src/schemas/index.ts} +1 -2
  112. package/src/schemas/mnemonic.ts +37 -0
  113. package/src/schemas/role.ts +127 -0
  114. package/src/schemas/schema.ts +140 -0
  115. package/src/schemas/used-direct-login-token.ts +38 -0
  116. package/src/schemas/user-role.ts +75 -0
  117. package/src/schemas/user.ts +202 -0
  118. package/src/services/backup-code.ts +316 -0
  119. package/src/services/base.ts +33 -0
  120. package/src/services/checksum.ts +161 -0
  121. package/src/services/crc.ts +213 -0
  122. package/src/services/database-initialization.ts +1479 -0
  123. package/src/services/db-init-cache.d.ts +16 -0
  124. package/src/services/direct-login-token.ts +62 -0
  125. package/src/services/fec-usage-example.ts +102 -0
  126. package/src/services/fec.ts +296 -0
  127. package/{dist/services/index.d.ts → src/services/index.ts} +0 -1
  128. package/src/services/jwt.ts +134 -0
  129. package/src/services/key-wrapping.ts +434 -0
  130. package/src/services/mnemonic.ts +167 -0
  131. package/src/services/request-user.ts +62 -0
  132. package/src/services/role.ts +396 -0
  133. package/src/services/symmetric.ts +139 -0
  134. package/src/services/system-user.ts +82 -0
  135. package/src/services/user.ts +2137 -0
  136. package/src/services/xor.ts +34 -0
  137. package/src/types.d.ts +44 -0
  138. package/src/types.ts +128 -0
  139. package/src/utils.ts +1022 -0
  140. package/dist/application-base.d.ts +0 -112
  141. package/dist/application-base.d.ts.map +0 -1
  142. package/dist/application-base.js +0 -301
  143. package/dist/application-base.js.map +0 -1
  144. package/dist/application.d.ts +0 -23
  145. package/dist/application.d.ts.map +0 -1
  146. package/dist/application.js +0 -126
  147. package/dist/application.js.map +0 -1
  148. package/dist/backup-code.d.ts +0 -67
  149. package/dist/backup-code.d.ts.map +0 -1
  150. package/dist/backup-code.js +0 -270
  151. package/dist/backup-code.js.map +0 -1
  152. package/dist/constants.d.ts +0 -16
  153. package/dist/constants.d.ts.map +0 -1
  154. package/dist/constants.js +0 -54
  155. package/dist/constants.js.map +0 -1
  156. package/dist/controllers/base.d.ts +0 -63
  157. package/dist/controllers/base.d.ts.map +0 -1
  158. package/dist/controllers/base.js +0 -269
  159. package/dist/controllers/base.js.map +0 -1
  160. package/dist/controllers/index.d.ts.map +0 -1
  161. package/dist/controllers/index.js +0 -19
  162. package/dist/controllers/index.js.map +0 -1
  163. package/dist/controllers/user.d.ts +0 -45
  164. package/dist/controllers/user.d.ts.map +0 -1
  165. package/dist/controllers/user.js +0 -750
  166. package/dist/controllers/user.js.map +0 -1
  167. package/dist/decorators/base-controller.d.ts +0 -14
  168. package/dist/decorators/base-controller.d.ts.map +0 -1
  169. package/dist/decorators/base-controller.js +0 -49
  170. package/dist/decorators/base-controller.js.map +0 -1
  171. package/dist/decorators/controller.d.ts +0 -32
  172. package/dist/decorators/controller.d.ts.map +0 -1
  173. package/dist/decorators/controller.js +0 -67
  174. package/dist/decorators/controller.js.map +0 -1
  175. package/dist/decorators/index.d.ts.map +0 -1
  176. package/dist/decorators/index.js +0 -20
  177. package/dist/decorators/index.js.map +0 -1
  178. package/dist/decorators/zod-validation.d.ts +0 -5
  179. package/dist/decorators/zod-validation.d.ts.map +0 -1
  180. package/dist/decorators/zod-validation.js +0 -47
  181. package/dist/decorators/zod-validation.js.map +0 -1
  182. package/dist/defaults.d.ts +0 -7
  183. package/dist/defaults.d.ts.map +0 -1
  184. package/dist/defaults.js +0 -83
  185. package/dist/defaults.js.map +0 -1
  186. package/dist/documents/base.d.ts +0 -3
  187. package/dist/documents/base.d.ts.map +0 -1
  188. package/dist/documents/base.js +0 -3
  189. package/dist/documents/base.js.map +0 -1
  190. package/dist/documents/email-token.d.ts +0 -8
  191. package/dist/documents/email-token.d.ts.map +0 -1
  192. package/dist/documents/email-token.js +0 -3
  193. package/dist/documents/email-token.js.map +0 -1
  194. package/dist/documents/index.d.ts.map +0 -1
  195. package/dist/documents/index.js +0 -3
  196. package/dist/documents/index.js.map +0 -1
  197. package/dist/documents/mnemonic.d.ts.map +0 -1
  198. package/dist/documents/mnemonic.js +0 -3
  199. package/dist/documents/mnemonic.js.map +0 -1
  200. package/dist/documents/role.d.ts.map +0 -1
  201. package/dist/documents/role.js +0 -3
  202. package/dist/documents/role.js.map +0 -1
  203. package/dist/documents/used-direct-login-token.d.ts +0 -5
  204. package/dist/documents/used-direct-login-token.d.ts.map +0 -1
  205. package/dist/documents/used-direct-login-token.js +0 -3
  206. package/dist/documents/used-direct-login-token.js.map +0 -1
  207. package/dist/documents/user-role.d.ts.map +0 -1
  208. package/dist/documents/user-role.js +0 -3
  209. package/dist/documents/user-role.js.map +0 -1
  210. package/dist/documents/user.d.ts.map +0 -1
  211. package/dist/documents/user.js +0 -3
  212. package/dist/documents/user.js.map +0 -1
  213. package/dist/enumerations/base-model-name.d.ts +0 -38
  214. package/dist/enumerations/base-model-name.d.ts.map +0 -1
  215. package/dist/enumerations/base-model-name.js +0 -34
  216. package/dist/enumerations/base-model-name.js.map +0 -1
  217. package/dist/enumerations/index.d.ts.map +0 -1
  218. package/dist/enumerations/index.js +0 -21
  219. package/dist/enumerations/index.js.map +0 -1
  220. package/dist/enumerations/length-encoding-type.d.ts +0 -7
  221. package/dist/enumerations/length-encoding-type.d.ts.map +0 -1
  222. package/dist/enumerations/length-encoding-type.js +0 -11
  223. package/dist/enumerations/length-encoding-type.js.map +0 -1
  224. package/dist/enumerations/schema-collection.d.ts +0 -34
  225. package/dist/enumerations/schema-collection.d.ts.map +0 -1
  226. package/dist/enumerations/schema-collection.js +0 -38
  227. package/dist/enumerations/schema-collection.js.map +0 -1
  228. package/dist/enumerations/symmetric-error-type.d.ts +0 -5
  229. package/dist/enumerations/symmetric-error-type.d.ts.map +0 -1
  230. package/dist/enumerations/symmetric-error-type.js +0 -9
  231. package/dist/enumerations/symmetric-error-type.js.map +0 -1
  232. package/dist/environment.d.ts +0 -189
  233. package/dist/environment.d.ts.map +0 -1
  234. package/dist/environment.js +0 -618
  235. package/dist/environment.js.map +0 -1
  236. package/dist/errors/express-validation.d.ts +0 -9
  237. package/dist/errors/express-validation.d.ts.map +0 -1
  238. package/dist/errors/express-validation.js +0 -17
  239. package/dist/errors/express-validation.js.map +0 -1
  240. package/dist/errors/index.d.ts.map +0 -1
  241. package/dist/errors/index.js +0 -29
  242. package/dist/errors/index.js.map +0 -1
  243. package/dist/errors/invalid-backup-code-version.d.ts +0 -6
  244. package/dist/errors/invalid-backup-code-version.d.ts.map +0 -1
  245. package/dist/errors/invalid-backup-code-version.js +0 -14
  246. package/dist/errors/invalid-backup-code-version.js.map +0 -1
  247. package/dist/errors/invalid-jwt-token.d.ts +0 -5
  248. package/dist/errors/invalid-jwt-token.d.ts.map +0 -1
  249. package/dist/errors/invalid-jwt-token.js +0 -11
  250. package/dist/errors/invalid-jwt-token.js.map +0 -1
  251. package/dist/errors/invalid-model.d.ts +0 -6
  252. package/dist/errors/invalid-model.d.ts.map +0 -1
  253. package/dist/errors/invalid-model.js +0 -13
  254. package/dist/errors/invalid-model.js.map +0 -1
  255. package/dist/errors/invalid-new-password.d.ts +0 -5
  256. package/dist/errors/invalid-new-password.d.ts.map +0 -1
  257. package/dist/errors/invalid-new-password.js +0 -14
  258. package/dist/errors/invalid-new-password.js.map +0 -1
  259. package/dist/errors/invalid-password.d.ts +0 -5
  260. package/dist/errors/invalid-password.d.ts.map +0 -1
  261. package/dist/errors/invalid-password.js +0 -14
  262. package/dist/errors/invalid-password.js.map +0 -1
  263. package/dist/errors/missing-validated-data.d.ts +0 -7
  264. package/dist/errors/missing-validated-data.d.ts.map +0 -1
  265. package/dist/errors/missing-validated-data.js +0 -34
  266. package/dist/errors/missing-validated-data.js.map +0 -1
  267. package/dist/errors/mnemonic-or-password-required.d.ts +0 -5
  268. package/dist/errors/mnemonic-or-password-required.d.ts.map +0 -1
  269. package/dist/errors/mnemonic-or-password-required.js +0 -13
  270. package/dist/errors/mnemonic-or-password-required.js.map +0 -1
  271. package/dist/errors/model-not-registered.d.ts +0 -5
  272. package/dist/errors/model-not-registered.d.ts.map +0 -1
  273. package/dist/errors/model-not-registered.js +0 -12
  274. package/dist/errors/model-not-registered.js.map +0 -1
  275. package/dist/errors/mongoose-validation.d.ts +0 -11
  276. package/dist/errors/mongoose-validation.d.ts.map +0 -1
  277. package/dist/errors/mongoose-validation.js +0 -16
  278. package/dist/errors/mongoose-validation.js.map +0 -1
  279. package/dist/errors/symmetric.d.ts +0 -8
  280. package/dist/errors/symmetric.d.ts.map +0 -1
  281. package/dist/errors/symmetric.js +0 -23
  282. package/dist/errors/symmetric.js.map +0 -1
  283. package/dist/errors/token-expired.d.ts +0 -5
  284. package/dist/errors/token-expired.d.ts.map +0 -1
  285. package/dist/errors/token-expired.js +0 -11
  286. package/dist/errors/token-expired.js.map +0 -1
  287. package/dist/get-language.d.ts +0 -2
  288. package/dist/get-language.d.ts.map +0 -1
  289. package/dist/get-language.js +0 -30
  290. package/dist/get-language.js.map +0 -1
  291. package/dist/get-timezone.d.ts +0 -3
  292. package/dist/get-timezone.d.ts.map +0 -1
  293. package/dist/get-timezone.js +0 -31
  294. package/dist/get-timezone.js.map +0 -1
  295. package/dist/index.d.ts.map +0 -1
  296. package/dist/index.js +0 -40
  297. package/dist/index.js.map +0 -1
  298. package/dist/interfaces/api-error-response.d.ts.map +0 -1
  299. package/dist/interfaces/api-error-response.js +0 -3
  300. package/dist/interfaces/api-error-response.js.map +0 -1
  301. package/dist/interfaces/api-express-validation-error-response.d.ts +0 -7
  302. package/dist/interfaces/api-express-validation-error-response.d.ts.map +0 -1
  303. package/dist/interfaces/api-express-validation-error-response.js +0 -3
  304. package/dist/interfaces/api-express-validation-error-response.js.map +0 -1
  305. package/dist/interfaces/api-message-response.d.ts +0 -4
  306. package/dist/interfaces/api-message-response.d.ts.map +0 -1
  307. package/dist/interfaces/api-message-response.js +0 -3
  308. package/dist/interfaces/api-message-response.js.map +0 -1
  309. package/dist/interfaces/api-mongo-validation-error-response.d.ts.map +0 -1
  310. package/dist/interfaces/api-mongo-validation-error-response.js +0 -3
  311. package/dist/interfaces/api-mongo-validation-error-response.js.map +0 -1
  312. package/dist/interfaces/api-responses/backup-codes-response.d.ts.map +0 -1
  313. package/dist/interfaces/api-responses/backup-codes-response.js +0 -3
  314. package/dist/interfaces/api-responses/backup-codes-response.js.map +0 -1
  315. package/dist/interfaces/api-responses/challenge-response.d.ts.map +0 -1
  316. package/dist/interfaces/api-responses/challenge-response.js +0 -3
  317. package/dist/interfaces/api-responses/challenge-response.js.map +0 -1
  318. package/dist/interfaces/api-responses/code-count-response.d.ts.map +0 -1
  319. package/dist/interfaces/api-responses/code-count-response.js +0 -3
  320. package/dist/interfaces/api-responses/code-count-response.js.map +0 -1
  321. package/dist/interfaces/api-responses/index.d.ts.map +0 -1
  322. package/dist/interfaces/api-responses/index.js +0 -24
  323. package/dist/interfaces/api-responses/index.js.map +0 -1
  324. package/dist/interfaces/api-responses/login-response.d.ts.map +0 -1
  325. package/dist/interfaces/api-responses/login-response.js +0 -3
  326. package/dist/interfaces/api-responses/login-response.js.map +0 -1
  327. package/dist/interfaces/api-responses/mnemonic-response.d.ts.map +0 -1
  328. package/dist/interfaces/api-responses/mnemonic-response.js +0 -3
  329. package/dist/interfaces/api-responses/mnemonic-response.js.map +0 -1
  330. package/dist/interfaces/api-responses/registration-response.d.ts.map +0 -1
  331. package/dist/interfaces/api-responses/registration-response.js +0 -3
  332. package/dist/interfaces/api-responses/registration-response.js.map +0 -1
  333. package/dist/interfaces/api-responses/request-user-response.d.ts.map +0 -1
  334. package/dist/interfaces/api-responses/request-user-response.js +0 -3
  335. package/dist/interfaces/api-responses/request-user-response.js.map +0 -1
  336. package/dist/interfaces/application.d.ts.map +0 -1
  337. package/dist/interfaces/application.js +0 -3
  338. package/dist/interfaces/application.js.map +0 -1
  339. package/dist/interfaces/backend-objects/email-token.d.ts +0 -4
  340. package/dist/interfaces/backend-objects/email-token.d.ts.map +0 -1
  341. package/dist/interfaces/backend-objects/email-token.js +0 -3
  342. package/dist/interfaces/backend-objects/email-token.js.map +0 -1
  343. package/dist/interfaces/backend-objects/index.d.ts.map +0 -1
  344. package/dist/interfaces/backend-objects/index.js +0 -21
  345. package/dist/interfaces/backend-objects/index.js.map +0 -1
  346. package/dist/interfaces/backend-objects/request-user.d.ts.map +0 -1
  347. package/dist/interfaces/backend-objects/request-user.js +0 -3
  348. package/dist/interfaces/backend-objects/request-user.js.map +0 -1
  349. package/dist/interfaces/backend-objects/role.d.ts.map +0 -1
  350. package/dist/interfaces/backend-objects/role.js +0 -3
  351. package/dist/interfaces/backend-objects/role.js.map +0 -1
  352. package/dist/interfaces/backend-objects/user.d.ts +0 -4
  353. package/dist/interfaces/backend-objects/user.d.ts.map +0 -1
  354. package/dist/interfaces/backend-objects/user.js +0 -3
  355. package/dist/interfaces/backend-objects/user.js.map +0 -1
  356. package/dist/interfaces/checksum-config.d.ts +0 -5
  357. package/dist/interfaces/checksum-config.d.ts.map +0 -1
  358. package/dist/interfaces/checksum-config.js +0 -3
  359. package/dist/interfaces/checksum-config.js.map +0 -1
  360. package/dist/interfaces/checksum-consts.d.ts +0 -11
  361. package/dist/interfaces/checksum-consts.d.ts.map +0 -1
  362. package/dist/interfaces/checksum-consts.js +0 -3
  363. package/dist/interfaces/checksum-consts.js.map +0 -1
  364. package/dist/interfaces/constants.d.ts.map +0 -1
  365. package/dist/interfaces/constants.js +0 -3
  366. package/dist/interfaces/constants.js.map +0 -1
  367. package/dist/interfaces/create-user-basics.d.ts +0 -18
  368. package/dist/interfaces/create-user-basics.d.ts.map +0 -1
  369. package/dist/interfaces/create-user-basics.js +0 -3
  370. package/dist/interfaces/create-user-basics.js.map +0 -1
  371. package/dist/interfaces/csp-config.d.ts +0 -14
  372. package/dist/interfaces/csp-config.d.ts.map +0 -1
  373. package/dist/interfaces/csp-config.js +0 -3
  374. package/dist/interfaces/csp-config.js.map +0 -1
  375. package/dist/interfaces/deep-partial.d.ts +0 -4
  376. package/dist/interfaces/deep-partial.d.ts.map +0 -1
  377. package/dist/interfaces/deep-partial.js +0 -3
  378. package/dist/interfaces/deep-partial.js.map +0 -1
  379. package/dist/interfaces/discriminator-collections.d.ts.map +0 -1
  380. package/dist/interfaces/discriminator-collections.js +0 -3
  381. package/dist/interfaces/discriminator-collections.js.map +0 -1
  382. package/dist/interfaces/email-service.d.ts +0 -4
  383. package/dist/interfaces/email-service.d.ts.map +0 -1
  384. package/dist/interfaces/email-service.js +0 -3
  385. package/dist/interfaces/email-service.js.map +0 -1
  386. package/dist/interfaces/environment-mongo.d.ts +0 -76
  387. package/dist/interfaces/environment-mongo.d.ts.map +0 -1
  388. package/dist/interfaces/environment-mongo.js +0 -3
  389. package/dist/interfaces/environment-mongo.js.map +0 -1
  390. package/dist/interfaces/environment.d.ts +0 -181
  391. package/dist/interfaces/environment.d.ts.map +0 -1
  392. package/dist/interfaces/environment.js +0 -3
  393. package/dist/interfaces/environment.js.map +0 -1
  394. package/dist/interfaces/failable-result.d.ts +0 -7
  395. package/dist/interfaces/failable-result.d.ts.map +0 -1
  396. package/dist/interfaces/failable-result.js +0 -3
  397. package/dist/interfaces/failable-result.js.map +0 -1
  398. package/dist/interfaces/fec-consts.d.ts +0 -5
  399. package/dist/interfaces/fec-consts.d.ts.map +0 -1
  400. package/dist/interfaces/fec-consts.js +0 -3
  401. package/dist/interfaces/fec-consts.js.map +0 -1
  402. package/dist/interfaces/handleable-error-options.d.ts +0 -7
  403. package/dist/interfaces/handleable-error-options.d.ts.map +0 -1
  404. package/dist/interfaces/handleable-error-options.js +0 -3
  405. package/dist/interfaces/handleable-error-options.js.map +0 -1
  406. package/dist/interfaces/index.d.ts.map +0 -1
  407. package/dist/interfaces/index.js +0 -46
  408. package/dist/interfaces/index.js.map +0 -1
  409. package/dist/interfaces/jwt-consts.d.ts +0 -11
  410. package/dist/interfaces/jwt-consts.d.ts.map +0 -1
  411. package/dist/interfaces/jwt-consts.js +0 -3
  412. package/dist/interfaces/jwt-consts.js.map +0 -1
  413. package/dist/interfaces/jwt-sign-response.d.ts +0 -11
  414. package/dist/interfaces/jwt-sign-response.d.ts.map +0 -1
  415. package/dist/interfaces/jwt-sign-response.js +0 -3
  416. package/dist/interfaces/jwt-sign-response.js.map +0 -1
  417. package/dist/interfaces/mongo-errors.d.ts +0 -5
  418. package/dist/interfaces/mongo-errors.d.ts.map +0 -1
  419. package/dist/interfaces/mongo-errors.js +0 -3
  420. package/dist/interfaces/mongo-errors.js.map +0 -1
  421. package/dist/interfaces/request-user.d.ts +0 -42
  422. package/dist/interfaces/request-user.d.ts.map +0 -1
  423. package/dist/interfaces/request-user.js +0 -3
  424. package/dist/interfaces/request-user.js.map +0 -1
  425. package/dist/interfaces/required-string-keys.d.ts +0 -22
  426. package/dist/interfaces/required-string-keys.d.ts.map +0 -1
  427. package/dist/interfaces/required-string-keys.js +0 -3
  428. package/dist/interfaces/required-string-keys.js.map +0 -1
  429. package/dist/interfaces/schema.d.ts +0 -29
  430. package/dist/interfaces/schema.d.ts.map +0 -1
  431. package/dist/interfaces/schema.js +0 -3
  432. package/dist/interfaces/schema.js.map +0 -1
  433. package/dist/interfaces/server-init-result.d.ts +0 -35
  434. package/dist/interfaces/server-init-result.d.ts.map +0 -1
  435. package/dist/interfaces/server-init-result.js +0 -3
  436. package/dist/interfaces/server-init-result.js.map +0 -1
  437. package/dist/interfaces/status-code-response.d.ts +0 -7
  438. package/dist/interfaces/status-code-response.d.ts.map +0 -1
  439. package/dist/interfaces/status-code-response.js +0 -3
  440. package/dist/interfaces/status-code-response.js.map +0 -1
  441. package/dist/interfaces/symmetric-encryption-results.d.ts +0 -5
  442. package/dist/interfaces/symmetric-encryption-results.d.ts.map +0 -1
  443. package/dist/interfaces/symmetric-encryption-results.js.map +0 -1
  444. package/dist/interfaces/token-response.d.ts.map +0 -1
  445. package/dist/interfaces/token-response.js +0 -3
  446. package/dist/interfaces/token-response.js.map +0 -1
  447. package/dist/middlewares/authenticate-crypto.d.ts +0 -13
  448. package/dist/middlewares/authenticate-crypto.d.ts.map +0 -1
  449. package/dist/middlewares/authenticate-crypto.js +0 -146
  450. package/dist/middlewares/authenticate-crypto.js.map +0 -1
  451. package/dist/middlewares/authenticate-token.d.ts +0 -24
  452. package/dist/middlewares/authenticate-token.d.ts.map +0 -1
  453. package/dist/middlewares/authenticate-token.js +0 -102
  454. package/dist/middlewares/authenticate-token.js.map +0 -1
  455. package/dist/middlewares/cleanup-crypto.d.ts +0 -7
  456. package/dist/middlewares/cleanup-crypto.d.ts.map +0 -1
  457. package/dist/middlewares/cleanup-crypto.js +0 -32
  458. package/dist/middlewares/cleanup-crypto.js.map +0 -1
  459. package/dist/middlewares/index.d.ts.map +0 -1
  460. package/dist/middlewares/index.js +0 -21
  461. package/dist/middlewares/index.js.map +0 -1
  462. package/dist/middlewares/set-global-context-language.d.ts +0 -3
  463. package/dist/middlewares/set-global-context-language.d.ts.map +0 -1
  464. package/dist/middlewares/set-global-context-language.js +0 -14
  465. package/dist/middlewares/set-global-context-language.js.map +0 -1
  466. package/dist/middlewares.d.ts +0 -18
  467. package/dist/middlewares.d.ts.map +0 -1
  468. package/dist/middlewares.js +0 -76
  469. package/dist/middlewares.js.map +0 -1
  470. package/dist/model-registry.d.ts +0 -23
  471. package/dist/model-registry.d.ts.map +0 -1
  472. package/dist/model-registry.js +0 -47
  473. package/dist/model-registry.js.map +0 -1
  474. package/dist/models/email-token.d.ts +0 -11
  475. package/dist/models/email-token.d.ts.map +0 -1
  476. package/dist/models/email-token.js +0 -11
  477. package/dist/models/email-token.js.map +0 -1
  478. package/dist/models/index.d.ts.map +0 -1
  479. package/dist/models/index.js +0 -23
  480. package/dist/models/index.js.map +0 -1
  481. package/dist/models/mnemonic.d.ts +0 -11
  482. package/dist/models/mnemonic.d.ts.map +0 -1
  483. package/dist/models/mnemonic.js +0 -11
  484. package/dist/models/mnemonic.js.map +0 -1
  485. package/dist/models/role.d.ts +0 -11
  486. package/dist/models/role.d.ts.map +0 -1
  487. package/dist/models/role.js +0 -11
  488. package/dist/models/role.js.map +0 -1
  489. package/dist/models/used-direct-login-token.d.ts +0 -11
  490. package/dist/models/used-direct-login-token.d.ts.map +0 -1
  491. package/dist/models/used-direct-login-token.js +0 -11
  492. package/dist/models/used-direct-login-token.js.map +0 -1
  493. package/dist/models/user-role.d.ts +0 -6
  494. package/dist/models/user-role.d.ts.map +0 -1
  495. package/dist/models/user-role.js +0 -10
  496. package/dist/models/user-role.js.map +0 -1
  497. package/dist/models/user.d.ts +0 -7
  498. package/dist/models/user.d.ts.map +0 -1
  499. package/dist/models/user.js +0 -11
  500. package/dist/models/user.js.map +0 -1
  501. package/dist/registry/email-service-registry.d.ts +0 -9
  502. package/dist/registry/email-service-registry.d.ts.map +0 -1
  503. package/dist/registry/email-service-registry.js +0 -17
  504. package/dist/registry/email-service-registry.js.map +0 -1
  505. package/dist/registry/index.d.ts.map +0 -1
  506. package/dist/registry/index.js +0 -6
  507. package/dist/registry/index.js.map +0 -1
  508. package/dist/routers/api.d.ts +0 -27
  509. package/dist/routers/api.d.ts.map +0 -1
  510. package/dist/routers/api.js +0 -44
  511. package/dist/routers/api.js.map +0 -1
  512. package/dist/routers/app.d.ts +0 -28
  513. package/dist/routers/app.d.ts.map +0 -1
  514. package/dist/routers/app.js +0 -182
  515. package/dist/routers/app.js.map +0 -1
  516. package/dist/routers/base.d.ts +0 -12
  517. package/dist/routers/base.d.ts.map +0 -1
  518. package/dist/routers/base.js +0 -12
  519. package/dist/routers/base.js.map +0 -1
  520. package/dist/routers/index.d.ts.map +0 -1
  521. package/dist/routers/index.js +0 -20
  522. package/dist/routers/index.js.map +0 -1
  523. package/dist/schemas/email-token.d.ts +0 -38
  524. package/dist/schemas/email-token.d.ts.map +0 -1
  525. package/dist/schemas/email-token.js +0 -56
  526. package/dist/schemas/email-token.js.map +0 -1
  527. package/dist/schemas/index.d.ts.map +0 -1
  528. package/dist/schemas/index.js +0 -24
  529. package/dist/schemas/index.js.map +0 -1
  530. package/dist/schemas/mnemonic.d.ts +0 -20
  531. package/dist/schemas/mnemonic.d.ts.map +0 -1
  532. package/dist/schemas/mnemonic.js +0 -30
  533. package/dist/schemas/mnemonic.js.map +0 -1
  534. package/dist/schemas/role.d.ts +0 -32
  535. package/dist/schemas/role.d.ts.map +0 -1
  536. package/dist/schemas/role.js +0 -86
  537. package/dist/schemas/role.js.map +0 -1
  538. package/dist/schemas/schema.d.ts +0 -40
  539. package/dist/schemas/schema.d.ts.map +0 -1
  540. package/dist/schemas/schema.js +0 -64
  541. package/dist/schemas/schema.js.map +0 -1
  542. package/dist/schemas/used-direct-login-token.d.ts +0 -27
  543. package/dist/schemas/used-direct-login-token.d.ts.map +0 -1
  544. package/dist/schemas/used-direct-login-token.js +0 -23
  545. package/dist/schemas/used-direct-login-token.js.map +0 -1
  546. package/dist/schemas/user-role.d.ts +0 -29
  547. package/dist/schemas/user-role.d.ts.map +0 -1
  548. package/dist/schemas/user-role.js +0 -54
  549. package/dist/schemas/user-role.js.map +0 -1
  550. package/dist/schemas/user.d.ts +0 -21
  551. package/dist/schemas/user.d.ts.map +0 -1
  552. package/dist/schemas/user.js +0 -178
  553. package/dist/schemas/user.js.map +0 -1
  554. package/dist/services/backup-code.d.ts +0 -78
  555. package/dist/services/backup-code.d.ts.map +0 -1
  556. package/dist/services/backup-code.js +0 -180
  557. package/dist/services/backup-code.js.map +0 -1
  558. package/dist/services/base.d.ts +0 -13
  559. package/dist/services/base.d.ts.map +0 -1
  560. package/dist/services/base.js +0 -14
  561. package/dist/services/base.js.map +0 -1
  562. package/dist/services/checksum.d.ts +0 -67
  563. package/dist/services/checksum.d.ts.map +0 -1
  564. package/dist/services/checksum.js +0 -175
  565. package/dist/services/checksum.js.map +0 -1
  566. package/dist/services/crc.d.ts +0 -87
  567. package/dist/services/crc.d.ts.map +0 -1
  568. package/dist/services/crc.js +0 -198
  569. package/dist/services/crc.js.map +0 -1
  570. package/dist/services/database-initialization.d.ts +0 -105
  571. package/dist/services/database-initialization.d.ts.map +0 -1
  572. package/dist/services/database-initialization.js +0 -779
  573. package/dist/services/database-initialization.js.map +0 -1
  574. package/dist/services/direct-login-token.d.ts +0 -9
  575. package/dist/services/direct-login-token.d.ts.map +0 -1
  576. package/dist/services/direct-login-token.js +0 -41
  577. package/dist/services/direct-login-token.js.map +0 -1
  578. package/dist/services/fec-usage-example.d.ts +0 -38
  579. package/dist/services/fec-usage-example.d.ts.map +0 -1
  580. package/dist/services/fec-usage-example.js +0 -77
  581. package/dist/services/fec-usage-example.js.map +0 -1
  582. package/dist/services/fec.d.ts +0 -46
  583. package/dist/services/fec.d.ts.map +0 -1
  584. package/dist/services/fec.js +0 -192
  585. package/dist/services/fec.js.map +0 -1
  586. package/dist/services/index.d.ts.map +0 -1
  587. package/dist/services/index.js +0 -35
  588. package/dist/services/index.js.map +0 -1
  589. package/dist/services/jwt.d.ts +0 -33
  590. package/dist/services/jwt.d.ts.map +0 -1
  591. package/dist/services/jwt.js +0 -90
  592. package/dist/services/jwt.js.map +0 -1
  593. package/dist/services/key-wrapping.d.ts +0 -60
  594. package/dist/services/key-wrapping.d.ts.map +0 -1
  595. package/dist/services/key-wrapping.js +0 -311
  596. package/dist/services/key-wrapping.js.map +0 -1
  597. package/dist/services/mnemonic.d.ts +0 -61
  598. package/dist/services/mnemonic.d.ts.map +0 -1
  599. package/dist/services/mnemonic.js +0 -112
  600. package/dist/services/mnemonic.js.map +0 -1
  601. package/dist/services/request-user.d.ts +0 -20
  602. package/dist/services/request-user.d.ts.map +0 -1
  603. package/dist/services/request-user.js +0 -50
  604. package/dist/services/request-user.js.map +0 -1
  605. package/dist/services/role.d.ts +0 -88
  606. package/dist/services/role.d.ts.map +0 -1
  607. package/dist/services/role.js +0 -263
  608. package/dist/services/role.js.map +0 -1
  609. package/dist/services/symmetric.d.ts +0 -42
  610. package/dist/services/symmetric.d.ts.map +0 -1
  611. package/dist/services/symmetric.js +0 -101
  612. package/dist/services/symmetric.js.map +0 -1
  613. package/dist/services/system-user.d.ts +0 -17
  614. package/dist/services/system-user.d.ts.map +0 -1
  615. package/dist/services/system-user.js +0 -46
  616. package/dist/services/system-user.js.map +0 -1
  617. package/dist/services/user.d.ts +0 -320
  618. package/dist/services/user.d.ts.map +0 -1
  619. package/dist/services/user.js +0 -1374
  620. package/dist/services/user.js.map +0 -1
  621. package/dist/services/xor.d.ts +0 -24
  622. package/dist/services/xor.d.ts.map +0 -1
  623. package/dist/services/xor.js +0 -37
  624. package/dist/services/xor.js.map +0 -1
  625. package/dist/types.d.ts +0 -70
  626. package/dist/types.d.ts.map +0 -1
  627. package/dist/types.js +0 -14
  628. package/dist/types.js.map +0 -1
  629. package/dist/utils.d.ts +0 -202
  630. package/dist/utils.d.ts.map +0 -1
  631. package/dist/utils.js +0 -786
  632. package/dist/utils.js.map +0 -1
  633. /package/{dist → src}/interfaces/symmetric-encryption-results.js +0 -0
@@ -0,0 +1,1451 @@
1
+ /// <reference path="../types.d.ts" />
2
+
3
+ import {
4
+ ECIES,
5
+ EmailString,
6
+ SecureString,
7
+ UINT64_SIZE,
8
+ } from '@digitaldefiance/ecies-lib';
9
+ import {
10
+ CoreLanguageCode,
11
+ HandleableError,
12
+ isValidTimezone,
13
+ LanguageCodes,
14
+ } from '@digitaldefiance/i18n-lib';
15
+ import {
16
+ Member as BackendMember,
17
+ ECIESService,
18
+ } from '@digitaldefiance/node-ecies-lib';
19
+ import {
20
+ AccountStatus,
21
+ Constants as AppConstants,
22
+ EmailTokenType,
23
+ GenericValidationError,
24
+ getSuiteCoreTranslation,
25
+ ITokenRole,
26
+ ITokenUser,
27
+ IUserBase,
28
+ SuiteCoreStringKey,
29
+ UsernameOrEmailRequiredError,
30
+ } from '@digitaldefiance/suite-core-lib';
31
+ import type { NextFunction, Request, Response } from 'express';
32
+ import { body } from 'express-validator';
33
+ import { Types } from 'mongoose';
34
+ import { z } from 'zod';
35
+ import { BackupCode } from '../backup-code';
36
+ import { DecoratorBaseController } from '../decorators/base-controller';
37
+ import { Controller, Get, Post } from '../decorators/controller';
38
+ import { IBaseDocument } from '../documents';
39
+ import { IUserDocument } from '../documents/user';
40
+ import { BaseModelName } from '../enumerations/base-model-name';
41
+ import { Environment } from '../environment';
42
+ import { MnemonicOrPasswordRequiredError } from '../errors/mnemonic-or-password-required';
43
+ import {
44
+ IApiChallengeResponse,
45
+ IApiCodeCountResponse,
46
+ IApiLoginResponse,
47
+ IApiMessageResponse,
48
+ IApiMnemonicResponse,
49
+ IApiRegistrationResponse,
50
+ IApiRequestUserResponse,
51
+ IConstants,
52
+ } from '../interfaces';
53
+ import { IApiBackupCodesResponse } from '../interfaces/api-responses/backup-codes-response';
54
+ import type { IApplication } from '../interfaces/application';
55
+ import { IStatusCodeResponse } from '../interfaces/status-code-response';
56
+ import { findAuthToken } from '../middlewares/authenticate-token';
57
+ import { BackupCodeService } from '../services/backup-code';
58
+ import { JwtService } from '../services/jwt';
59
+ import { RequestUserService } from '../services/request-user';
60
+ import { RoleService } from '../services/role';
61
+ import { SystemUserService } from '../services/system-user';
62
+ import { UserService } from '../services/user';
63
+ import { ApiErrorResponse } from '../types';
64
+ import { requireValidatedFieldsAsync, withTransaction } from '../utils';
65
+
66
+ const isString = (v: unknown): v is string => typeof v === 'string';
67
+
68
+ const RegisterSchema = z.object({
69
+ username: z.string(),
70
+ email: z.string(),
71
+ timezone: z.string(),
72
+ password: z.string().min(8).optional(),
73
+ });
74
+
75
+ const EmailLoginChallengeSchema = z.object({
76
+ token: z.string(),
77
+ signature: z.string(),
78
+ email: z.string().optional(),
79
+ username: z.string().optional(),
80
+ });
81
+
82
+ const DirectLoginChallengeSchema = z.object({
83
+ challenge: z.string(),
84
+ signature: z.string(),
85
+ email: z.string().optional(),
86
+ username: z.string().optional(),
87
+ });
88
+
89
+ @Controller()
90
+ export class UserController<
91
+ I extends Types.ObjectId | string = Types.ObjectId,
92
+ D extends Date = Date,
93
+ S extends string = string,
94
+ A extends string = string,
95
+ TUser extends IUserBase<I, D, S, A> = IUserBase<I, D, S, A>,
96
+ TTokenRole extends ITokenRole<I, D> = ITokenRole<I, D>,
97
+ TTokenUser extends ITokenUser = ITokenUser,
98
+ TApplication extends IApplication<
99
+ any,
100
+ Types.ObjectId,
101
+ IBaseDocument<any, Types.ObjectId>,
102
+ Environment,
103
+ IConstants
104
+ > = IApplication<
105
+ any,
106
+ Types.ObjectId,
107
+ IBaseDocument<any, Types.ObjectId>,
108
+ Environment,
109
+ IConstants
110
+ >,
111
+ TLanguage extends CoreLanguageCode = CoreLanguageCode,
112
+ > extends DecoratorBaseController<TLanguage> {
113
+ protected readonly userService: UserService<
114
+ any,
115
+ I,
116
+ D,
117
+ S,
118
+ A,
119
+ any,
120
+ any,
121
+ any,
122
+ TUser,
123
+ TTokenRole,
124
+ TApplication
125
+ >;
126
+ protected readonly jwtService: JwtService<
127
+ I,
128
+ D,
129
+ TTokenRole,
130
+ TTokenUser,
131
+ TApplication
132
+ >;
133
+ protected readonly backupCodeService: BackupCodeService<
134
+ I,
135
+ D,
136
+ TTokenRole,
137
+ TApplication
138
+ >;
139
+ protected readonly roleService: RoleService<I, D, TTokenRole>;
140
+ protected readonly eciesService: ECIESService;
141
+ protected readonly systemUser: BackendMember;
142
+
143
+ constructor(
144
+ application: IApplication<
145
+ any,
146
+ Types.ObjectId,
147
+ IBaseDocument<any, Types.ObjectId>,
148
+ Environment,
149
+ IConstants
150
+ >,
151
+ jwtService: JwtService<I, D, TTokenRole, TTokenUser, TApplication>,
152
+ userService: UserService<
153
+ any,
154
+ I,
155
+ D,
156
+ S,
157
+ A,
158
+ any,
159
+ any,
160
+ any,
161
+ TUser,
162
+ TTokenRole,
163
+ TApplication
164
+ >,
165
+ backupCodeService: BackupCodeService<I, D, TTokenRole, TApplication>,
166
+ roleService: RoleService<I, D, TTokenRole>,
167
+ eciesService: ECIESService,
168
+ ) {
169
+ super(application);
170
+ this.jwtService = jwtService;
171
+ this.userService = userService;
172
+ this.backupCodeService = backupCodeService;
173
+ this.roleService = roleService;
174
+ this.eciesService = eciesService;
175
+ this.systemUser = SystemUserService.getSystemUser(application.environment);
176
+ }
177
+
178
+ @Get('/verify', { auth: true })
179
+ async tokenVerifiedResponse(
180
+ req: Request,
181
+ res: Response,
182
+ next: NextFunction,
183
+ ): Promise<IStatusCodeResponse<IApiRequestUserResponse | ApiErrorResponse>> {
184
+ if (!req.user) {
185
+ throw new HandleableError(
186
+ new Error(
187
+ getSuiteCoreTranslation(SuiteCoreStringKey.Common_NoUserOnRequest),
188
+ ),
189
+ {
190
+ statusCode: 401,
191
+ },
192
+ );
193
+ }
194
+ return {
195
+ statusCode: 200,
196
+ response: {
197
+ message: getSuiteCoreTranslation(
198
+ SuiteCoreStringKey.Validation_TokenValid,
199
+ ),
200
+ user: req.user,
201
+ },
202
+ };
203
+ }
204
+
205
+ @Get('/refresh-token', { auth: true })
206
+ async refreshToken(
207
+ req: Request,
208
+ res: Response,
209
+ next: NextFunction,
210
+ ): Promise<IStatusCodeResponse<IApiLoginResponse | ApiErrorResponse>> {
211
+ const token = findAuthToken(req.headers);
212
+ if (!token) {
213
+ throw new GenericValidationError(
214
+ getSuiteCoreTranslation(SuiteCoreStringKey.Validation_TokenMissing),
215
+ );
216
+ }
217
+
218
+ const tokenUser = await this.jwtService.verifyToken(token);
219
+ if (!tokenUser) {
220
+ throw new GenericValidationError(
221
+ getSuiteCoreTranslation(SuiteCoreStringKey.Validation_TokenInvalid),
222
+ );
223
+ }
224
+
225
+ const UserModel = this.application.getModel<IUserDocument>(
226
+ BaseModelName.User,
227
+ );
228
+ const userDoc = await UserModel.findById(tokenUser.userId, {
229
+ password: 0,
230
+ });
231
+ if (!userDoc || userDoc.accountStatus !== AccountStatus.Active) {
232
+ throw new GenericValidationError(
233
+ getSuiteCoreTranslation(SuiteCoreStringKey.Validation_UserNotFound),
234
+ );
235
+ }
236
+ const { token: newToken, roles } = await this.jwtService.signToken(
237
+ userDoc,
238
+ this.application.environment.jwtSecret,
239
+ (req.user?.siteLanguage as string) ?? LanguageCodes.EN_US,
240
+ );
241
+
242
+ return {
243
+ statusCode: 200,
244
+ response: {
245
+ message: getSuiteCoreTranslation(SuiteCoreStringKey.TokenRefreshed),
246
+ user: RequestUserService.makeRequestUserDTO(userDoc, roles),
247
+ token: newToken,
248
+ serverPublicKey: this.application.environment.systemPublicKeyHex ?? '',
249
+ },
250
+ headers: {
251
+ Authorization: `Bearer ${newToken}`,
252
+ },
253
+ };
254
+ }
255
+
256
+ @Post('/register', {
257
+ schema: RegisterSchema,
258
+ validation: (validationLanguage: TLanguage) => [
259
+ body('username')
260
+ .matches(AppConstants.UsernameRegex)
261
+ .withMessage(
262
+ getSuiteCoreTranslation(
263
+ SuiteCoreStringKey.Validation_UsernameRegexErrorTemplate,
264
+ undefined,
265
+ validationLanguage,
266
+ ),
267
+ ),
268
+ body('email')
269
+ .isEmail()
270
+ .withMessage(
271
+ getSuiteCoreTranslation(
272
+ SuiteCoreStringKey.Validation_InvalidEmail,
273
+ undefined,
274
+ validationLanguage,
275
+ ),
276
+ ),
277
+ body('timezone')
278
+ .isString()
279
+ .custom((value) => isValidTimezone(value))
280
+ .withMessage(
281
+ getSuiteCoreTranslation(
282
+ SuiteCoreStringKey.Validation_TimezoneInvalid,
283
+ undefined,
284
+ validationLanguage,
285
+ ),
286
+ ),
287
+ body('password')
288
+ .optional()
289
+ .matches(AppConstants.PasswordRegex)
290
+ .withMessage(
291
+ getSuiteCoreTranslation(
292
+ SuiteCoreStringKey.Validation_PasswordRegexErrorTemplate,
293
+ ),
294
+ ),
295
+ ],
296
+ })
297
+ async register(
298
+ req: Request,
299
+ res: Response,
300
+ next: NextFunction,
301
+ ): Promise<IStatusCodeResponse<IApiRegistrationResponse | ApiErrorResponse>> {
302
+ return await withTransaction(
303
+ this.application.db.connection,
304
+ this.application.environment.mongo.useTransactions,
305
+ undefined,
306
+ async (sess) => {
307
+ return await requireValidatedFieldsAsync(
308
+ req,
309
+ RegisterSchema,
310
+ async ({ username, email, timezone, password }) => {
311
+ if (
312
+ !isString(username) ||
313
+ !isString(email) ||
314
+ !isString(timezone)
315
+ ) {
316
+ throw new GenericValidationError(
317
+ getSuiteCoreTranslation(
318
+ SuiteCoreStringKey.Validation_MissingValidatedData,
319
+ ),
320
+ );
321
+ }
322
+
323
+ const { user, mnemonic, backupCodes } =
324
+ await this.userService.newUser(
325
+ this.systemUser,
326
+ {
327
+ username: username.trim(),
328
+ email: email.trim(),
329
+ timezone: timezone,
330
+ },
331
+ undefined,
332
+ undefined,
333
+ sess,
334
+ this.application.environment.debug,
335
+ password as string | undefined,
336
+ );
337
+
338
+ await this.userService.createAndSendEmailToken(
339
+ user,
340
+ EmailTokenType.AccountVerification,
341
+ sess,
342
+ this.application.environment.debug,
343
+ );
344
+
345
+ return {
346
+ statusCode: 201,
347
+ response: {
348
+ message: getSuiteCoreTranslation(
349
+ SuiteCoreStringKey.Registration_Success,
350
+ { MNEMONIC: mnemonic },
351
+ ),
352
+ mnemonic,
353
+ backupCodes,
354
+ },
355
+ };
356
+ },
357
+ );
358
+ },
359
+ {
360
+ timeoutMs: this.application.environment.mongo.transactionTimeout * 30,
361
+ },
362
+ );
363
+ }
364
+
365
+ @Post('/account-verification', {
366
+ validation: (validationLanguage: TLanguage) => [
367
+ body('token')
368
+ .not()
369
+ .isEmpty()
370
+ .withMessage(
371
+ getSuiteCoreTranslation(
372
+ SuiteCoreStringKey.Validation_TokenRequired,
373
+ undefined,
374
+ validationLanguage,
375
+ ),
376
+ )
377
+ .matches(new RegExp(`^[a-f0-9]{${AppConstants.EmailTokenLength * 2}}$`))
378
+ .withMessage(
379
+ getSuiteCoreTranslation(
380
+ SuiteCoreStringKey.Validation_InvalidToken,
381
+ undefined,
382
+ validationLanguage,
383
+ ),
384
+ ),
385
+ ],
386
+ })
387
+ async completeAccountVerification(
388
+ req: Request,
389
+ res: Response,
390
+ next: NextFunction,
391
+ ): Promise<IStatusCodeResponse<IApiMessageResponse | ApiErrorResponse>> {
392
+ const { token } = this.validatedBody;
393
+
394
+ return await withTransaction(
395
+ this.application.db.connection,
396
+ this.application.environment.mongo.useTransactions,
397
+ undefined,
398
+ async (sess) => {
399
+ await this.userService.verifyAccountTokenAndComplete(
400
+ token as string,
401
+ sess,
402
+ );
403
+ return {
404
+ statusCode: 200,
405
+ response: {
406
+ message: getSuiteCoreTranslation(
407
+ SuiteCoreStringKey.EmailVerification_Success,
408
+ ),
409
+ },
410
+ };
411
+ },
412
+ );
413
+ }
414
+
415
+ @Post('/language', {
416
+ auth: true,
417
+ validation: (validationLanguage: TLanguage) => [
418
+ body('language')
419
+ .isString()
420
+ .withMessage(
421
+ getSuiteCoreTranslation(
422
+ SuiteCoreStringKey.Validation_InvalidLanguage,
423
+ undefined,
424
+ validationLanguage,
425
+ ),
426
+ )
427
+ .isIn(Object.values(LanguageCodes))
428
+ .withMessage(
429
+ getSuiteCoreTranslation(
430
+ SuiteCoreStringKey.Validation_InvalidLanguage,
431
+ undefined,
432
+ validationLanguage,
433
+ ),
434
+ ),
435
+ ],
436
+ })
437
+ async setLanguage(
438
+ req: Request,
439
+ res: Response,
440
+ next: NextFunction,
441
+ ): Promise<IStatusCodeResponse<IApiRequestUserResponse | ApiErrorResponse>> {
442
+ return await withTransaction(
443
+ this.application.db.connection,
444
+ this.application.environment.mongo.useTransactions,
445
+ undefined,
446
+ async (sess) => {
447
+ const { language } = this.validatedBody;
448
+ if (!req.user) {
449
+ throw new HandleableError(
450
+ new Error(
451
+ getSuiteCoreTranslation(
452
+ SuiteCoreStringKey.Common_NoUserOnRequest,
453
+ ),
454
+ ),
455
+ { statusCode: 401 },
456
+ );
457
+ }
458
+
459
+ const user = await this.userService.updateSiteLanguage(
460
+ req.user.id,
461
+ language as string,
462
+ sess,
463
+ );
464
+
465
+ return {
466
+ statusCode: 200,
467
+ response: {
468
+ message: getSuiteCoreTranslation(
469
+ SuiteCoreStringKey.LanguageUpdate_Success,
470
+ ),
471
+ user,
472
+ },
473
+ };
474
+ },
475
+ );
476
+ }
477
+
478
+ @Get('/backup-codes', { auth: true })
479
+ async getBackupCodeCount(
480
+ req: Request,
481
+ res: Response,
482
+ next: NextFunction,
483
+ ): Promise<IStatusCodeResponse<IApiCodeCountResponse | ApiErrorResponse>> {
484
+ if (!req.user) {
485
+ throw new HandleableError(
486
+ new Error(
487
+ getSuiteCoreTranslation(SuiteCoreStringKey.Common_NoUserOnRequest),
488
+ ),
489
+ { statusCode: 401 },
490
+ );
491
+ }
492
+
493
+ const UserModel = this.application.getModel<IUserDocument>(
494
+ BaseModelName.User,
495
+ );
496
+ const user = await UserModel.findById(req.user.id);
497
+
498
+ return {
499
+ statusCode: 200,
500
+ response: {
501
+ message: 'Backup codes retrieved',
502
+ codeCount: user?.backupCodes?.length || 0,
503
+ } as IApiCodeCountResponse,
504
+ };
505
+ }
506
+
507
+ @Post('/backup-codes', {
508
+ auth: true,
509
+ cryptoAuth: true,
510
+ validation: (validationLanguage: TLanguage) => [
511
+ body().custom((value, { req }) => {
512
+ if (!req.body?.password && !req.body?.mnemonic) {
513
+ throw new MnemonicOrPasswordRequiredError();
514
+ }
515
+ return true;
516
+ }),
517
+ body('password')
518
+ .optional()
519
+ .notEmpty()
520
+ .withMessage(
521
+ getSuiteCoreTranslation(
522
+ SuiteCoreStringKey.Validation_CurrentPasswordRequired,
523
+ undefined,
524
+ validationLanguage,
525
+ ),
526
+ ),
527
+ body('mnemonic')
528
+ .optional()
529
+ .notEmpty()
530
+ .withMessage(
531
+ getSuiteCoreTranslation(
532
+ SuiteCoreStringKey.Validation_MnemonicRequired,
533
+ undefined,
534
+ validationLanguage,
535
+ ),
536
+ )
537
+ .matches(AppConstants.MnemonicRegex)
538
+ .withMessage(
539
+ getSuiteCoreTranslation(
540
+ SuiteCoreStringKey.Validation_MnemonicRegex,
541
+ undefined,
542
+ validationLanguage,
543
+ ),
544
+ ),
545
+ ],
546
+ })
547
+ async resetBackupCodes(
548
+ req: Request,
549
+ res: Response,
550
+ next: NextFunction,
551
+ ): Promise<IStatusCodeResponse<IApiBackupCodesResponse | ApiErrorResponse>> {
552
+ if (!req.user || !req.eciesUser || !req.eciesUser.hasPrivateKey) {
553
+ throw new HandleableError(
554
+ new Error(
555
+ getSuiteCoreTranslation(SuiteCoreStringKey.Common_NoUserOnRequest),
556
+ ),
557
+ { statusCode: 401 },
558
+ );
559
+ }
560
+
561
+ const newBackupCodes = await this.userService.resetUserBackupCodes(
562
+ req.eciesUser,
563
+ this.systemUser,
564
+ );
565
+ const codes = newBackupCodes.map((c) => c.notNullValue);
566
+ newBackupCodes.forEach((c) => c.dispose());
567
+
568
+ return {
569
+ statusCode: 200,
570
+ response: {
571
+ message: getSuiteCoreTranslation(
572
+ SuiteCoreStringKey.BackupCodeRecovery_YourNewCodes,
573
+ ),
574
+ backupCodes: codes,
575
+ },
576
+ };
577
+ }
578
+
579
+ @Post('/recover-mnemonic', {
580
+ auth: true,
581
+ cryptoAuth: true,
582
+ validation: (validationLanguage: TLanguage) => [
583
+ body('password')
584
+ .isString()
585
+ .withMessage(
586
+ getSuiteCoreTranslation(
587
+ SuiteCoreStringKey.Validation_CurrentPasswordRequired,
588
+ undefined,
589
+ validationLanguage,
590
+ ),
591
+ ),
592
+ ],
593
+ })
594
+ async recoverMnemonic(
595
+ req: Request,
596
+ res: Response,
597
+ next: NextFunction,
598
+ ): Promise<IStatusCodeResponse<IApiMnemonicResponse | ApiErrorResponse>> {
599
+ return await withTransaction(
600
+ this.application.db.connection,
601
+ this.application.environment.mongo.useTransactions,
602
+ undefined,
603
+ async (sess) => {
604
+ if (!req.user) {
605
+ throw new HandleableError(
606
+ new Error(
607
+ getSuiteCoreTranslation(
608
+ SuiteCoreStringKey.Validation_InvalidCredentials,
609
+ ),
610
+ ),
611
+ { statusCode: 401 },
612
+ );
613
+ } else if (!req.eciesUser) {
614
+ throw new HandleableError(
615
+ new Error(
616
+ getSuiteCoreTranslation(
617
+ SuiteCoreStringKey.Validation_MnemonicOrPasswordRequired,
618
+ ),
619
+ ),
620
+ { statusCode: 401 },
621
+ );
622
+ }
623
+
624
+ const { password } = this.validatedBody;
625
+ if (!isString(password)) {
626
+ throw new GenericValidationError(
627
+ getSuiteCoreTranslation(
628
+ SuiteCoreStringKey.Validation_MissingValidatedData,
629
+ ),
630
+ );
631
+ }
632
+
633
+ const userDoc = await this.userService.findUserById(
634
+ new Types.ObjectId(req.user.id),
635
+ true,
636
+ sess,
637
+ );
638
+
639
+ const mnemonic = await this.userService.recoverMnemonic(
640
+ req.eciesUser,
641
+ userDoc.mnemonicRecovery,
642
+ );
643
+
644
+ return {
645
+ statusCode: 200,
646
+ response: {
647
+ message: getSuiteCoreTranslation(
648
+ SuiteCoreStringKey.MnemonicRecovery_Success,
649
+ ),
650
+ mnemonic: mnemonic.notNullValue,
651
+ },
652
+ };
653
+ },
654
+ );
655
+ }
656
+
657
+ @Post('/change-password', {
658
+ auth: true,
659
+ validation: (validationLanguage: TLanguage) => [
660
+ body('currentPassword')
661
+ .notEmpty()
662
+ .withMessage(
663
+ getSuiteCoreTranslation(
664
+ SuiteCoreStringKey.Validation_Required,
665
+ undefined,
666
+ validationLanguage,
667
+ ),
668
+ ),
669
+ body('newPassword')
670
+ .matches(AppConstants.PasswordRegex)
671
+ .withMessage(
672
+ getSuiteCoreTranslation(
673
+ SuiteCoreStringKey.Validation_PasswordRegexErrorTemplate,
674
+ ),
675
+ )
676
+ .notEmpty()
677
+ .withMessage(
678
+ getSuiteCoreTranslation(
679
+ SuiteCoreStringKey.Validation_Required,
680
+ undefined,
681
+ validationLanguage,
682
+ ),
683
+ ),
684
+ ],
685
+ })
686
+ async changePassword(
687
+ req: Request,
688
+ res: Response,
689
+ next: NextFunction,
690
+ ): Promise<IStatusCodeResponse<IApiMessageResponse | ApiErrorResponse>> {
691
+ return await withTransaction(
692
+ this.application.db.connection,
693
+ this.application.environment.mongo.useTransactions,
694
+ undefined,
695
+ async (sess) => {
696
+ const { currentPassword, newPassword } = this.validatedBody;
697
+ if (!req.user) {
698
+ throw new HandleableError(
699
+ new Error(
700
+ getSuiteCoreTranslation(
701
+ SuiteCoreStringKey.Common_NoUserOnRequest,
702
+ ),
703
+ ),
704
+ { statusCode: 401 },
705
+ );
706
+ }
707
+
708
+ if (!isString(currentPassword) || !isString(newPassword)) {
709
+ throw new GenericValidationError(
710
+ getSuiteCoreTranslation(
711
+ SuiteCoreStringKey.Validation_MissingValidatedData,
712
+ ),
713
+ );
714
+ }
715
+
716
+ await this.userService.changePassword(
717
+ req.user.id,
718
+ currentPassword,
719
+ newPassword,
720
+ sess,
721
+ );
722
+
723
+ return {
724
+ statusCode: 200,
725
+ response: {
726
+ message: getSuiteCoreTranslation(
727
+ SuiteCoreStringKey.PasswordChange_Success,
728
+ ),
729
+ },
730
+ };
731
+ },
732
+ );
733
+ }
734
+
735
+ @Post('/request-direct-login')
736
+ async requestDirectLogin(
737
+ req: Request,
738
+ res: Response,
739
+ next: NextFunction,
740
+ ): Promise<IStatusCodeResponse<IApiChallengeResponse | ApiErrorResponse>> {
741
+ const challenge = this.userService.generateDirectLoginChallenge();
742
+ return {
743
+ statusCode: 200,
744
+ response: {
745
+ challenge: challenge,
746
+ message: getSuiteCoreTranslation(
747
+ SuiteCoreStringKey.Login_ChallengeGenerated,
748
+ ),
749
+ serverPublicKey: this.application.environment.systemPublicKeyHex ?? '',
750
+ },
751
+ };
752
+ }
753
+
754
+ @Post('/direct-challenge', {
755
+ schema: DirectLoginChallengeSchema,
756
+ validation: (validationLanguage: TLanguage) => [
757
+ body('challenge')
758
+ .not()
759
+ .isEmpty()
760
+ .withMessage(
761
+ getSuiteCoreTranslation(
762
+ SuiteCoreStringKey.Validation_InvalidChallenge,
763
+ undefined,
764
+ validationLanguage,
765
+ ),
766
+ )
767
+ .matches(
768
+ new RegExp(
769
+ `^[a-f0-9]{${(UINT64_SIZE + 32 + ECIES.SIGNATURE_SIZE) * 2}}$`,
770
+ ),
771
+ )
772
+ .withMessage(
773
+ getSuiteCoreTranslation(
774
+ SuiteCoreStringKey.Validation_InvalidChallenge,
775
+ undefined,
776
+ validationLanguage,
777
+ ),
778
+ ),
779
+ body('signature')
780
+ .not()
781
+ .isEmpty()
782
+ .withMessage(
783
+ getSuiteCoreTranslation(
784
+ SuiteCoreStringKey.Validation_InvalidSignature,
785
+ ),
786
+ )
787
+ .matches(new RegExp(`^[a-f0-9]{${ECIES.SIGNATURE_SIZE * 2}}$`))
788
+ .withMessage(SuiteCoreStringKey.Validation_InvalidSignature),
789
+ body().custom((value, { req }) => {
790
+ if (!req.body.username && !req.body.email) {
791
+ throw new UsernameOrEmailRequiredError();
792
+ }
793
+ return true;
794
+ }),
795
+ body('username')
796
+ .optional()
797
+ .matches(AppConstants.UsernameRegex)
798
+ .withMessage(
799
+ getSuiteCoreTranslation(
800
+ SuiteCoreStringKey.Validation_UsernameRegexErrorTemplate,
801
+ undefined,
802
+ validationLanguage,
803
+ ),
804
+ ),
805
+ body('email')
806
+ .optional()
807
+ .isEmail()
808
+ .withMessage(
809
+ getSuiteCoreTranslation(
810
+ SuiteCoreStringKey.Validation_InvalidEmail,
811
+ undefined,
812
+ validationLanguage,
813
+ ),
814
+ ),
815
+ ],
816
+ })
817
+ async directLoginChallenge(
818
+ req: Request,
819
+ res: Response,
820
+ next: NextFunction,
821
+ ): Promise<IStatusCodeResponse<IApiLoginResponse | ApiErrorResponse>> {
822
+ return await withTransaction(
823
+ this.application.db.connection,
824
+ this.application.environment.mongo.useTransactions,
825
+ undefined,
826
+ async (sess) => {
827
+ const { username, email, challenge, signature } = this.validatedBody;
828
+
829
+ const { userDoc } = await this.userService.verifyDirectLoginChallenge(
830
+ String(challenge),
831
+ String(signature) as any,
832
+ username ? String(username) : undefined,
833
+ email ? String(email) : undefined,
834
+ sess,
835
+ );
836
+
837
+ const { token: jwtToken, roles } = await this.jwtService.signToken(
838
+ userDoc,
839
+ this.application.environment.jwtSecret,
840
+ (req.user?.siteLanguage as string) ?? LanguageCodes.EN_US,
841
+ );
842
+
843
+ return {
844
+ statusCode: 200,
845
+ response: {
846
+ user: userDoc as any,
847
+ token: jwtToken,
848
+ serverPublicKey:
849
+ this.application.environment.systemPublicKeyHex ?? '',
850
+ message: getSuiteCoreTranslation(
851
+ SuiteCoreStringKey.LoggedIn_Success,
852
+ ),
853
+ },
854
+ };
855
+ },
856
+ );
857
+ }
858
+
859
+ @Post('/request-email-login', {
860
+ validation: (validationLanguage: TLanguage) => [
861
+ body().custom((value, { req }) => {
862
+ if (!req.body.username && !req.body.email) {
863
+ throw new UsernameOrEmailRequiredError();
864
+ }
865
+ return true;
866
+ }),
867
+ body('username')
868
+ .optional()
869
+ .matches(AppConstants.UsernameRegex)
870
+ .withMessage(
871
+ getSuiteCoreTranslation(
872
+ SuiteCoreStringKey.Validation_UsernameRegexErrorTemplate,
873
+ undefined,
874
+ validationLanguage,
875
+ ),
876
+ ),
877
+ body('email')
878
+ .optional()
879
+ .isEmail()
880
+ .withMessage(
881
+ getSuiteCoreTranslation(
882
+ SuiteCoreStringKey.Validation_InvalidEmail,
883
+ undefined,
884
+ validationLanguage,
885
+ ),
886
+ ),
887
+ ],
888
+ })
889
+ async requestEmailLogin(
890
+ req: Request,
891
+ res: Response,
892
+ next: NextFunction,
893
+ ): Promise<IStatusCodeResponse<IApiMessageResponse | ApiErrorResponse>> {
894
+ const { username, email } = this.validatedBody;
895
+
896
+ try {
897
+ await withTransaction(
898
+ this.application.db.connection,
899
+ this.application.environment.mongo.useTransactions,
900
+ undefined,
901
+ async (sess) => {
902
+ const userDoc = await this.userService.findUser(
903
+ email as string,
904
+ username as string,
905
+ sess,
906
+ );
907
+ await this.userService.createAndSendEmailToken(
908
+ userDoc,
909
+ EmailTokenType.LoginRequest,
910
+ sess,
911
+ this.application.environment.debug,
912
+ );
913
+ },
914
+ );
915
+ } catch (error) {
916
+ // Suppress user-related errors for security
917
+ }
918
+
919
+ return {
920
+ statusCode: 200,
921
+ response: {
922
+ message: getSuiteCoreTranslation(SuiteCoreStringKey.Email_TokenSent),
923
+ },
924
+ };
925
+ }
926
+
927
+ @Post('/email-challenge', {
928
+ schema: EmailLoginChallengeSchema,
929
+ validation: (validationLanguage: TLanguage) => [
930
+ body('token')
931
+ .not()
932
+ .isEmpty()
933
+ .withMessage(
934
+ getSuiteCoreTranslation(
935
+ SuiteCoreStringKey.Validation_TokenRequired,
936
+ undefined,
937
+ validationLanguage,
938
+ ),
939
+ )
940
+ .matches(new RegExp(`^[a-f0-9]{${AppConstants.EmailTokenLength * 2}}$`))
941
+ .withMessage(
942
+ getSuiteCoreTranslation(
943
+ SuiteCoreStringKey.Validation_InvalidToken,
944
+ undefined,
945
+ validationLanguage,
946
+ ),
947
+ ),
948
+ body('signature')
949
+ .not()
950
+ .isEmpty()
951
+ .withMessage(
952
+ getSuiteCoreTranslation(
953
+ SuiteCoreStringKey.Validation_InvalidSignature,
954
+ ),
955
+ )
956
+ .matches(new RegExp(`^[a-f0-9]{${ECIES.SIGNATURE_SIZE * 2}}$`))
957
+ .withMessage(SuiteCoreStringKey.Validation_InvalidSignature),
958
+ body().custom((value, { req }) => {
959
+ if (!req.body.username && !req.body.email) {
960
+ throw new UsernameOrEmailRequiredError();
961
+ }
962
+ return true;
963
+ }),
964
+ body('username')
965
+ .optional()
966
+ .matches(AppConstants.UsernameRegex)
967
+ .withMessage(
968
+ getSuiteCoreTranslation(
969
+ SuiteCoreStringKey.Validation_UsernameRegexErrorTemplate,
970
+ undefined,
971
+ validationLanguage,
972
+ ),
973
+ ),
974
+ body('email')
975
+ .optional()
976
+ .isEmail()
977
+ .withMessage(
978
+ getSuiteCoreTranslation(
979
+ SuiteCoreStringKey.Validation_InvalidEmail,
980
+ undefined,
981
+ validationLanguage,
982
+ ),
983
+ ),
984
+ ],
985
+ })
986
+ async emailLoginChallenge(
987
+ req: Request,
988
+ res: Response,
989
+ next: NextFunction,
990
+ ): Promise<IStatusCodeResponse<IApiLoginResponse | ApiErrorResponse>> {
991
+ return await withTransaction(
992
+ this.application.db.connection,
993
+ this.application.environment.mongo.useTransactions,
994
+ undefined,
995
+ async (sess) => {
996
+ const { token, signature } = this.validatedBody;
997
+
998
+ const userDoc = await this.userService.validateEmailLoginTokenChallenge(
999
+ String(token),
1000
+ String(signature) as any,
1001
+ sess,
1002
+ );
1003
+
1004
+ const { token: jwtToken, roles } = await this.jwtService.signToken(
1005
+ userDoc,
1006
+ this.application.environment.jwtSecret,
1007
+ (req.user?.siteLanguage as string) ?? LanguageCodes.EN_US,
1008
+ );
1009
+
1010
+ return {
1011
+ statusCode: 200,
1012
+ response: {
1013
+ user: userDoc as any,
1014
+ token: jwtToken,
1015
+ serverPublicKey:
1016
+ this.application.environment.systemPublicKeyHex ?? '',
1017
+ message: getSuiteCoreTranslation(
1018
+ SuiteCoreStringKey.LoggedIn_Success,
1019
+ ),
1020
+ },
1021
+ };
1022
+ },
1023
+ );
1024
+ }
1025
+
1026
+ @Post('/resend-verification', {
1027
+ validation: (validationLanguage: TLanguage) => [
1028
+ body().custom((value, { req }) => {
1029
+ if (!req.body.username && !req.body.email) {
1030
+ throw new UsernameOrEmailRequiredError();
1031
+ }
1032
+ return true;
1033
+ }),
1034
+ body('username')
1035
+ .optional()
1036
+ .isString()
1037
+ .matches(AppConstants.UsernameRegex)
1038
+ .withMessage(
1039
+ getSuiteCoreTranslation(
1040
+ SuiteCoreStringKey.Validation_UsernameRegexErrorTemplate,
1041
+ undefined,
1042
+ validationLanguage,
1043
+ ),
1044
+ ),
1045
+ body('email').optional().isEmail(),
1046
+ ],
1047
+ })
1048
+ async resendVerification(
1049
+ req: Request,
1050
+ res: Response,
1051
+ next: NextFunction,
1052
+ ): Promise<IStatusCodeResponse<IApiMessageResponse | ApiErrorResponse>> {
1053
+ return await withTransaction(
1054
+ this.application.db.connection,
1055
+ this.application.environment.mongo.useTransactions,
1056
+ undefined,
1057
+ async (sess) => {
1058
+ const { username, email } = this.validatedBody;
1059
+
1060
+ const UserModel = this.application.getModel<IUserDocument>(
1061
+ BaseModelName.User,
1062
+ );
1063
+ let query: { username?: string; email?: string } = {};
1064
+ if (isString(username)) query.username = username;
1065
+ else if (isString(email)) query.email = email;
1066
+ else {
1067
+ throw new GenericValidationError(
1068
+ getSuiteCoreTranslation(
1069
+ SuiteCoreStringKey.Validation_MissingValidatedData,
1070
+ ),
1071
+ );
1072
+ }
1073
+
1074
+ const user = await UserModel.findOne(query).session(sess ?? null);
1075
+ if (!user) {
1076
+ throw new GenericValidationError(
1077
+ getSuiteCoreTranslation(SuiteCoreStringKey.Validation_UserNotFound),
1078
+ { statusCode: 404 },
1079
+ );
1080
+ }
1081
+
1082
+ await this.userService.resendEmailToken(
1083
+ user._id.toString(),
1084
+ EmailTokenType.AccountVerification,
1085
+ sess,
1086
+ this.application.environment.debug,
1087
+ );
1088
+
1089
+ return {
1090
+ statusCode: 200,
1091
+ response: {
1092
+ message: getSuiteCoreTranslation(
1093
+ SuiteCoreStringKey.EmailVerification_Resent,
1094
+ ),
1095
+ },
1096
+ };
1097
+ },
1098
+ );
1099
+ }
1100
+
1101
+ @Post('/backup-code', {
1102
+ validation: (validationLanguage: TLanguage) => [
1103
+ body('email').optional().isEmail(),
1104
+ body('username')
1105
+ .optional()
1106
+ .matches(AppConstants.UsernameRegex)
1107
+ .withMessage(
1108
+ getSuiteCoreTranslation(
1109
+ SuiteCoreStringKey.Validation_UsernameRegexErrorTemplate,
1110
+ undefined,
1111
+ validationLanguage,
1112
+ ),
1113
+ ),
1114
+ body('code')
1115
+ .custom((value) => {
1116
+ const normalized = BackupCode.normalizeCode(value);
1117
+ return (
1118
+ AppConstants.BACKUP_CODES.DisplayRegex.test(value) ||
1119
+ AppConstants.BACKUP_CODES.NormalizedHexRegex.test(normalized)
1120
+ );
1121
+ })
1122
+ .withMessage(
1123
+ getSuiteCoreTranslation(
1124
+ SuiteCoreStringKey.Validation_InvalidBackupCode,
1125
+ undefined,
1126
+ validationLanguage,
1127
+ ),
1128
+ ),
1129
+ body('recoverMnemonic').isBoolean().optional(),
1130
+ body('newPassword')
1131
+ .optional()
1132
+ .matches(AppConstants.PasswordRegex)
1133
+ .withMessage(
1134
+ getSuiteCoreTranslation(
1135
+ SuiteCoreStringKey.Validation_PasswordRegexErrorTemplate,
1136
+ undefined,
1137
+ validationLanguage,
1138
+ ),
1139
+ ),
1140
+ ],
1141
+ })
1142
+ async useBackupCodeLogin(
1143
+ req: Request,
1144
+ res: Response,
1145
+ next: NextFunction,
1146
+ ): Promise<IStatusCodeResponse<IApiLoginResponse | ApiErrorResponse>> {
1147
+ return await withTransaction(
1148
+ this.application.db.connection,
1149
+ this.application.environment.mongo.useTransactions,
1150
+ undefined,
1151
+ async (sess) => {
1152
+ const { code, newPassword, email, username } = this.validatedBody;
1153
+
1154
+ if (!code) {
1155
+ throw new GenericValidationError(
1156
+ getSuiteCoreTranslation(
1157
+ SuiteCoreStringKey.Validation_MissingValidatedData,
1158
+ ),
1159
+ );
1160
+ }
1161
+
1162
+ const recoverMnemonic =
1163
+ this.validatedBody?.['recoverMnemonic'] === 'true' ||
1164
+ this.validatedBody?.['recoverMnemonic'] === true;
1165
+
1166
+ const userDoc = await this.userService.findUser(
1167
+ email as string,
1168
+ username as string,
1169
+ sess,
1170
+ );
1171
+
1172
+ const {
1173
+ user,
1174
+ userDoc: updatedUserDoc,
1175
+ codeCount,
1176
+ } = await this.backupCodeService.recoverKeyWithBackupCode(
1177
+ userDoc,
1178
+ code as string,
1179
+ newPassword ? new SecureString(newPassword as string) : undefined,
1180
+ sess,
1181
+ );
1182
+
1183
+ let mnemonic: SecureString | undefined;
1184
+ if (recoverMnemonic) {
1185
+ const memberType = await this.roleService.getMemberType(
1186
+ updatedUserDoc,
1187
+ sess,
1188
+ );
1189
+ const freshUser = new BackendMember(
1190
+ this.eciesService,
1191
+ memberType,
1192
+ updatedUserDoc.username,
1193
+ new EmailString(updatedUserDoc.email),
1194
+ Buffer.from(updatedUserDoc.publicKey, 'hex'),
1195
+ user.privateKey,
1196
+ undefined,
1197
+ updatedUserDoc._id,
1198
+ new Date(updatedUserDoc.createdAt),
1199
+ new Date(updatedUserDoc.updatedAt),
1200
+ );
1201
+ mnemonic = await this.userService.recoverMnemonic(
1202
+ freshUser,
1203
+ updatedUserDoc.mnemonicRecovery,
1204
+ );
1205
+ }
1206
+
1207
+ const { token, roles } = await this.jwtService.signToken(
1208
+ userDoc,
1209
+ this.application.environment.jwtSecret,
1210
+ LanguageCodes.EN_US,
1211
+ );
1212
+
1213
+ this.userService.updateLastLogin(updatedUserDoc._id).catch(() => {});
1214
+
1215
+ return {
1216
+ statusCode: 200,
1217
+ response: {
1218
+ user: RequestUserService.makeRequestUserDTO(userDoc, roles),
1219
+ token: token,
1220
+ message: getSuiteCoreTranslation(
1221
+ SuiteCoreStringKey.BackupCodeRecovery_Success,
1222
+ ),
1223
+ codeCount,
1224
+ ...(recoverMnemonic && mnemonic
1225
+ ? { mnemonic: mnemonic.value }
1226
+ : {}),
1227
+ serverPublicKey:
1228
+ this.application.environment.systemPublicKeyHex ?? '',
1229
+ },
1230
+ };
1231
+ },
1232
+ );
1233
+ }
1234
+
1235
+ @Post('/forgot-password', {
1236
+ validation: (validationLanguage: TLanguage) => [
1237
+ body('email')
1238
+ .isEmail()
1239
+ .withMessage(
1240
+ getSuiteCoreTranslation(
1241
+ SuiteCoreStringKey.Validation_InvalidEmail,
1242
+ undefined,
1243
+ validationLanguage,
1244
+ ),
1245
+ ),
1246
+ ],
1247
+ })
1248
+ async forgotPassword(
1249
+ req: Request,
1250
+ res: Response,
1251
+ next: NextFunction,
1252
+ ): Promise<IStatusCodeResponse<IApiMessageResponse | ApiErrorResponse>> {
1253
+ return await withTransaction(
1254
+ this.application.db.connection,
1255
+ this.application.environment.mongo.useTransactions,
1256
+ undefined,
1257
+ async (sess) => {
1258
+ const { email } = this.validatedBody;
1259
+
1260
+ const UserModel = this.application.getModel<IUserDocument>(
1261
+ BaseModelName.User,
1262
+ );
1263
+ if (!isString(email)) {
1264
+ throw new GenericValidationError(
1265
+ getSuiteCoreTranslation(
1266
+ SuiteCoreStringKey.Validation_MissingValidatedData,
1267
+ ),
1268
+ );
1269
+ }
1270
+
1271
+ const user = await UserModel.findOne({
1272
+ email: email.toLowerCase(),
1273
+ }).session(sess ?? null);
1274
+
1275
+ if (!user || !user.passwordWrappedPrivateKey) {
1276
+ return {
1277
+ statusCode: 200,
1278
+ response: {
1279
+ message: getSuiteCoreTranslation(
1280
+ SuiteCoreStringKey.PasswordReset_Success,
1281
+ ),
1282
+ },
1283
+ };
1284
+ }
1285
+
1286
+ await this.userService.createAndSendEmailToken(
1287
+ user,
1288
+ EmailTokenType.PasswordReset,
1289
+ sess,
1290
+ this.application.environment.debug,
1291
+ );
1292
+
1293
+ return {
1294
+ statusCode: 200,
1295
+ response: {
1296
+ message: getSuiteCoreTranslation(
1297
+ SuiteCoreStringKey.PasswordReset_Success,
1298
+ ),
1299
+ },
1300
+ };
1301
+ },
1302
+ );
1303
+ }
1304
+
1305
+ @Get('/verify-reset-token')
1306
+ async verifyResetToken(
1307
+ req: Request,
1308
+ res: Response,
1309
+ next: NextFunction,
1310
+ ): Promise<IStatusCodeResponse<IApiMessageResponse | ApiErrorResponse>> {
1311
+ const token = req.query['token'] as string;
1312
+ if (!token) {
1313
+ throw new GenericValidationError(
1314
+ getSuiteCoreTranslation(SuiteCoreStringKey.Validation_TokenMissing),
1315
+ );
1316
+ }
1317
+
1318
+ return await withTransaction(
1319
+ this.application.db.connection,
1320
+ this.application.environment.mongo.useTransactions,
1321
+ undefined,
1322
+ async (sess) => {
1323
+ await this.userService.verifyEmailToken(
1324
+ token,
1325
+ EmailTokenType.PasswordReset,
1326
+ sess,
1327
+ );
1328
+ return {
1329
+ statusCode: 200,
1330
+ response: {
1331
+ message: 'Token is valid',
1332
+ },
1333
+ };
1334
+ },
1335
+ );
1336
+ }
1337
+
1338
+ @Post('/reset-password', {
1339
+ validation: (validationLanguage: TLanguage) => [
1340
+ body('token')
1341
+ .not()
1342
+ .isEmpty()
1343
+ .withMessage(
1344
+ getSuiteCoreTranslation(
1345
+ SuiteCoreStringKey.Validation_TokenRequired,
1346
+ undefined,
1347
+ validationLanguage,
1348
+ ),
1349
+ )
1350
+ .matches(new RegExp(`^[a-f0-9]{${AppConstants.EmailTokenLength * 2}}$`))
1351
+ .withMessage(
1352
+ getSuiteCoreTranslation(
1353
+ SuiteCoreStringKey.Validation_InvalidToken,
1354
+ undefined,
1355
+ validationLanguage,
1356
+ ),
1357
+ ),
1358
+ body('newPassword')
1359
+ .optional()
1360
+ .isLength({ min: 8 })
1361
+ .withMessage(
1362
+ getSuiteCoreTranslation(
1363
+ SuiteCoreStringKey.Validation_PasswordMinLengthTemplate,
1364
+ undefined,
1365
+ validationLanguage,
1366
+ ),
1367
+ )
1368
+ .matches(AppConstants.PasswordRegex)
1369
+ .withMessage(
1370
+ getSuiteCoreTranslation(
1371
+ SuiteCoreStringKey.Validation_PasswordRegexErrorTemplate,
1372
+ undefined,
1373
+ validationLanguage,
1374
+ ),
1375
+ ),
1376
+ body('password')
1377
+ .optional()
1378
+ .isLength({ min: 8 })
1379
+ .withMessage(
1380
+ getSuiteCoreTranslation(
1381
+ SuiteCoreStringKey.Validation_PasswordMinLengthTemplate,
1382
+ undefined,
1383
+ validationLanguage,
1384
+ ),
1385
+ )
1386
+ .matches(AppConstants.PasswordRegex)
1387
+ .withMessage(
1388
+ getSuiteCoreTranslation(
1389
+ SuiteCoreStringKey.Validation_PasswordRegexErrorTemplate,
1390
+ undefined,
1391
+ validationLanguage,
1392
+ ),
1393
+ ),
1394
+ body('currentPassword').optional().isString(),
1395
+ body('mnemonic').optional().isString(),
1396
+ ],
1397
+ })
1398
+ async resetPassword(
1399
+ req: Request,
1400
+ res: Response,
1401
+ next: NextFunction,
1402
+ ): Promise<IStatusCodeResponse<IApiMessageResponse | ApiErrorResponse>> {
1403
+ return await withTransaction(
1404
+ this.application.db.connection,
1405
+ this.application.environment.mongo.useTransactions,
1406
+ undefined,
1407
+ async (sess) => {
1408
+ const { token, newPassword, password, currentPassword, mnemonic } =
1409
+ this.validatedBody;
1410
+ const selectedNewPassword = (newPassword ?? password) as
1411
+ | string
1412
+ | undefined;
1413
+
1414
+ if (!isString(token) || !isString(selectedNewPassword)) {
1415
+ throw new GenericValidationError(
1416
+ getSuiteCoreTranslation(
1417
+ SuiteCoreStringKey.Validation_MissingValidatedData,
1418
+ ),
1419
+ );
1420
+ }
1421
+
1422
+ const credential =
1423
+ (mnemonic as string | undefined) ??
1424
+ (currentPassword as string | undefined);
1425
+ if (!isString(credential)) {
1426
+ throw new GenericValidationError(
1427
+ getSuiteCoreTranslation(
1428
+ SuiteCoreStringKey.Validation_MissingValidatedData,
1429
+ ),
1430
+ );
1431
+ }
1432
+
1433
+ await this.userService.resetPasswordWithToken(
1434
+ token as string,
1435
+ selectedNewPassword,
1436
+ credential,
1437
+ sess,
1438
+ );
1439
+
1440
+ return {
1441
+ statusCode: 200,
1442
+ response: {
1443
+ message: getSuiteCoreTranslation(
1444
+ SuiteCoreStringKey.PasswordChange_Success,
1445
+ ),
1446
+ },
1447
+ };
1448
+ },
1449
+ );
1450
+ }
1451
+ }