@futdevpro/nts-dynamo 1.15.140 → 1.15.142

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 (294) hide show
  1. package/README.md +27 -0
  2. package/__documentations/2026-08-18-standalone-account-security-and-privacy-bedrock.md +132 -0
  3. package/build/_modules/account-security/_enums/account-challenge-purpose.type-enum.d.ts +6 -0
  4. package/build/_modules/account-security/_enums/account-challenge-purpose.type-enum.d.ts.map +1 -0
  5. package/build/_modules/account-security/_enums/account-challenge-purpose.type-enum.js +10 -0
  6. package/build/_modules/account-security/_enums/account-challenge-purpose.type-enum.js.map +1 -0
  7. package/build/_modules/account-security/_enums/account-refresh-token-state.type-enum.d.ts +7 -0
  8. package/build/_modules/account-security/_enums/account-refresh-token-state.type-enum.d.ts.map +1 -0
  9. package/build/_modules/account-security/_enums/account-refresh-token-state.type-enum.js +11 -0
  10. package/build/_modules/account-security/_enums/account-refresh-token-state.type-enum.js.map +1 -0
  11. package/build/_modules/account-security/_enums/account-session-state.type-enum.d.ts +7 -0
  12. package/build/_modules/account-security/_enums/account-session-state.type-enum.d.ts.map +1 -0
  13. package/build/_modules/account-security/_enums/account-session-state.type-enum.js +11 -0
  14. package/build/_modules/account-security/_enums/account-session-state.type-enum.js.map +1 -0
  15. package/build/_modules/account-security/_models/account-challenge-adapter.interface.d.ts +9 -0
  16. package/build/_modules/account-security/_models/account-challenge-adapter.interface.d.ts.map +1 -0
  17. package/build/_modules/account-security/_models/account-challenge-adapter.interface.js +3 -0
  18. package/build/_modules/account-security/_models/account-challenge-adapter.interface.js.map +1 -0
  19. package/build/_modules/account-security/_models/account-challenge-issued.interface.d.ts +7 -0
  20. package/build/_modules/account-security/_models/account-challenge-issued.interface.d.ts.map +1 -0
  21. package/build/_modules/account-security/_models/account-challenge-issued.interface.js +3 -0
  22. package/build/_modules/account-security/_models/account-challenge-issued.interface.js.map +1 -0
  23. package/build/_modules/account-security/_models/account-challenge-record.interface.d.ts +13 -0
  24. package/build/_modules/account-security/_models/account-challenge-record.interface.d.ts.map +1 -0
  25. package/build/_modules/account-security/_models/account-challenge-record.interface.js +3 -0
  26. package/build/_modules/account-security/_models/account-challenge-record.interface.js.map +1 -0
  27. package/build/_modules/account-security/_models/account-challenge-result.interface.d.ts +8 -0
  28. package/build/_modules/account-security/_models/account-challenge-result.interface.d.ts.map +1 -0
  29. package/build/_modules/account-security/_models/account-challenge-result.interface.js +3 -0
  30. package/build/_modules/account-security/_models/account-challenge-result.interface.js.map +1 -0
  31. package/build/_modules/account-security/_models/account-challenge-settings.interface.d.ts +9 -0
  32. package/build/_modules/account-security/_models/account-challenge-settings.interface.d.ts.map +1 -0
  33. package/build/_modules/account-security/_models/account-challenge-settings.interface.js +3 -0
  34. package/build/_modules/account-security/_models/account-challenge-settings.interface.js.map +1 -0
  35. package/build/_modules/account-security/_models/account-password-policy.interface.d.ts +12 -0
  36. package/build/_modules/account-security/_models/account-password-policy.interface.d.ts.map +1 -0
  37. package/build/_modules/account-security/_models/account-password-policy.interface.js +3 -0
  38. package/build/_modules/account-security/_models/account-password-policy.interface.js.map +1 -0
  39. package/build/_modules/account-security/_models/account-password-verification.interface.d.ts +6 -0
  40. package/build/_modules/account-security/_models/account-password-verification.interface.d.ts.map +1 -0
  41. package/build/_modules/account-security/_models/account-password-verification.interface.js +3 -0
  42. package/build/_modules/account-security/_models/account-password-verification.interface.js.map +1 -0
  43. package/build/_modules/account-security/_models/account-refresh-token-record.interface.d.ts +13 -0
  44. package/build/_modules/account-security/_models/account-refresh-token-record.interface.d.ts.map +1 -0
  45. package/build/_modules/account-security/_models/account-refresh-token-record.interface.js +3 -0
  46. package/build/_modules/account-security/_models/account-refresh-token-record.interface.js.map +1 -0
  47. package/build/_modules/account-security/_models/account-session-adapter.interface.d.ts +19 -0
  48. package/build/_modules/account-security/_models/account-session-adapter.interface.d.ts.map +1 -0
  49. package/build/_modules/account-security/_models/account-session-adapter.interface.js +3 -0
  50. package/build/_modules/account-security/_models/account-session-adapter.interface.js.map +1 -0
  51. package/build/_modules/account-security/_models/account-session-claims.interface.d.ts +8 -0
  52. package/build/_modules/account-security/_models/account-session-claims.interface.d.ts.map +1 -0
  53. package/build/_modules/account-security/_models/account-session-claims.interface.js +3 -0
  54. package/build/_modules/account-security/_models/account-session-claims.interface.js.map +1 -0
  55. package/build/_modules/account-security/_models/account-session-context.interface.d.ts +7 -0
  56. package/build/_modules/account-security/_models/account-session-context.interface.d.ts.map +1 -0
  57. package/build/_modules/account-security/_models/account-session-context.interface.js +3 -0
  58. package/build/_modules/account-security/_models/account-session-context.interface.js.map +1 -0
  59. package/build/_modules/account-security/_models/account-session-create.interface.d.ts +8 -0
  60. package/build/_modules/account-security/_models/account-session-create.interface.d.ts.map +1 -0
  61. package/build/_modules/account-security/_models/account-session-create.interface.js +3 -0
  62. package/build/_modules/account-security/_models/account-session-create.interface.js.map +1 -0
  63. package/build/_modules/account-security/_models/account-session-record.interface.d.ts +21 -0
  64. package/build/_modules/account-security/_models/account-session-record.interface.d.ts.map +1 -0
  65. package/build/_modules/account-security/_models/account-session-record.interface.js +3 -0
  66. package/build/_modules/account-security/_models/account-session-record.interface.js.map +1 -0
  67. package/build/_modules/account-security/_models/account-session-rotate.interface.d.ts +11 -0
  68. package/build/_modules/account-security/_models/account-session-rotate.interface.d.ts.map +1 -0
  69. package/build/_modules/account-security/_models/account-session-rotate.interface.js +3 -0
  70. package/build/_modules/account-security/_models/account-session-rotate.interface.js.map +1 -0
  71. package/build/_modules/account-security/_models/account-session-settings.interface.d.ts +11 -0
  72. package/build/_modules/account-security/_models/account-session-settings.interface.d.ts.map +1 -0
  73. package/build/_modules/account-security/_models/account-session-settings.interface.js +3 -0
  74. package/build/_modules/account-security/_models/account-session-settings.interface.js.map +1 -0
  75. package/build/_modules/account-security/_models/account-session-token-pair.interface.d.ts +9 -0
  76. package/build/_modules/account-security/_models/account-session-token-pair.interface.d.ts.map +1 -0
  77. package/build/_modules/account-security/_models/account-session-token-pair.interface.js +3 -0
  78. package/build/_modules/account-security/_models/account-session-token-pair.interface.js.map +1 -0
  79. package/build/_modules/account-security/_models/account-session-view.interface.d.ts +13 -0
  80. package/build/_modules/account-security/_models/account-session-view.interface.d.ts.map +1 -0
  81. package/build/_modules/account-security/_models/account-session-view.interface.js +3 -0
  82. package/build/_modules/account-security/_models/account-session-view.interface.js.map +1 -0
  83. package/build/_modules/account-security/account-challenge.control-service.d.ts +24 -0
  84. package/build/_modules/account-security/account-challenge.control-service.d.ts.map +1 -0
  85. package/build/_modules/account-security/account-challenge.control-service.js +117 -0
  86. package/build/_modules/account-security/account-challenge.control-service.js.map +1 -0
  87. package/build/_modules/account-security/account-password.control-service.d.ts +24 -0
  88. package/build/_modules/account-security/account-password.control-service.d.ts.map +1 -0
  89. package/build/_modules/account-security/account-password.control-service.js +214 -0
  90. package/build/_modules/account-security/account-password.control-service.js.map +1 -0
  91. package/build/_modules/account-security/account-session.control-service.d.ts +39 -0
  92. package/build/_modules/account-security/account-session.control-service.d.ts.map +1 -0
  93. package/build/_modules/account-security/account-session.control-service.js +277 -0
  94. package/build/_modules/account-security/account-session.control-service.js.map +1 -0
  95. package/build/_modules/account-security/index.d.ts +24 -0
  96. package/build/_modules/account-security/index.d.ts.map +1 -0
  97. package/build/_modules/account-security/index.js +16 -0
  98. package/build/_modules/account-security/index.js.map +1 -0
  99. package/build/_modules/privacy-lifecycle/_enums/privacy-consent-decision.type-enum.d.ts +6 -0
  100. package/build/_modules/privacy-lifecycle/_enums/privacy-consent-decision.type-enum.d.ts.map +1 -0
  101. package/build/_modules/privacy-lifecycle/_enums/privacy-consent-decision.type-enum.js +10 -0
  102. package/build/_modules/privacy-lifecycle/_enums/privacy-consent-decision.type-enum.js.map +1 -0
  103. package/build/_modules/privacy-lifecycle/_enums/privacy-contributor-action.type-enum.d.ts +11 -0
  104. package/build/_modules/privacy-lifecycle/_enums/privacy-contributor-action.type-enum.d.ts.map +1 -0
  105. package/build/_modules/privacy-lifecycle/_enums/privacy-contributor-action.type-enum.js +15 -0
  106. package/build/_modules/privacy-lifecycle/_enums/privacy-contributor-action.type-enum.js.map +1 -0
  107. package/build/_modules/privacy-lifecycle/_enums/privacy-contributor-outcome.type-enum.d.ts +8 -0
  108. package/build/_modules/privacy-lifecycle/_enums/privacy-contributor-outcome.type-enum.d.ts.map +1 -0
  109. package/build/_modules/privacy-lifecycle/_enums/privacy-contributor-outcome.type-enum.js +12 -0
  110. package/build/_modules/privacy-lifecycle/_enums/privacy-contributor-outcome.type-enum.js.map +1 -0
  111. package/build/_modules/privacy-lifecycle/_enums/privacy-request-state.type-enum.d.ts +16 -0
  112. package/build/_modules/privacy-lifecycle/_enums/privacy-request-state.type-enum.d.ts.map +1 -0
  113. package/build/_modules/privacy-lifecycle/_enums/privacy-request-state.type-enum.js +20 -0
  114. package/build/_modules/privacy-lifecycle/_enums/privacy-request-state.type-enum.js.map +1 -0
  115. package/build/_modules/privacy-lifecycle/_enums/privacy-request.type-enum.d.ts +10 -0
  116. package/build/_modules/privacy-lifecycle/_enums/privacy-request.type-enum.d.ts.map +1 -0
  117. package/build/_modules/privacy-lifecycle/_enums/privacy-request.type-enum.js +14 -0
  118. package/build/_modules/privacy-lifecycle/_enums/privacy-request.type-enum.js.map +1 -0
  119. package/build/_modules/privacy-lifecycle/_models/privacy-consent-input.interface.d.ts +12 -0
  120. package/build/_modules/privacy-lifecycle/_models/privacy-consent-input.interface.d.ts.map +1 -0
  121. package/build/_modules/privacy-lifecycle/_models/privacy-consent-input.interface.js +3 -0
  122. package/build/_modules/privacy-lifecycle/_models/privacy-consent-input.interface.js.map +1 -0
  123. package/build/_modules/privacy-lifecycle/_models/privacy-consent-persistence-result.interface.d.ts +7 -0
  124. package/build/_modules/privacy-lifecycle/_models/privacy-consent-persistence-result.interface.d.ts.map +1 -0
  125. package/build/_modules/privacy-lifecycle/_models/privacy-consent-persistence-result.interface.js +3 -0
  126. package/build/_modules/privacy-lifecycle/_models/privacy-consent-persistence-result.interface.js.map +1 -0
  127. package/build/_modules/privacy-lifecycle/_models/privacy-consent-record.interface.d.ts +15 -0
  128. package/build/_modules/privacy-lifecycle/_models/privacy-consent-record.interface.d.ts.map +1 -0
  129. package/build/_modules/privacy-lifecycle/_models/privacy-consent-record.interface.js +3 -0
  130. package/build/_modules/privacy-lifecycle/_models/privacy-consent-record.interface.js.map +1 -0
  131. package/build/_modules/privacy-lifecycle/_models/privacy-consent-result.interface.d.ts +7 -0
  132. package/build/_modules/privacy-lifecycle/_models/privacy-consent-result.interface.d.ts.map +1 -0
  133. package/build/_modules/privacy-lifecycle/_models/privacy-consent-result.interface.js +3 -0
  134. package/build/_modules/privacy-lifecycle/_models/privacy-consent-result.interface.js.map +1 -0
  135. package/build/_modules/privacy-lifecycle/_models/privacy-consent-view.interface.d.ts +13 -0
  136. package/build/_modules/privacy-lifecycle/_models/privacy-consent-view.interface.d.ts.map +1 -0
  137. package/build/_modules/privacy-lifecycle/_models/privacy-consent-view.interface.js +3 -0
  138. package/build/_modules/privacy-lifecycle/_models/privacy-consent-view.interface.js.map +1 -0
  139. package/build/_modules/privacy-lifecycle/_models/privacy-contributor-context.interface.d.ts +9 -0
  140. package/build/_modules/privacy-lifecycle/_models/privacy-contributor-context.interface.d.ts.map +1 -0
  141. package/build/_modules/privacy-lifecycle/_models/privacy-contributor-context.interface.js +3 -0
  142. package/build/_modules/privacy-lifecycle/_models/privacy-contributor-context.interface.js.map +1 -0
  143. package/build/_modules/privacy-lifecycle/_models/privacy-contributor-outcome.interface.d.ts +14 -0
  144. package/build/_modules/privacy-lifecycle/_models/privacy-contributor-outcome.interface.d.ts.map +1 -0
  145. package/build/_modules/privacy-lifecycle/_models/privacy-contributor-outcome.interface.js +3 -0
  146. package/build/_modules/privacy-lifecycle/_models/privacy-contributor-outcome.interface.js.map +1 -0
  147. package/build/_modules/privacy-lifecycle/_models/privacy-contributor-plan.interface.d.ts +8 -0
  148. package/build/_modules/privacy-lifecycle/_models/privacy-contributor-plan.interface.d.ts.map +1 -0
  149. package/build/_modules/privacy-lifecycle/_models/privacy-contributor-plan.interface.js +3 -0
  150. package/build/_modules/privacy-lifecycle/_models/privacy-contributor-plan.interface.js.map +1 -0
  151. package/build/_modules/privacy-lifecycle/_models/privacy-contributor.interface.d.ts +11 -0
  152. package/build/_modules/privacy-lifecycle/_models/privacy-contributor.interface.d.ts.map +1 -0
  153. package/build/_modules/privacy-lifecycle/_models/privacy-contributor.interface.js +3 -0
  154. package/build/_modules/privacy-lifecycle/_models/privacy-contributor.interface.js.map +1 -0
  155. package/build/_modules/privacy-lifecycle/_models/privacy-ledger-adapter.interface.d.ts +13 -0
  156. package/build/_modules/privacy-lifecycle/_models/privacy-ledger-adapter.interface.d.ts.map +1 -0
  157. package/build/_modules/privacy-lifecycle/_models/privacy-ledger-adapter.interface.js +3 -0
  158. package/build/_modules/privacy-lifecycle/_models/privacy-ledger-adapter.interface.js.map +1 -0
  159. package/build/_modules/privacy-lifecycle/_models/privacy-ledger-result.interface.d.ts +9 -0
  160. package/build/_modules/privacy-lifecycle/_models/privacy-ledger-result.interface.d.ts.map +1 -0
  161. package/build/_modules/privacy-lifecycle/_models/privacy-ledger-result.interface.js +3 -0
  162. package/build/_modules/privacy-lifecycle/_models/privacy-ledger-result.interface.js.map +1 -0
  163. package/build/_modules/privacy-lifecycle/_models/privacy-ledger-settings.interface.d.ts +7 -0
  164. package/build/_modules/privacy-lifecycle/_models/privacy-ledger-settings.interface.d.ts.map +1 -0
  165. package/build/_modules/privacy-lifecycle/_models/privacy-ledger-settings.interface.js +3 -0
  166. package/build/_modules/privacy-lifecycle/_models/privacy-ledger-settings.interface.js.map +1 -0
  167. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-input.interface.d.ts +11 -0
  168. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-input.interface.d.ts.map +1 -0
  169. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-input.interface.js +3 -0
  170. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-input.interface.js.map +1 -0
  171. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-persistence-result.interface.d.ts +7 -0
  172. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-persistence-result.interface.d.ts.map +1 -0
  173. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-persistence-result.interface.js +3 -0
  174. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-persistence-result.interface.js.map +1 -0
  175. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-result.interface.d.ts +7 -0
  176. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-result.interface.d.ts.map +1 -0
  177. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-result.interface.js +3 -0
  178. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-result.interface.js.map +1 -0
  179. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-view.interface.d.ts +12 -0
  180. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-view.interface.d.ts.map +1 -0
  181. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-view.interface.js +3 -0
  182. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-view.interface.js.map +1 -0
  183. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance.interface.d.ts +14 -0
  184. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance.interface.d.ts.map +1 -0
  185. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance.interface.js +3 -0
  186. package/build/_modules/privacy-lifecycle/_models/privacy-legal-acceptance.interface.js.map +1 -0
  187. package/build/_modules/privacy-lifecycle/_models/privacy-lifecycle-adapter.interface.d.ts +15 -0
  188. package/build/_modules/privacy-lifecycle/_models/privacy-lifecycle-adapter.interface.d.ts.map +1 -0
  189. package/build/_modules/privacy-lifecycle/_models/privacy-lifecycle-adapter.interface.js +3 -0
  190. package/build/_modules/privacy-lifecycle/_models/privacy-lifecycle-adapter.interface.js.map +1 -0
  191. package/build/_modules/privacy-lifecycle/_models/privacy-lifecycle-settings.interface.d.ts +9 -0
  192. package/build/_modules/privacy-lifecycle/_models/privacy-lifecycle-settings.interface.d.ts.map +1 -0
  193. package/build/_modules/privacy-lifecycle/_models/privacy-lifecycle-settings.interface.js +3 -0
  194. package/build/_modules/privacy-lifecycle/_models/privacy-lifecycle-settings.interface.js.map +1 -0
  195. package/build/_modules/privacy-lifecycle/_models/privacy-request-create-result.interface.d.ts +7 -0
  196. package/build/_modules/privacy-lifecycle/_models/privacy-request-create-result.interface.d.ts.map +1 -0
  197. package/build/_modules/privacy-lifecycle/_models/privacy-request-create-result.interface.js +3 -0
  198. package/build/_modules/privacy-lifecycle/_models/privacy-request-create-result.interface.js.map +1 -0
  199. package/build/_modules/privacy-lifecycle/_models/privacy-request-open.interface.d.ts +10 -0
  200. package/build/_modules/privacy-lifecycle/_models/privacy-request-open.interface.d.ts.map +1 -0
  201. package/build/_modules/privacy-lifecycle/_models/privacy-request-open.interface.js +3 -0
  202. package/build/_modules/privacy-lifecycle/_models/privacy-request-open.interface.js.map +1 -0
  203. package/build/_modules/privacy-lifecycle/_models/privacy-request-persistence-create-result.interface.d.ts +7 -0
  204. package/build/_modules/privacy-lifecycle/_models/privacy-request-persistence-create-result.interface.d.ts.map +1 -0
  205. package/build/_modules/privacy-lifecycle/_models/privacy-request-persistence-create-result.interface.js +3 -0
  206. package/build/_modules/privacy-lifecycle/_models/privacy-request-persistence-create-result.interface.js.map +1 -0
  207. package/build/_modules/privacy-lifecycle/_models/privacy-request-record.interface.d.ts +21 -0
  208. package/build/_modules/privacy-lifecycle/_models/privacy-request-record.interface.d.ts.map +1 -0
  209. package/build/_modules/privacy-lifecycle/_models/privacy-request-record.interface.js +3 -0
  210. package/build/_modules/privacy-lifecycle/_models/privacy-request-record.interface.js.map +1 -0
  211. package/build/_modules/privacy-lifecycle/_models/privacy-request-result.interface.d.ts +8 -0
  212. package/build/_modules/privacy-lifecycle/_models/privacy-request-result.interface.d.ts.map +1 -0
  213. package/build/_modules/privacy-lifecycle/_models/privacy-request-result.interface.js +3 -0
  214. package/build/_modules/privacy-lifecycle/_models/privacy-request-result.interface.js.map +1 -0
  215. package/build/_modules/privacy-lifecycle/_models/privacy-request-view.interface.d.ts +20 -0
  216. package/build/_modules/privacy-lifecycle/_models/privacy-request-view.interface.d.ts.map +1 -0
  217. package/build/_modules/privacy-lifecycle/_models/privacy-request-view.interface.js +3 -0
  218. package/build/_modules/privacy-lifecycle/_models/privacy-request-view.interface.js.map +1 -0
  219. package/build/_modules/privacy-lifecycle/index.d.ts +33 -0
  220. package/build/_modules/privacy-lifecycle/index.d.ts.map +1 -0
  221. package/build/_modules/privacy-lifecycle/index.js +18 -0
  222. package/build/_modules/privacy-lifecycle/index.js.map +1 -0
  223. package/build/_modules/privacy-lifecycle/privacy-ledger.control-service.d.ts +28 -0
  224. package/build/_modules/privacy-lifecycle/privacy-ledger.control-service.d.ts.map +1 -0
  225. package/build/_modules/privacy-lifecycle/privacy-ledger.control-service.js +168 -0
  226. package/build/_modules/privacy-lifecycle/privacy-ledger.control-service.js.map +1 -0
  227. package/build/_modules/privacy-lifecycle/privacy-lifecycle.control-service.d.ts +42 -0
  228. package/build/_modules/privacy-lifecycle/privacy-lifecycle.control-service.d.ts.map +1 -0
  229. package/build/_modules/privacy-lifecycle/privacy-lifecycle.control-service.js +364 -0
  230. package/build/_modules/privacy-lifecycle/privacy-lifecycle.control-service.js.map +1 -0
  231. package/package.json +19 -1
  232. package/src/_modules/account-security/_enums/account-challenge-purpose.type-enum.ts +5 -0
  233. package/src/_modules/account-security/_enums/account-refresh-token-state.type-enum.ts +6 -0
  234. package/src/_modules/account-security/_enums/account-session-state.type-enum.ts +6 -0
  235. package/src/_modules/account-security/_models/account-challenge-adapter.interface.ts +17 -0
  236. package/src/_modules/account-security/_models/account-challenge-issued.interface.ts +6 -0
  237. package/src/_modules/account-security/_models/account-challenge-record.interface.ts +13 -0
  238. package/src/_modules/account-security/_models/account-challenge-result.interface.ts +8 -0
  239. package/src/_modules/account-security/_models/account-challenge-settings.interface.ts +9 -0
  240. package/src/_modules/account-security/_models/account-password-policy.interface.ts +11 -0
  241. package/src/_modules/account-security/_models/account-password-verification.interface.ts +5 -0
  242. package/src/_modules/account-security/_models/account-refresh-token-record.interface.ts +13 -0
  243. package/src/_modules/account-security/_models/account-session-adapter.interface.ts +19 -0
  244. package/src/_modules/account-security/_models/account-session-claims.interface.ts +7 -0
  245. package/src/_modules/account-security/_models/account-session-context.interface.ts +6 -0
  246. package/src/_modules/account-security/_models/account-session-create.interface.ts +8 -0
  247. package/src/_modules/account-security/_models/account-session-record.interface.ts +21 -0
  248. package/src/_modules/account-security/_models/account-session-rotate.interface.ts +11 -0
  249. package/src/_modules/account-security/_models/account-session-settings.interface.ts +11 -0
  250. package/src/_modules/account-security/_models/account-session-token-pair.interface.ts +8 -0
  251. package/src/_modules/account-security/_models/account-session-view.interface.ts +13 -0
  252. package/src/_modules/account-security/account-challenge.control-service.spec.ts +195 -0
  253. package/src/_modules/account-security/account-challenge.control-service.ts +164 -0
  254. package/src/_modules/account-security/account-password.control-service.spec.ts +96 -0
  255. package/src/_modules/account-security/account-password.control-service.ts +288 -0
  256. package/src/_modules/account-security/account-session.control-service.spec.ts +294 -0
  257. package/src/_modules/account-security/account-session.control-service.ts +376 -0
  258. package/src/_modules/account-security/index.ts +31 -0
  259. package/src/_modules/privacy-lifecycle/_enums/privacy-consent-decision.type-enum.ts +5 -0
  260. package/src/_modules/privacy-lifecycle/_enums/privacy-contributor-action.type-enum.ts +10 -0
  261. package/src/_modules/privacy-lifecycle/_enums/privacy-contributor-outcome.type-enum.ts +7 -0
  262. package/src/_modules/privacy-lifecycle/_enums/privacy-request-state.type-enum.ts +15 -0
  263. package/src/_modules/privacy-lifecycle/_enums/privacy-request.type-enum.ts +9 -0
  264. package/src/_modules/privacy-lifecycle/_models/privacy-consent-input.interface.ts +12 -0
  265. package/src/_modules/privacy-lifecycle/_models/privacy-consent-persistence-result.interface.ts +7 -0
  266. package/src/_modules/privacy-lifecycle/_models/privacy-consent-record.interface.ts +15 -0
  267. package/src/_modules/privacy-lifecycle/_models/privacy-consent-result.interface.ts +7 -0
  268. package/src/_modules/privacy-lifecycle/_models/privacy-consent-view.interface.ts +13 -0
  269. package/src/_modules/privacy-lifecycle/_models/privacy-contributor-context.interface.ts +9 -0
  270. package/src/_modules/privacy-lifecycle/_models/privacy-contributor-outcome.interface.ts +14 -0
  271. package/src/_modules/privacy-lifecycle/_models/privacy-contributor-plan.interface.ts +8 -0
  272. package/src/_modules/privacy-lifecycle/_models/privacy-contributor.interface.ts +17 -0
  273. package/src/_modules/privacy-lifecycle/_models/privacy-ledger-adapter.interface.ts +21 -0
  274. package/src/_modules/privacy-lifecycle/_models/privacy-ledger-result.interface.ts +9 -0
  275. package/src/_modules/privacy-lifecycle/_models/privacy-ledger-settings.interface.ts +7 -0
  276. package/src/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-input.interface.ts +10 -0
  277. package/src/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-persistence-result.interface.ts +7 -0
  278. package/src/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-result.interface.ts +7 -0
  279. package/src/_modules/privacy-lifecycle/_models/privacy-legal-acceptance-view.interface.ts +11 -0
  280. package/src/_modules/privacy-lifecycle/_models/privacy-legal-acceptance.interface.ts +13 -0
  281. package/src/_modules/privacy-lifecycle/_models/privacy-lifecycle-adapter.interface.ts +19 -0
  282. package/src/_modules/privacy-lifecycle/_models/privacy-lifecycle-settings.interface.ts +9 -0
  283. package/src/_modules/privacy-lifecycle/_models/privacy-request-create-result.interface.ts +7 -0
  284. package/src/_modules/privacy-lifecycle/_models/privacy-request-open.interface.ts +10 -0
  285. package/src/_modules/privacy-lifecycle/_models/privacy-request-persistence-create-result.interface.ts +7 -0
  286. package/src/_modules/privacy-lifecycle/_models/privacy-request-record.interface.ts +21 -0
  287. package/src/_modules/privacy-lifecycle/_models/privacy-request-result.interface.ts +8 -0
  288. package/src/_modules/privacy-lifecycle/_models/privacy-request-view.interface.ts +20 -0
  289. package/src/_modules/privacy-lifecycle/index.ts +40 -0
  290. package/src/_modules/privacy-lifecycle/privacy-ledger.control-service.spec.ts +206 -0
  291. package/src/_modules/privacy-lifecycle/privacy-ledger.control-service.ts +253 -0
  292. package/src/_modules/privacy-lifecycle/privacy-lifecycle.control-service.spec.ts +353 -0
  293. package/src/_modules/privacy-lifecycle/privacy-lifecycle.control-service.ts +586 -0
  294. package/SKILLS.md +0 -75
@@ -0,0 +1,288 @@
1
+ import {
2
+ createHash,
3
+ randomBytes,
4
+ scrypt,
5
+ timingSafeEqual
6
+ } from 'crypto';
7
+
8
+ import { DyFM_Error } from '@futdevpro/fsm-dynamo';
9
+
10
+ import { DyNTS_global_settings } from '../../_collections/global-settings.const';
11
+ import {
12
+ DyNTS_AccountPasswordPolicy_Interface
13
+ } from './_models/account-password-policy.interface';
14
+ import {
15
+ DyNTS_AccountPasswordVerification_Interface
16
+ } from './_models/account-password-verification.interface';
17
+
18
+ const PASSWORD_HASH_VERSION: string = 'dyn-scrypt-v1';
19
+ const DEFAULT_POLICY: DyNTS_AccountPasswordPolicy_Interface = {
20
+ minLength: 12,
21
+ maxLength: 128,
22
+ scryptCost: 16_384,
23
+ scryptBlockSize: 8,
24
+ scryptParallelization: 1,
25
+ derivedKeyLength: 32,
26
+ saltLength: 16,
27
+ commonPasswordSha256: [],
28
+ };
29
+ const MAX_VERIFY_COST: number = 131_072;
30
+ const MAX_VERIFY_BLOCK_SIZE: number = 8;
31
+ const MAX_VERIFY_PARALLELIZATION: number = 2;
32
+ const MAX_VERIFY_KEY_LENGTH: number = 64;
33
+ const MAX_PASSWORD_LENGTH: number = 1_024;
34
+ const MAX_STORED_HASH_LENGTH: number = 1_024;
35
+
36
+ interface DyNTS_ParsedPasswordHash {
37
+ cost: number;
38
+ blockSize: number;
39
+ parallelization: number;
40
+ salt: Buffer;
41
+ derivedKey: Buffer;
42
+ }
43
+
44
+ interface DyNTS_ScryptDerivationSettings {
45
+ password: string;
46
+ salt: Buffer;
47
+ cost: number;
48
+ blockSize: number;
49
+ parallelization: number;
50
+ keyLength: number;
51
+ }
52
+
53
+ /**
54
+ * Standalone password capability using versioned Node scrypt hashes.
55
+ * Passwords and derived bytes never enter errors, logs or result objects.
56
+ */
57
+ export class DyNTS_AccountPassword_ControlService {
58
+ private readonly policy: DyNTS_AccountPasswordPolicy_Interface;
59
+
60
+ /** Creates a validated immutable policy snapshot. */
61
+ constructor(policy?: Partial<DyNTS_AccountPasswordPolicy_Interface>) {
62
+ this.policy = {
63
+ ...DEFAULT_POLICY,
64
+ ...policy,
65
+ commonPasswordSha256: [
66
+ ...(policy?.commonPasswordSha256 ?? DEFAULT_POLICY.commonPasswordSha256),
67
+ ],
68
+ };
69
+ this.validatePolicy();
70
+ }
71
+
72
+ /** Validates and hashes one password to a self-describing scrypt record. */
73
+ public async hashPassword(password: string): Promise<string> {
74
+ this.validatePassword(password);
75
+
76
+ const salt: Buffer = randomBytes(this.policy.saltLength);
77
+ const derivedKey: Buffer = await this.deriveKey({
78
+ password: password,
79
+ salt: salt,
80
+ cost: this.policy.scryptCost,
81
+ blockSize: this.policy.scryptBlockSize,
82
+ parallelization: this.policy.scryptParallelization,
83
+ keyLength: this.policy.derivedKeyLength,
84
+ });
85
+
86
+ return [
87
+ PASSWORD_HASH_VERSION,
88
+ this.policy.scryptCost,
89
+ this.policy.scryptBlockSize,
90
+ this.policy.scryptParallelization,
91
+ salt.toString('base64url'),
92
+ derivedKey.toString('base64url'),
93
+ ].join('$');
94
+ }
95
+
96
+ /** Performs constant-time verification and reports whether the stored cost should be upgraded. */
97
+ public async verifyPassword(
98
+ password: string,
99
+ storedHash: string
100
+ ): Promise<DyNTS_AccountPasswordVerification_Interface> {
101
+ if (typeof password !== 'string' || Array.from(password).length > this.policy.maxLength) {
102
+ await this.runDummyDerivation('invalid-password-candidate');
103
+
104
+ return { isValid: false, needsRehash: false };
105
+ }
106
+
107
+ const parsed: DyNTS_ParsedPasswordHash | null = this.parseHash(storedHash);
108
+
109
+ if (!parsed) {
110
+ await this.runDummyDerivation(password);
111
+
112
+ return { isValid: false, needsRehash: false };
113
+ }
114
+
115
+ let candidate: Buffer;
116
+
117
+ try {
118
+ candidate = await this.deriveKey({
119
+ password: password,
120
+ salt: parsed.salt,
121
+ cost: parsed.cost,
122
+ blockSize: parsed.blockSize,
123
+ parallelization: parsed.parallelization,
124
+ keyLength: parsed.derivedKey.length,
125
+ });
126
+ } catch {
127
+ await this.runDummyDerivation(password);
128
+
129
+ return { isValid: false, needsRehash: false };
130
+ }
131
+
132
+ const isValid: boolean = candidate.length === parsed.derivedKey.length
133
+ && timingSafeEqual(candidate, parsed.derivedKey);
134
+
135
+ return {
136
+ isValid: isValid,
137
+ needsRehash: isValid && (
138
+ parsed.cost !== this.policy.scryptCost
139
+ || parsed.blockSize !== this.policy.scryptBlockSize
140
+ || parsed.parallelization !== this.policy.scryptParallelization
141
+ || parsed.derivedKey.length !== this.policy.derivedKeyLength
142
+ || parsed.salt.length !== this.policy.saltLength
143
+ ),
144
+ };
145
+ }
146
+
147
+ /** Rejects weak, oversized and configured common passwords without exposing their content. */
148
+ public validatePassword(password: string): void {
149
+ if (typeof password !== 'string') {
150
+ throw this.buildError('PASSWORD_POLICY_LENGTH', 'Password input must be a string.');
151
+ }
152
+
153
+ const passwordLength: number = Array.from(password).length;
154
+
155
+ if (passwordLength < this.policy.minLength || passwordLength > this.policy.maxLength) {
156
+ throw this.buildError(
157
+ 'PASSWORD_POLICY_LENGTH',
158
+ `Password length must be between ${this.policy.minLength} and ${this.policy.maxLength} characters.`
159
+ );
160
+ }
161
+
162
+ const commonDigest: string = createHash('sha256')
163
+ .update(password.normalize('NFKC').toLocaleLowerCase('en-US'))
164
+ .digest('hex');
165
+
166
+ if (this.policy.commonPasswordSha256.includes(commonDigest)) {
167
+ throw this.buildError(
168
+ 'PASSWORD_POLICY_COMMON',
169
+ 'The password matches a configured common-password denylist entry.'
170
+ );
171
+ }
172
+ }
173
+
174
+ private parseHash(storedHash: string): DyNTS_ParsedPasswordHash | null {
175
+ if (typeof storedHash !== 'string' || storedHash.length > MAX_STORED_HASH_LENGTH) {
176
+ return null;
177
+ }
178
+
179
+ const parts: string[] = storedHash.split('$');
180
+
181
+ if (parts.length !== 6 || parts[0] !== PASSWORD_HASH_VERSION) {
182
+ return null;
183
+ }
184
+
185
+ const cost: number = Number(parts[1]);
186
+ const blockSize: number = Number(parts[2]);
187
+ const parallelization: number = Number(parts[3]);
188
+ const salt: Buffer = Buffer.from(parts[4], 'base64url');
189
+ const derivedKey: Buffer = Buffer.from(parts[5], 'base64url');
190
+
191
+ if (
192
+ !this.isPowerOfTwo(cost)
193
+ || cost < 2
194
+ || cost > MAX_VERIFY_COST
195
+ || !Number.isInteger(blockSize)
196
+ || blockSize < 1
197
+ || blockSize > MAX_VERIFY_BLOCK_SIZE
198
+ || !Number.isInteger(parallelization)
199
+ || parallelization < 1
200
+ || parallelization > MAX_VERIFY_PARALLELIZATION
201
+ || !salt.length
202
+ || !derivedKey.length
203
+ || derivedKey.length > MAX_VERIFY_KEY_LENGTH
204
+ ) {
205
+ return null;
206
+ }
207
+
208
+ return {
209
+ cost: cost,
210
+ blockSize: blockSize,
211
+ parallelization: parallelization,
212
+ salt: salt,
213
+ derivedKey: derivedKey,
214
+ };
215
+ }
216
+
217
+ private async runDummyDerivation(password: string): Promise<void> {
218
+ await this.deriveKey({
219
+ password: password,
220
+ salt: Buffer.alloc(this.policy.saltLength),
221
+ cost: this.policy.scryptCost,
222
+ blockSize: this.policy.scryptBlockSize,
223
+ parallelization: this.policy.scryptParallelization,
224
+ keyLength: this.policy.derivedKeyLength,
225
+ });
226
+ }
227
+
228
+ private deriveKey(set: DyNTS_ScryptDerivationSettings): Promise<Buffer> {
229
+ return new Promise<Buffer>((resolve: (value: Buffer) => void, reject: (reason: unknown) => void): void => {
230
+ const maxmem: number = Math.max(32 * 1024 * 1024, 256 * set.cost * set.blockSize);
231
+
232
+ scrypt(set.password, set.salt, set.keyLength, {
233
+ N: set.cost,
234
+ r: set.blockSize,
235
+ p: set.parallelization,
236
+ maxmem: maxmem,
237
+ }, (error: Error | null, derivedKey: Buffer): void => {
238
+ if (error) {
239
+ reject(error);
240
+
241
+ return;
242
+ }
243
+
244
+ resolve(derivedKey);
245
+ });
246
+ });
247
+ }
248
+
249
+ private validatePolicy(): void {
250
+ if (
251
+ !Number.isInteger(this.policy.minLength)
252
+ || !Number.isInteger(this.policy.maxLength)
253
+ || this.policy.minLength < 8
254
+ || this.policy.maxLength < this.policy.minLength
255
+ || this.policy.maxLength > MAX_PASSWORD_LENGTH
256
+ || !this.isPowerOfTwo(this.policy.scryptCost)
257
+ || this.policy.scryptCost > MAX_VERIFY_COST
258
+ || !Number.isInteger(this.policy.scryptBlockSize)
259
+ || this.policy.scryptBlockSize < 1
260
+ || this.policy.scryptBlockSize > MAX_VERIFY_BLOCK_SIZE
261
+ || !Number.isInteger(this.policy.scryptParallelization)
262
+ || this.policy.scryptParallelization < 1
263
+ || this.policy.scryptParallelization > MAX_VERIFY_PARALLELIZATION
264
+ || !Number.isInteger(this.policy.derivedKeyLength)
265
+ || this.policy.derivedKeyLength < 16
266
+ || this.policy.derivedKeyLength > MAX_VERIFY_KEY_LENGTH
267
+ || !Number.isInteger(this.policy.saltLength)
268
+ || this.policy.saltLength < 16
269
+ || this.policy.saltLength > 64
270
+ ) {
271
+ throw this.buildError('PASSWORD_POLICY_INVALID', 'The password policy is outside supported safe bounds.');
272
+ }
273
+ }
274
+
275
+ private isPowerOfTwo(value: number): boolean {
276
+ return Number.isSafeInteger(value) && value > 0 && (value & (value - 1)) === 0;
277
+ }
278
+
279
+ private buildError(errorCode: string, message: string): DyFM_Error {
280
+ return new DyFM_Error({
281
+ status: 400,
282
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|ACCOUNT_SECURITY|${errorCode}`,
283
+ message: message,
284
+ userMessage: 'The credential does not satisfy the account security policy.',
285
+ issuerService: this.constructor.name,
286
+ });
287
+ }
288
+ }
@@ -0,0 +1,294 @@
1
+ import { createHash } from 'crypto';
2
+
3
+ import { DyFM_Error } from '@futdevpro/fsm-dynamo';
4
+
5
+ import { DyNTS_AccountSession_ControlService } from './account-session.control-service';
6
+ import { DyNTS_AccountRefreshTokenState_Type } from './_enums/account-refresh-token-state.type-enum';
7
+ import { DyNTS_AccountSessionState_Type } from './_enums/account-session-state.type-enum';
8
+ import { DyNTS_AccountRefreshTokenRecord_Interface } from './_models/account-refresh-token-record.interface';
9
+ import { DyNTS_AccountSessionAdapter_Interface } from './_models/account-session-adapter.interface';
10
+ import { DyNTS_AccountSessionClaims_Interface } from './_models/account-session-claims.interface';
11
+ import { DyNTS_AccountSessionContext_Interface } from './_models/account-session-context.interface';
12
+ import { DyNTS_AccountSessionCreate_Interface } from './_models/account-session-create.interface';
13
+ import { DyNTS_AccountSessionRecord_Interface } from './_models/account-session-record.interface';
14
+ import { DyNTS_AccountSessionRotate_Interface } from './_models/account-session-rotate.interface';
15
+ import { DyNTS_AccountSessionTokenPair_Interface } from './_models/account-session-token-pair.interface';
16
+ import { DyNTS_AccountSessionView_Interface } from './_models/account-session-view.interface';
17
+
18
+ class DyNTS_TestSessionAdapter implements DyNTS_AccountSessionAdapter_Interface {
19
+ public readonly sessions: Map<string, DyNTS_AccountSessionRecord_Interface> = new Map();
20
+ public readonly refreshTokens: Map<string, DyNTS_AccountRefreshTokenRecord_Interface> = new Map();
21
+
22
+ /** Stores a new session and its first refresh credential atomically in memory. */
23
+ public async createSession(set: DyNTS_AccountSessionCreate_Interface): Promise<void> {
24
+ this.sessions.set(set.session.sessionId, this.cloneSession(set.session));
25
+ this.refreshTokens.set(set.refreshToken.tokenHash, { ...set.refreshToken });
26
+ }
27
+
28
+ /** Finds a test session by its stable identifier. */
29
+ public async findSessionById(sessionId: string): Promise<DyNTS_AccountSessionRecord_Interface | null> {
30
+ const session: DyNTS_AccountSessionRecord_Interface | undefined = this.sessions.get(sessionId);
31
+
32
+ return session ? this.cloneSession(session) : null;
33
+ }
34
+
35
+ /** Finds a test session by its hashed access credential. */
36
+ public async findSessionByAccessTokenHash(
37
+ accessTokenHash: string
38
+ ): Promise<DyNTS_AccountSessionRecord_Interface | null> {
39
+ const session: DyNTS_AccountSessionRecord_Interface | undefined = Array.from(this.sessions.values())
40
+ .find((item: DyNTS_AccountSessionRecord_Interface): boolean => item.accessTokenHash === accessTokenHash);
41
+
42
+ return session ? this.cloneSession(session) : null;
43
+ }
44
+
45
+ /** Finds a one-time refresh ledger record by credential hash. */
46
+ public async findRefreshTokenByHash(
47
+ tokenHash: string
48
+ ): Promise<DyNTS_AccountRefreshTokenRecord_Interface | null> {
49
+ const token: DyNTS_AccountRefreshTokenRecord_Interface | undefined = this.refreshTokens.get(tokenHash);
50
+
51
+ return token ? { ...token } : null;
52
+ }
53
+
54
+ /** Performs an in-memory compare-and-set rotation without yielding between checks and writes. */
55
+ public async rotateSession(set: DyNTS_AccountSessionRotate_Interface): Promise<boolean> {
56
+ const session: DyNTS_AccountSessionRecord_Interface | undefined =
57
+ this.sessions.get(set.replacementSession.sessionId);
58
+ const currentToken: DyNTS_AccountRefreshTokenRecord_Interface | undefined =
59
+ this.refreshTokens.get(set.currentRefreshTokenHash);
60
+
61
+ if (
62
+ session?.revision !== set.expectedSessionRevision
63
+ || currentToken?.state !== DyNTS_AccountRefreshTokenState_Type.active
64
+ ) {
65
+ return false;
66
+ }
67
+
68
+ currentToken.state = DyNTS_AccountRefreshTokenState_Type.used;
69
+ currentToken.usedAt = set.usedAt;
70
+ this.sessions.set(set.replacementSession.sessionId, this.cloneSession(set.replacementSession));
71
+ this.refreshTokens.set(set.replacementRefreshToken.tokenHash, { ...set.replacementRefreshToken });
72
+
73
+ return true;
74
+ }
75
+
76
+ /** Revokes the family belonging to one test session. */
77
+ public async revokeSession(sessionId: string, revokedAt: string, reasonCode: string): Promise<void> {
78
+ const session: DyNTS_AccountSessionRecord_Interface | undefined = this.sessions.get(sessionId);
79
+
80
+ if (session) {
81
+ this.revokeMatching(session.familyId, revokedAt, reasonCode);
82
+ }
83
+ }
84
+
85
+ /** Revokes one complete test token family. */
86
+ public async revokeFamily(familyId: string, revokedAt: string, reasonCode: string): Promise<void> {
87
+ this.revokeMatching(familyId, revokedAt, reasonCode);
88
+ }
89
+
90
+ /** Revokes every test session belonging to one subject. */
91
+ public async revokeSubject(subjectId: string, revokedAt: string, reasonCode: string): Promise<void> {
92
+ Array.from(this.sessions.values())
93
+ .filter((session: DyNTS_AccountSessionRecord_Interface): boolean => session.subjectId === subjectId)
94
+ .forEach((session: DyNTS_AccountSessionRecord_Interface): void => {
95
+ this.revokeMatching(session.familyId, revokedAt, reasonCode);
96
+ });
97
+ }
98
+
99
+ /** Lists cloned test sessions belonging to one subject. */
100
+ public async listSubjectSessions(subjectId: string): Promise<DyNTS_AccountSessionRecord_Interface[]> {
101
+ return Array.from(this.sessions.values())
102
+ .filter((session: DyNTS_AccountSessionRecord_Interface): boolean => session.subjectId === subjectId)
103
+ .map((session: DyNTS_AccountSessionRecord_Interface): DyNTS_AccountSessionRecord_Interface => (
104
+ this.cloneSession(session)
105
+ ));
106
+ }
107
+
108
+ private revokeMatching(familyId: string, revokedAt: string, reasonCode: string): void {
109
+ Array.from(this.sessions.values())
110
+ .filter((session: DyNTS_AccountSessionRecord_Interface): boolean => session.familyId === familyId)
111
+ .forEach((session: DyNTS_AccountSessionRecord_Interface): void => {
112
+ session.state = DyNTS_AccountSessionState_Type.revoked;
113
+ session.revokedAt = revokedAt;
114
+ session.revocationReason = reasonCode;
115
+ });
116
+ Array.from(this.refreshTokens.values())
117
+ .filter((token: DyNTS_AccountRefreshTokenRecord_Interface): boolean => token.familyId === familyId)
118
+ .forEach((token: DyNTS_AccountRefreshTokenRecord_Interface): void => {
119
+ token.state = DyNTS_AccountRefreshTokenState_Type.revoked;
120
+ });
121
+ }
122
+
123
+ private cloneSession(
124
+ session: DyNTS_AccountSessionRecord_Interface
125
+ ): DyNTS_AccountSessionRecord_Interface {
126
+ return { ...session, context: { ...session.context } };
127
+ }
128
+ }
129
+
130
+ describe('| DyNTS_AccountSession_ControlService', (): void => {
131
+ const context: DyNTS_AccountSessionContext_Interface = {
132
+ deviceName: 'Test browser',
133
+ ipAddressHash: 'ip-hash',
134
+ userAgentHash: 'agent-hash',
135
+ };
136
+ let now: Date;
137
+ let adapter: DyNTS_TestSessionAdapter;
138
+ let service: DyNTS_AccountSession_ControlService;
139
+
140
+ beforeEach((): void => {
141
+ now = new Date('2026-08-18T10:00:00.000Z');
142
+ adapter = new DyNTS_TestSessionAdapter();
143
+ service = new DyNTS_AccountSession_ControlService({
144
+ adapter: adapter,
145
+ accessTtlMs: 60_000,
146
+ refreshTtlMs: 600_000,
147
+ clock: (): Date => new Date(now),
148
+ });
149
+ });
150
+
151
+ it('| persists only token hashes and authenticates an active access credential', async (): Promise<void> => {
152
+ const pair: DyNTS_AccountSessionTokenPair_Interface = await service.issueSession('subject-1', 3, context);
153
+ const stored: DyNTS_AccountSessionRecord_Interface | undefined = adapter.sessions.get(pair.sessionId);
154
+ const claims: DyNTS_AccountSessionClaims_Interface = await service.authenticateAccessToken(pair.accessToken);
155
+
156
+ expect(stored?.accessTokenHash).toBe(hashToken(pair.accessToken));
157
+ expect(JSON.stringify(Array.from(adapter.sessions.values()))).not.toContain(pair.accessToken);
158
+ expect(JSON.stringify(Array.from(adapter.refreshTokens.values()))).not.toContain(pair.refreshToken);
159
+ expect(claims).toEqual({
160
+ subjectId: 'subject-1',
161
+ sessionId: pair.sessionId,
162
+ familyId: stored?.familyId,
163
+ authorizationVersion: 3,
164
+ });
165
+ });
166
+
167
+ it('| rejects an expired access credential with a content-free error', async (): Promise<void> => {
168
+ const pair: DyNTS_AccountSessionTokenPair_Interface = await service.issueSession('subject-1', 1, context);
169
+
170
+ now = new Date('2026-08-18T10:01:00.001Z');
171
+ const error: DyFM_Error = await captureError(service.authenticateAccessToken(pair.accessToken));
172
+
173
+ expect(error._errorCode).toContain('ACCESS_TOKEN_INVALID');
174
+ expect(error._message).not.toContain(pair.accessToken);
175
+ });
176
+
177
+ it('| rejects a corrupt access expiry instead of treating it as unbounded', async (): Promise<void> => {
178
+ const pair: DyNTS_AccountSessionTokenPair_Interface = await service.issueSession('subject-1', 1, context);
179
+ const stored: DyNTS_AccountSessionRecord_Interface | undefined = adapter.sessions.get(pair.sessionId);
180
+
181
+ if (stored) {
182
+ stored.accessExpiresAt = 'not-a-date';
183
+ }
184
+
185
+ await expectAsync(service.authenticateAccessToken(pair.accessToken)).toBeRejectedWithError(DyFM_Error);
186
+ });
187
+
188
+ it('| rotates a refresh credential once without extending its absolute expiry', async (): Promise<void> => {
189
+ const first: DyNTS_AccountSessionTokenPair_Interface = await service.issueSession('subject-1', 1, context);
190
+
191
+ now = new Date('2026-08-18T10:00:30.000Z');
192
+ const second: DyNTS_AccountSessionTokenPair_Interface = await service.rotateRefreshToken(
193
+ first.refreshToken,
194
+ context
195
+ );
196
+ const oldToken: DyNTS_AccountRefreshTokenRecord_Interface | undefined =
197
+ adapter.refreshTokens.get(hashToken(first.refreshToken));
198
+
199
+ expect(second.refreshToken).not.toBe(first.refreshToken);
200
+ expect(second.refreshExpiresAt).toBe(first.refreshExpiresAt);
201
+ expect(oldToken?.state).toBe(DyNTS_AccountRefreshTokenState_Type.used);
202
+ expect((await service.authenticateAccessToken(second.accessToken)).subjectId).toBe('subject-1');
203
+ });
204
+
205
+ it('| treats refresh replay as compromise and revokes the complete family', async (): Promise<void> => {
206
+ const first: DyNTS_AccountSessionTokenPair_Interface = await service.issueSession('subject-1', 1, context);
207
+ const second: DyNTS_AccountSessionTokenPair_Interface = await service.rotateRefreshToken(
208
+ first.refreshToken,
209
+ context
210
+ );
211
+ const replayError: DyFM_Error = await captureError(service.rotateRefreshToken(first.refreshToken, context));
212
+ const accessError: DyFM_Error = await captureError(service.authenticateAccessToken(second.accessToken));
213
+
214
+ expect(replayError._errorCode).toContain('REFRESH_TOKEN_REUSE');
215
+ expect(accessError._errorCode).toContain('ACCESS_TOKEN_INVALID');
216
+ });
217
+
218
+ it('| permits one concurrent rotation winner then revokes the raced family', async (): Promise<void> => {
219
+ const first: DyNTS_AccountSessionTokenPair_Interface = await service.issueSession('subject-1', 1, context);
220
+ const results: PromiseSettledResult<DyNTS_AccountSessionTokenPair_Interface>[] = await Promise.allSettled([
221
+ service.rotateRefreshToken(first.refreshToken, context),
222
+ service.rotateRefreshToken(first.refreshToken, context),
223
+ ]);
224
+ const winners: PromiseFulfilledResult<DyNTS_AccountSessionTokenPair_Interface>[] = results.filter(
225
+ (result: PromiseSettledResult<DyNTS_AccountSessionTokenPair_Interface>):
226
+ result is PromiseFulfilledResult<DyNTS_AccountSessionTokenPair_Interface> => result.status === 'fulfilled'
227
+ );
228
+
229
+ expect(winners.length).toBe(1);
230
+ expect(results.filter((result: PromiseSettledResult<DyNTS_AccountSessionTokenPair_Interface>): boolean => (
231
+ result.status === 'rejected'
232
+ )).length).toBe(1);
233
+ await expectAsync(service.authenticateAccessToken(winners[0].value.accessToken)).toBeRejectedWithError(
234
+ DyFM_Error
235
+ );
236
+ });
237
+
238
+ it('| revokes one session or every subject session without affecting another subject', async (): Promise<void> => {
239
+ const first: DyNTS_AccountSessionTokenPair_Interface = await service.issueSession('subject-1', 1, context);
240
+ const second: DyNTS_AccountSessionTokenPair_Interface = await service.issueSession('subject-1', 1, context);
241
+ const other: DyNTS_AccountSessionTokenPair_Interface = await service.issueSession('subject-2', 1, context);
242
+
243
+ await service.revokeSession(first.sessionId, 'user-sign-out');
244
+ await expectAsync(service.authenticateAccessToken(first.accessToken)).toBeRejectedWithError(DyFM_Error);
245
+ expect((await service.authenticateAccessToken(second.accessToken)).subjectId).toBe('subject-1');
246
+ await service.revokeAllSubjectSessions('subject-1', 'password-change');
247
+ await expectAsync(service.authenticateAccessToken(second.accessToken)).toBeRejectedWithError(DyFM_Error);
248
+ expect((await service.authenticateAccessToken(other.accessToken)).subjectId).toBe('subject-2');
249
+ });
250
+
251
+ it('| exposes only the user-safe session projection', async (): Promise<void> => {
252
+ await service.issueSession('subject-1', 1, context);
253
+ const sessions: DyNTS_AccountSessionView_Interface[] = await service.listSubjectSessions('subject-1');
254
+ const serialized: string = JSON.stringify(sessions);
255
+
256
+ expect(sessions.length).toBe(1);
257
+ expect(serialized).not.toContain('accessTokenHash');
258
+ expect(serialized).not.toContain('familyId');
259
+ expect(serialized).not.toContain('subjectId');
260
+ expect(sessions[0].context).toEqual(context);
261
+ });
262
+
263
+ it('| rejects unsafe runtime settings before issuing credentials', (): void => {
264
+ expect((): DyNTS_AccountSession_ControlService => new DyNTS_AccountSession_ControlService({
265
+ adapter: adapter,
266
+ tokenBytes: 16,
267
+ })).toThrowError(DyFM_Error);
268
+ });
269
+
270
+ it('| rejects malformed authorization versions and oversized context before persistence', async (): Promise<void> => {
271
+ await expectAsync(service.issueSession('subject-1', -1, context)).toBeRejectedWithError(DyFM_Error);
272
+ await expectAsync(service.issueSession('subject-1', 1, {
273
+ ...context,
274
+ deviceName: 'x'.repeat(161),
275
+ })).toBeRejectedWithError(DyFM_Error);
276
+ expect(adapter.sessions.size).toBe(0);
277
+ });
278
+ });
279
+
280
+ function hashToken(token: string): string {
281
+ return createHash('sha256').update(token).digest('hex');
282
+ }
283
+
284
+ async function captureError(promise: Promise<unknown>): Promise<DyFM_Error> {
285
+ try {
286
+ await promise;
287
+ } catch (error: unknown) {
288
+ if (error instanceof DyFM_Error) {
289
+ return error;
290
+ }
291
+ }
292
+
293
+ throw new Error('Expected a DyFM_Error rejection.');
294
+ }