@portaidentity/server 1.7.0

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 (742) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +33 -0
  4. package/dist/applications/cache.d.ts +64 -0
  5. package/dist/applications/cache.d.ts.map +1 -0
  6. package/dist/applications/cache.js +161 -0
  7. package/dist/applications/cache.js.map +1 -0
  8. package/dist/applications/errors.d.ts +25 -0
  9. package/dist/applications/errors.d.ts.map +1 -0
  10. package/dist/applications/errors.js +31 -0
  11. package/dist/applications/errors.js.map +1 -0
  12. package/dist/applications/index.d.ts +17 -0
  13. package/dist/applications/index.d.ts.map +1 -0
  14. package/dist/applications/index.js +19 -0
  15. package/dist/applications/index.js.map +1 -0
  16. package/dist/applications/repository.d.ts +184 -0
  17. package/dist/applications/repository.d.ts.map +1 -0
  18. package/dist/applications/repository.js +367 -0
  19. package/dist/applications/repository.js.map +1 -0
  20. package/dist/applications/service.d.ts +160 -0
  21. package/dist/applications/service.d.ts.map +1 -0
  22. package/dist/applications/service.js +388 -0
  23. package/dist/applications/service.js.map +1 -0
  24. package/dist/applications/slugs.d.ts +61 -0
  25. package/dist/applications/slugs.d.ts.map +1 -0
  26. package/dist/applications/slugs.js +115 -0
  27. package/dist/applications/slugs.js.map +1 -0
  28. package/dist/applications/types.d.ts +129 -0
  29. package/dist/applications/types.d.ts.map +1 -0
  30. package/dist/applications/types.js +62 -0
  31. package/dist/applications/types.js.map +1 -0
  32. package/dist/auth/csrf.d.ts +71 -0
  33. package/dist/auth/csrf.d.ts.map +1 -0
  34. package/dist/auth/csrf.js +112 -0
  35. package/dist/auth/csrf.js.map +1 -0
  36. package/dist/auth/email-renderer.d.ts +37 -0
  37. package/dist/auth/email-renderer.d.ts.map +1 -0
  38. package/dist/auth/email-renderer.js +116 -0
  39. package/dist/auth/email-renderer.js.map +1 -0
  40. package/dist/auth/email-service.d.ts +154 -0
  41. package/dist/auth/email-service.d.ts.map +1 -0
  42. package/dist/auth/email-service.js +415 -0
  43. package/dist/auth/email-service.js.map +1 -0
  44. package/dist/auth/email-transport.d.ts +65 -0
  45. package/dist/auth/email-transport.d.ts.map +1 -0
  46. package/dist/auth/email-transport.js +82 -0
  47. package/dist/auth/email-transport.js.map +1 -0
  48. package/dist/auth/i18n.d.ts +81 -0
  49. package/dist/auth/i18n.d.ts.map +1 -0
  50. package/dist/auth/i18n.js +395 -0
  51. package/dist/auth/i18n.js.map +1 -0
  52. package/dist/auth/index.d.ts +28 -0
  53. package/dist/auth/index.d.ts.map +1 -0
  54. package/dist/auth/index.js +31 -0
  55. package/dist/auth/index.js.map +1 -0
  56. package/dist/auth/interaction-authority.d.ts +39 -0
  57. package/dist/auth/interaction-authority.d.ts.map +1 -0
  58. package/dist/auth/interaction-authority.js +30 -0
  59. package/dist/auth/interaction-authority.js.map +1 -0
  60. package/dist/auth/magic-link-session.d.ts +81 -0
  61. package/dist/auth/magic-link-session.d.ts.map +1 -0
  62. package/dist/auth/magic-link-session.js +183 -0
  63. package/dist/auth/magic-link-session.js.map +1 -0
  64. package/dist/auth/rate-limiter.d.ts +143 -0
  65. package/dist/auth/rate-limiter.d.ts.map +1 -0
  66. package/dist/auth/rate-limiter.js +220 -0
  67. package/dist/auth/rate-limiter.js.map +1 -0
  68. package/dist/auth/recovery-crypto.d.ts +34 -0
  69. package/dist/auth/recovery-crypto.d.ts.map +1 -0
  70. package/dist/auth/recovery-crypto.js +103 -0
  71. package/dist/auth/recovery-crypto.js.map +1 -0
  72. package/dist/auth/recovery-job-processor.d.ts +24 -0
  73. package/dist/auth/recovery-job-processor.d.ts.map +1 -0
  74. package/dist/auth/recovery-job-processor.js +123 -0
  75. package/dist/auth/recovery-job-processor.js.map +1 -0
  76. package/dist/auth/recovery-job-repository.d.ts +219 -0
  77. package/dist/auth/recovery-job-repository.d.ts.map +1 -0
  78. package/dist/auth/recovery-job-repository.js +340 -0
  79. package/dist/auth/recovery-job-repository.js.map +1 -0
  80. package/dist/auth/recovery-service.d.ts +27 -0
  81. package/dist/auth/recovery-service.d.ts.map +1 -0
  82. package/dist/auth/recovery-service.js +53 -0
  83. package/dist/auth/recovery-service.js.map +1 -0
  84. package/dist/auth/recovery-worker.d.ts +158 -0
  85. package/dist/auth/recovery-worker.d.ts.map +1 -0
  86. package/dist/auth/recovery-worker.js +306 -0
  87. package/dist/auth/recovery-worker.js.map +1 -0
  88. package/dist/auth/template-engine.d.ts +134 -0
  89. package/dist/auth/template-engine.d.ts.map +1 -0
  90. package/dist/auth/template-engine.js +225 -0
  91. package/dist/auth/template-engine.js.map +1 -0
  92. package/dist/auth/token-repository.d.ts +238 -0
  93. package/dist/auth/token-repository.d.ts.map +1 -0
  94. package/dist/auth/token-repository.js +475 -0
  95. package/dist/auth/token-repository.js.map +1 -0
  96. package/dist/auth/tokens.d.ts +45 -0
  97. package/dist/auth/tokens.d.ts.map +1 -0
  98. package/dist/auth/tokens.js +51 -0
  99. package/dist/auth/tokens.js.map +1 -0
  100. package/dist/cli/bootstrap.d.ts +55 -0
  101. package/dist/cli/bootstrap.d.ts.map +1 -0
  102. package/dist/cli/bootstrap.js +90 -0
  103. package/dist/cli/bootstrap.js.map +1 -0
  104. package/dist/cli/commands/health.d.ts +20 -0
  105. package/dist/cli/commands/health.d.ts.map +1 -0
  106. package/dist/cli/commands/health.js +80 -0
  107. package/dist/cli/commands/health.js.map +1 -0
  108. package/dist/cli/commands/init.d.ts +48 -0
  109. package/dist/cli/commands/init.d.ts.map +1 -0
  110. package/dist/cli/commands/init.js +349 -0
  111. package/dist/cli/commands/init.js.map +1 -0
  112. package/dist/cli/commands/migrate.d.ts +19 -0
  113. package/dist/cli/commands/migrate.d.ts.map +1 -0
  114. package/dist/cli/commands/migrate.js +88 -0
  115. package/dist/cli/commands/migrate.js.map +1 -0
  116. package/dist/cli/commands/seed.d.ts +19 -0
  117. package/dist/cli/commands/seed.d.ts.map +1 -0
  118. package/dist/cli/commands/seed.js +71 -0
  119. package/dist/cli/commands/seed.js.map +1 -0
  120. package/dist/cli/commands/user-2fa.d.ts +29 -0
  121. package/dist/cli/commands/user-2fa.d.ts.map +1 -0
  122. package/dist/cli/commands/user-2fa.js +113 -0
  123. package/dist/cli/commands/user-2fa.js.map +1 -0
  124. package/dist/cli/error-handler.d.ts +31 -0
  125. package/dist/cli/error-handler.d.ts.map +1 -0
  126. package/dist/cli/error-handler.js +65 -0
  127. package/dist/cli/error-handler.js.map +1 -0
  128. package/dist/cli/index.d.ts +48 -0
  129. package/dist/cli/index.d.ts.map +1 -0
  130. package/dist/cli/index.js +99 -0
  131. package/dist/cli/index.js.map +1 -0
  132. package/dist/cli/output.d.ts +94 -0
  133. package/dist/cli/output.d.ts.map +1 -0
  134. package/dist/cli/output.js +124 -0
  135. package/dist/cli/output.js.map +1 -0
  136. package/dist/cli/prompt.d.ts +48 -0
  137. package/dist/cli/prompt.d.ts.map +1 -0
  138. package/dist/cli/prompt.js +96 -0
  139. package/dist/cli/prompt.js.map +1 -0
  140. package/dist/clients/cache.d.ts +63 -0
  141. package/dist/clients/cache.d.ts.map +1 -0
  142. package/dist/clients/cache.js +160 -0
  143. package/dist/clients/cache.js.map +1 -0
  144. package/dist/clients/crypto.d.ts +69 -0
  145. package/dist/clients/crypto.d.ts.map +1 -0
  146. package/dist/clients/crypto.js +83 -0
  147. package/dist/clients/crypto.js.map +1 -0
  148. package/dist/clients/errors.d.ts +27 -0
  149. package/dist/clients/errors.d.ts.map +1 -0
  150. package/dist/clients/errors.js +33 -0
  151. package/dist/clients/errors.js.map +1 -0
  152. package/dist/clients/index.d.ts +19 -0
  153. package/dist/clients/index.d.ts.map +1 -0
  154. package/dist/clients/index.js +23 -0
  155. package/dist/clients/index.js.map +1 -0
  156. package/dist/clients/repository.d.ts +161 -0
  157. package/dist/clients/repository.d.ts.map +1 -0
  158. package/dist/clients/repository.js +318 -0
  159. package/dist/clients/repository.js.map +1 -0
  160. package/dist/clients/resolve-login-methods.d.ts +74 -0
  161. package/dist/clients/resolve-login-methods.d.ts.map +1 -0
  162. package/dist/clients/resolve-login-methods.js +68 -0
  163. package/dist/clients/resolve-login-methods.js.map +1 -0
  164. package/dist/clients/secret-repository.d.ts +109 -0
  165. package/dist/clients/secret-repository.d.ts.map +1 -0
  166. package/dist/clients/secret-repository.js +166 -0
  167. package/dist/clients/secret-repository.js.map +1 -0
  168. package/dist/clients/secret-service.d.ts +87 -0
  169. package/dist/clients/secret-service.d.ts.map +1 -0
  170. package/dist/clients/secret-service.js +204 -0
  171. package/dist/clients/secret-service.js.map +1 -0
  172. package/dist/clients/service.d.ts +158 -0
  173. package/dist/clients/service.d.ts.map +1 -0
  174. package/dist/clients/service.js +523 -0
  175. package/dist/clients/service.js.map +1 -0
  176. package/dist/clients/types.d.ts +214 -0
  177. package/dist/clients/types.d.ts.map +1 -0
  178. package/dist/clients/types.js +77 -0
  179. package/dist/clients/types.js.map +1 -0
  180. package/dist/clients/validators.d.ts +87 -0
  181. package/dist/clients/validators.d.ts.map +1 -0
  182. package/dist/clients/validators.js +164 -0
  183. package/dist/clients/validators.js.map +1 -0
  184. package/dist/config/index.d.ts +23 -0
  185. package/dist/config/index.d.ts.map +1 -0
  186. package/dist/config/index.js +52 -0
  187. package/dist/config/index.js.map +1 -0
  188. package/dist/config/schema.d.ts +35 -0
  189. package/dist/config/schema.d.ts.map +1 -0
  190. package/dist/config/schema.js +167 -0
  191. package/dist/config/schema.js.map +1 -0
  192. package/dist/custom-claims/cache.d.ts +48 -0
  193. package/dist/custom-claims/cache.d.ts.map +1 -0
  194. package/dist/custom-claims/cache.js +122 -0
  195. package/dist/custom-claims/cache.js.map +1 -0
  196. package/dist/custom-claims/errors.d.ts +25 -0
  197. package/dist/custom-claims/errors.d.ts.map +1 -0
  198. package/dist/custom-claims/errors.js +31 -0
  199. package/dist/custom-claims/errors.js.map +1 -0
  200. package/dist/custom-claims/index.d.ts +14 -0
  201. package/dist/custom-claims/index.d.ts.map +1 -0
  202. package/dist/custom-claims/index.js +17 -0
  203. package/dist/custom-claims/index.js.map +1 -0
  204. package/dist/custom-claims/repository.d.ts +147 -0
  205. package/dist/custom-claims/repository.d.ts.map +1 -0
  206. package/dist/custom-claims/repository.js +338 -0
  207. package/dist/custom-claims/repository.js.map +1 -0
  208. package/dist/custom-claims/service.d.ts +129 -0
  209. package/dist/custom-claims/service.d.ts.map +1 -0
  210. package/dist/custom-claims/service.js +296 -0
  211. package/dist/custom-claims/service.js.map +1 -0
  212. package/dist/custom-claims/types.d.ts +132 -0
  213. package/dist/custom-claims/types.d.ts.map +1 -0
  214. package/dist/custom-claims/types.js +61 -0
  215. package/dist/custom-claims/types.js.map +1 -0
  216. package/dist/custom-claims/validators.d.ts +61 -0
  217. package/dist/custom-claims/validators.d.ts.map +1 -0
  218. package/dist/custom-claims/validators.js +136 -0
  219. package/dist/custom-claims/validators.js.map +1 -0
  220. package/dist/index.d.ts +14 -0
  221. package/dist/index.d.ts.map +1 -0
  222. package/dist/index.js +91 -0
  223. package/dist/index.js.map +1 -0
  224. package/dist/lib/admin-permissions.d.ts +147 -0
  225. package/dist/lib/admin-permissions.d.ts.map +1 -0
  226. package/dist/lib/admin-permissions.js +261 -0
  227. package/dist/lib/admin-permissions.js.map +1 -0
  228. package/dist/lib/audit-log.d.ts +45 -0
  229. package/dist/lib/audit-log.d.ts.map +1 -0
  230. package/dist/lib/audit-log.js +79 -0
  231. package/dist/lib/audit-log.js.map +1 -0
  232. package/dist/lib/branding-assets.d.ts +47 -0
  233. package/dist/lib/branding-assets.d.ts.map +1 -0
  234. package/dist/lib/branding-assets.js +93 -0
  235. package/dist/lib/branding-assets.js.map +1 -0
  236. package/dist/lib/bulk-operations.d.ts +54 -0
  237. package/dist/lib/bulk-operations.d.ts.map +1 -0
  238. package/dist/lib/bulk-operations.js +201 -0
  239. package/dist/lib/bulk-operations.js.map +1 -0
  240. package/dist/lib/cursor.d.ts +110 -0
  241. package/dist/lib/cursor.d.ts.map +1 -0
  242. package/dist/lib/cursor.js +115 -0
  243. package/dist/lib/cursor.js.map +1 -0
  244. package/dist/lib/data-export.d.ts +67 -0
  245. package/dist/lib/data-export.d.ts.map +1 -0
  246. package/dist/lib/data-export.js +272 -0
  247. package/dist/lib/data-export.js.map +1 -0
  248. package/dist/lib/data-import-plan.d.ts +50 -0
  249. package/dist/lib/data-import-plan.d.ts.map +1 -0
  250. package/dist/lib/data-import-plan.js +235 -0
  251. package/dist/lib/data-import-plan.js.map +1 -0
  252. package/dist/lib/data-import.d.ts +276 -0
  253. package/dist/lib/data-import.d.ts.map +1 -0
  254. package/dist/lib/data-import.js +1694 -0
  255. package/dist/lib/data-import.js.map +1 -0
  256. package/dist/lib/database.d.ts +22 -0
  257. package/dist/lib/database.d.ts.map +1 -0
  258. package/dist/lib/database.js +103 -0
  259. package/dist/lib/database.js.map +1 -0
  260. package/dist/lib/entity-history.d.ts +52 -0
  261. package/dist/lib/entity-history.d.ts.map +1 -0
  262. package/dist/lib/entity-history.js +121 -0
  263. package/dist/lib/entity-history.js.map +1 -0
  264. package/dist/lib/etag.d.ts +92 -0
  265. package/dist/lib/etag.d.ts.map +1 -0
  266. package/dist/lib/etag.js +155 -0
  267. package/dist/lib/etag.js.map +1 -0
  268. package/dist/lib/image-validator.d.ts +44 -0
  269. package/dist/lib/image-validator.d.ts.map +1 -0
  270. package/dist/lib/image-validator.js +204 -0
  271. package/dist/lib/image-validator.js.map +1 -0
  272. package/dist/lib/logger.d.ts +15 -0
  273. package/dist/lib/logger.d.ts.map +1 -0
  274. package/dist/lib/logger.js +84 -0
  275. package/dist/lib/logger.js.map +1 -0
  276. package/dist/lib/migrator.d.ts +14 -0
  277. package/dist/lib/migrator.d.ts.map +1 -0
  278. package/dist/lib/migrator.js +34 -0
  279. package/dist/lib/migrator.js.map +1 -0
  280. package/dist/lib/redis.d.ts +5 -0
  281. package/dist/lib/redis.d.ts.map +1 -0
  282. package/dist/lib/redis.js +29 -0
  283. package/dist/lib/redis.js.map +1 -0
  284. package/dist/lib/runtime-paths.d.ts +38 -0
  285. package/dist/lib/runtime-paths.d.ts.map +1 -0
  286. package/dist/lib/runtime-paths.js +50 -0
  287. package/dist/lib/runtime-paths.js.map +1 -0
  288. package/dist/lib/session-tracking.d.ts +81 -0
  289. package/dist/lib/session-tracking.d.ts.map +1 -0
  290. package/dist/lib/session-tracking.js +145 -0
  291. package/dist/lib/session-tracking.js.map +1 -0
  292. package/dist/lib/signing-key-crypto.d.ts +53 -0
  293. package/dist/lib/signing-key-crypto.d.ts.map +1 -0
  294. package/dist/lib/signing-key-crypto.js +108 -0
  295. package/dist/lib/signing-key-crypto.js.map +1 -0
  296. package/dist/lib/signing-keys.d.ts +141 -0
  297. package/dist/lib/signing-keys.d.ts.map +1 -0
  298. package/dist/lib/signing-keys.js +206 -0
  299. package/dist/lib/signing-keys.js.map +1 -0
  300. package/dist/lib/stats.d.ts +83 -0
  301. package/dist/lib/stats.d.ts.map +1 -0
  302. package/dist/lib/stats.js +187 -0
  303. package/dist/lib/stats.js.map +1 -0
  304. package/dist/lib/super-admin-protection.d.ts +69 -0
  305. package/dist/lib/super-admin-protection.d.ts.map +1 -0
  306. package/dist/lib/super-admin-protection.js +102 -0
  307. package/dist/lib/super-admin-protection.js.map +1 -0
  308. package/dist/lib/system-config.d.ts +79 -0
  309. package/dist/lib/system-config.d.ts.map +1 -0
  310. package/dist/lib/system-config.js +159 -0
  311. package/dist/lib/system-config.js.map +1 -0
  312. package/dist/middleware/admin-auth.d.ts +100 -0
  313. package/dist/middleware/admin-auth.d.ts.map +1 -0
  314. package/dist/middleware/admin-auth.js +241 -0
  315. package/dist/middleware/admin-auth.js.map +1 -0
  316. package/dist/middleware/admin-cors.d.ts +41 -0
  317. package/dist/middleware/admin-cors.d.ts.map +1 -0
  318. package/dist/middleware/admin-cors.js +83 -0
  319. package/dist/middleware/admin-cors.js.map +1 -0
  320. package/dist/middleware/admin-mutation-audit.d.ts +9 -0
  321. package/dist/middleware/admin-mutation-audit.d.ts.map +1 -0
  322. package/dist/middleware/admin-mutation-audit.js +76 -0
  323. package/dist/middleware/admin-mutation-audit.js.map +1 -0
  324. package/dist/middleware/admin-rate-limiter.d.ts +56 -0
  325. package/dist/middleware/admin-rate-limiter.d.ts.map +1 -0
  326. package/dist/middleware/admin-rate-limiter.js +97 -0
  327. package/dist/middleware/admin-rate-limiter.js.map +1 -0
  328. package/dist/middleware/client-secret-hash.d.ts +34 -0
  329. package/dist/middleware/client-secret-hash.d.ts.map +1 -0
  330. package/dist/middleware/client-secret-hash.js +74 -0
  331. package/dist/middleware/client-secret-hash.js.map +1 -0
  332. package/dist/middleware/error-handler.d.ts +3 -0
  333. package/dist/middleware/error-handler.d.ts.map +1 -0
  334. package/dist/middleware/error-handler.js +93 -0
  335. package/dist/middleware/error-handler.js.map +1 -0
  336. package/dist/middleware/health.d.ts +3 -0
  337. package/dist/middleware/health.d.ts.map +1 -0
  338. package/dist/middleware/health.js +35 -0
  339. package/dist/middleware/health.js.map +1 -0
  340. package/dist/middleware/metrics.d.ts +40 -0
  341. package/dist/middleware/metrics.d.ts.map +1 -0
  342. package/dist/middleware/metrics.js +75 -0
  343. package/dist/middleware/metrics.js.map +1 -0
  344. package/dist/middleware/oidc-client-tenant.d.ts +37 -0
  345. package/dist/middleware/oidc-client-tenant.d.ts.map +1 -0
  346. package/dist/middleware/oidc-client-tenant.js +156 -0
  347. package/dist/middleware/oidc-client-tenant.js.map +1 -0
  348. package/dist/middleware/oidc-cors.d.ts +33 -0
  349. package/dist/middleware/oidc-cors.d.ts.map +1 -0
  350. package/dist/middleware/oidc-cors.js +68 -0
  351. package/dist/middleware/oidc-cors.js.map +1 -0
  352. package/dist/middleware/oidc-preflight-cors.d.ts +54 -0
  353. package/dist/middleware/oidc-preflight-cors.d.ts.map +1 -0
  354. package/dist/middleware/oidc-preflight-cors.js +267 -0
  355. package/dist/middleware/oidc-preflight-cors.js.map +1 -0
  356. package/dist/middleware/prompt-login-reset.d.ts +103 -0
  357. package/dist/middleware/prompt-login-reset.d.ts.map +1 -0
  358. package/dist/middleware/prompt-login-reset.js +204 -0
  359. package/dist/middleware/prompt-login-reset.js.map +1 -0
  360. package/dist/middleware/ready.d.ts +23 -0
  361. package/dist/middleware/ready.d.ts.map +1 -0
  362. package/dist/middleware/ready.js +60 -0
  363. package/dist/middleware/ready.js.map +1 -0
  364. package/dist/middleware/request-logger.d.ts +27 -0
  365. package/dist/middleware/request-logger.d.ts.map +1 -0
  366. package/dist/middleware/request-logger.js +46 -0
  367. package/dist/middleware/request-logger.js.map +1 -0
  368. package/dist/middleware/require-permission.d.ts +28 -0
  369. package/dist/middleware/require-permission.d.ts.map +1 -0
  370. package/dist/middleware/require-permission.js +63 -0
  371. package/dist/middleware/require-permission.js.map +1 -0
  372. package/dist/middleware/require-user-organization.d.ts +18 -0
  373. package/dist/middleware/require-user-organization.d.ts.map +1 -0
  374. package/dist/middleware/require-user-organization.js +53 -0
  375. package/dist/middleware/require-user-organization.js.map +1 -0
  376. package/dist/middleware/root-page.d.ts +62 -0
  377. package/dist/middleware/root-page.d.ts.map +1 -0
  378. package/dist/middleware/root-page.js +134 -0
  379. package/dist/middleware/root-page.js.map +1 -0
  380. package/dist/middleware/security-headers.d.ts +98 -0
  381. package/dist/middleware/security-headers.d.ts.map +1 -0
  382. package/dist/middleware/security-headers.js +159 -0
  383. package/dist/middleware/security-headers.js.map +1 -0
  384. package/dist/middleware/tenant-resolver.d.ts +37 -0
  385. package/dist/middleware/tenant-resolver.d.ts.map +1 -0
  386. package/dist/middleware/tenant-resolver.js +71 -0
  387. package/dist/middleware/tenant-resolver.js.map +1 -0
  388. package/dist/middleware/token-rate-limiter.d.ts +89 -0
  389. package/dist/middleware/token-rate-limiter.d.ts.map +1 -0
  390. package/dist/middleware/token-rate-limiter.js +189 -0
  391. package/dist/middleware/token-rate-limiter.js.map +1 -0
  392. package/dist/oidc/account-finder.d.ts +52 -0
  393. package/dist/oidc/account-finder.d.ts.map +1 -0
  394. package/dist/oidc/account-finder.js +134 -0
  395. package/dist/oidc/account-finder.js.map +1 -0
  396. package/dist/oidc/adapter-factory.d.ts +90 -0
  397. package/dist/oidc/adapter-factory.d.ts.map +1 -0
  398. package/dist/oidc/adapter-factory.js +165 -0
  399. package/dist/oidc/adapter-factory.js.map +1 -0
  400. package/dist/oidc/configuration.d.ts +155 -0
  401. package/dist/oidc/configuration.d.ts.map +1 -0
  402. package/dist/oidc/configuration.js +531 -0
  403. package/dist/oidc/configuration.js.map +1 -0
  404. package/dist/oidc/postgres-adapter.d.ts +159 -0
  405. package/dist/oidc/postgres-adapter.d.ts.map +1 -0
  406. package/dist/oidc/postgres-adapter.js +222 -0
  407. package/dist/oidc/postgres-adapter.js.map +1 -0
  408. package/dist/oidc/protocol-security-observer.d.ts +38 -0
  409. package/dist/oidc/protocol-security-observer.d.ts.map +1 -0
  410. package/dist/oidc/protocol-security-observer.js +93 -0
  411. package/dist/oidc/protocol-security-observer.js.map +1 -0
  412. package/dist/oidc/provider.d.ts +37 -0
  413. package/dist/oidc/provider.d.ts.map +1 -0
  414. package/dist/oidc/provider.js +71 -0
  415. package/dist/oidc/provider.js.map +1 -0
  416. package/dist/oidc/redis-adapter.d.ts +140 -0
  417. package/dist/oidc/redis-adapter.d.ts.map +1 -0
  418. package/dist/oidc/redis-adapter.js +344 -0
  419. package/dist/oidc/redis-adapter.js.map +1 -0
  420. package/dist/organizations/cache.d.ts +60 -0
  421. package/dist/organizations/cache.d.ts.map +1 -0
  422. package/dist/organizations/cache.js +157 -0
  423. package/dist/organizations/cache.js.map +1 -0
  424. package/dist/organizations/errors.d.ts +25 -0
  425. package/dist/organizations/errors.d.ts.map +1 -0
  426. package/dist/organizations/errors.js +31 -0
  427. package/dist/organizations/errors.js.map +1 -0
  428. package/dist/organizations/index.d.ts +15 -0
  429. package/dist/organizations/index.d.ts.map +1 -0
  430. package/dist/organizations/index.js +15 -0
  431. package/dist/organizations/index.js.map +1 -0
  432. package/dist/organizations/repository.d.ts +183 -0
  433. package/dist/organizations/repository.d.ts.map +1 -0
  434. package/dist/organizations/repository.js +354 -0
  435. package/dist/organizations/repository.js.map +1 -0
  436. package/dist/organizations/service.d.ts +168 -0
  437. package/dist/organizations/service.d.ts.map +1 -0
  438. package/dist/organizations/service.js +511 -0
  439. package/dist/organizations/service.js.map +1 -0
  440. package/dist/organizations/slugs.d.ts +57 -0
  441. package/dist/organizations/slugs.d.ts.map +1 -0
  442. package/dist/organizations/slugs.js +130 -0
  443. package/dist/organizations/slugs.js.map +1 -0
  444. package/dist/organizations/types.d.ts +146 -0
  445. package/dist/organizations/types.d.ts.map +1 -0
  446. package/dist/organizations/types.js +49 -0
  447. package/dist/organizations/types.js.map +1 -0
  448. package/dist/rbac/cache.d.ts +110 -0
  449. package/dist/rbac/cache.d.ts.map +1 -0
  450. package/dist/rbac/cache.js +271 -0
  451. package/dist/rbac/cache.js.map +1 -0
  452. package/dist/rbac/errors.d.ts +34 -0
  453. package/dist/rbac/errors.d.ts.map +1 -0
  454. package/dist/rbac/errors.js +43 -0
  455. package/dist/rbac/errors.js.map +1 -0
  456. package/dist/rbac/index.d.ts +17 -0
  457. package/dist/rbac/index.d.ts.map +1 -0
  458. package/dist/rbac/index.js +21 -0
  459. package/dist/rbac/index.js.map +1 -0
  460. package/dist/rbac/mapping-repository.d.ts +121 -0
  461. package/dist/rbac/mapping-repository.d.ts.map +1 -0
  462. package/dist/rbac/mapping-repository.js +224 -0
  463. package/dist/rbac/mapping-repository.js.map +1 -0
  464. package/dist/rbac/permission-repository.d.ts +100 -0
  465. package/dist/rbac/permission-repository.d.ts.map +1 -0
  466. package/dist/rbac/permission-repository.js +190 -0
  467. package/dist/rbac/permission-repository.js.map +1 -0
  468. package/dist/rbac/permission-service.d.ts +85 -0
  469. package/dist/rbac/permission-service.d.ts.map +1 -0
  470. package/dist/rbac/permission-service.js +179 -0
  471. package/dist/rbac/permission-service.js.map +1 -0
  472. package/dist/rbac/role-repository.d.ts +104 -0
  473. package/dist/rbac/role-repository.d.ts.map +1 -0
  474. package/dist/rbac/role-repository.js +202 -0
  475. package/dist/rbac/role-repository.js.map +1 -0
  476. package/dist/rbac/role-service.d.ts +111 -0
  477. package/dist/rbac/role-service.d.ts.map +1 -0
  478. package/dist/rbac/role-service.js +256 -0
  479. package/dist/rbac/role-service.js.map +1 -0
  480. package/dist/rbac/slugs.d.ts +116 -0
  481. package/dist/rbac/slugs.d.ts.map +1 -0
  482. package/dist/rbac/slugs.js +178 -0
  483. package/dist/rbac/slugs.js.map +1 -0
  484. package/dist/rbac/types.d.ts +187 -0
  485. package/dist/rbac/types.d.ts.map +1 -0
  486. package/dist/rbac/types.js +75 -0
  487. package/dist/rbac/types.js.map +1 -0
  488. package/dist/rbac/user-role-service.d.ts +101 -0
  489. package/dist/rbac/user-role-service.d.ts.map +1 -0
  490. package/dist/rbac/user-role-service.js +166 -0
  491. package/dist/rbac/user-role-service.js.map +1 -0
  492. package/dist/routes/applications.d.ts +40 -0
  493. package/dist/routes/applications.d.ts.map +1 -0
  494. package/dist/routes/applications.js +284 -0
  495. package/dist/routes/applications.js.map +1 -0
  496. package/dist/routes/audit.d.ts +31 -0
  497. package/dist/routes/audit.d.ts.map +1 -0
  498. package/dist/routes/audit.js +140 -0
  499. package/dist/routes/audit.js.map +1 -0
  500. package/dist/routes/branding.d.ts +17 -0
  501. package/dist/routes/branding.d.ts.map +1 -0
  502. package/dist/routes/branding.js +104 -0
  503. package/dist/routes/branding.js.map +1 -0
  504. package/dist/routes/bulk.d.ts +37 -0
  505. package/dist/routes/bulk.d.ts.map +1 -0
  506. package/dist/routes/bulk.js +94 -0
  507. package/dist/routes/bulk.js.map +1 -0
  508. package/dist/routes/clients.d.ts +44 -0
  509. package/dist/routes/clients.d.ts.map +1 -0
  510. package/dist/routes/clients.js +353 -0
  511. package/dist/routes/clients.js.map +1 -0
  512. package/dist/routes/config.d.ts +35 -0
  513. package/dist/routes/config.d.ts.map +1 -0
  514. package/dist/routes/config.js +115 -0
  515. package/dist/routes/config.js.map +1 -0
  516. package/dist/routes/custom-claims.d.ts +38 -0
  517. package/dist/routes/custom-claims.d.ts.map +1 -0
  518. package/dist/routes/custom-claims.js +208 -0
  519. package/dist/routes/custom-claims.js.map +1 -0
  520. package/dist/routes/exports.d.ts +21 -0
  521. package/dist/routes/exports.d.ts.map +1 -0
  522. package/dist/routes/exports.js +98 -0
  523. package/dist/routes/exports.js.map +1 -0
  524. package/dist/routes/imports.d.ts +18 -0
  525. package/dist/routes/imports.d.ts.map +1 -0
  526. package/dist/routes/imports.js +72 -0
  527. package/dist/routes/imports.js.map +1 -0
  528. package/dist/routes/interactions.d.ts +111 -0
  529. package/dist/routes/interactions.d.ts.map +1 -0
  530. package/dist/routes/interactions.js +1074 -0
  531. package/dist/routes/interactions.js.map +1 -0
  532. package/dist/routes/invitation.d.ts +31 -0
  533. package/dist/routes/invitation.d.ts.map +1 -0
  534. package/dist/routes/invitation.js +342 -0
  535. package/dist/routes/invitation.js.map +1 -0
  536. package/dist/routes/keys.d.ts +28 -0
  537. package/dist/routes/keys.d.ts.map +1 -0
  538. package/dist/routes/keys.js +91 -0
  539. package/dist/routes/keys.js.map +1 -0
  540. package/dist/routes/magic-link.d.ts +43 -0
  541. package/dist/routes/magic-link.d.ts.map +1 -0
  542. package/dist/routes/magic-link.js +264 -0
  543. package/dist/routes/magic-link.js.map +1 -0
  544. package/dist/routes/organizations.d.ts +38 -0
  545. package/dist/routes/organizations.d.ts.map +1 -0
  546. package/dist/routes/organizations.js +347 -0
  547. package/dist/routes/organizations.js.map +1 -0
  548. package/dist/routes/password-reset.d.ts +65 -0
  549. package/dist/routes/password-reset.d.ts.map +1 -0
  550. package/dist/routes/password-reset.js +493 -0
  551. package/dist/routes/password-reset.js.map +1 -0
  552. package/dist/routes/permissions.d.ts +33 -0
  553. package/dist/routes/permissions.d.ts.map +1 -0
  554. package/dist/routes/permissions.js +166 -0
  555. package/dist/routes/permissions.js.map +1 -0
  556. package/dist/routes/roles.d.ts +37 -0
  557. package/dist/routes/roles.d.ts.map +1 -0
  558. package/dist/routes/roles.js +222 -0
  559. package/dist/routes/roles.js.map +1 -0
  560. package/dist/routes/sessions.d.ts +37 -0
  561. package/dist/routes/sessions.d.ts.map +1 -0
  562. package/dist/routes/sessions.js +121 -0
  563. package/dist/routes/sessions.js.map +1 -0
  564. package/dist/routes/stats.d.ts +28 -0
  565. package/dist/routes/stats.d.ts.map +1 -0
  566. package/dist/routes/stats.js +65 -0
  567. package/dist/routes/stats.js.map +1 -0
  568. package/dist/routes/two-factor-admin.d.ts +54 -0
  569. package/dist/routes/two-factor-admin.d.ts.map +1 -0
  570. package/dist/routes/two-factor-admin.js +344 -0
  571. package/dist/routes/two-factor-admin.js.map +1 -0
  572. package/dist/routes/two-factor.d.ts +31 -0
  573. package/dist/routes/two-factor.d.ts.map +1 -0
  574. package/dist/routes/two-factor.js +595 -0
  575. package/dist/routes/two-factor.js.map +1 -0
  576. package/dist/routes/user-roles.d.ts +33 -0
  577. package/dist/routes/user-roles.d.ts.map +1 -0
  578. package/dist/routes/user-roles.js +129 -0
  579. package/dist/routes/user-roles.js.map +1 -0
  580. package/dist/routes/users.d.ts +61 -0
  581. package/dist/routes/users.d.ts.map +1 -0
  582. package/dist/routes/users.js +868 -0
  583. package/dist/routes/users.js.map +1 -0
  584. package/dist/security/decision-context.d.ts +52 -0
  585. package/dist/security/decision-context.d.ts.map +1 -0
  586. package/dist/security/decision-context.js +150 -0
  587. package/dist/security/decision-context.js.map +1 -0
  588. package/dist/security/decision-event.d.ts +128 -0
  589. package/dist/security/decision-event.d.ts.map +1 -0
  590. package/dist/security/decision-event.js +225 -0
  591. package/dist/security/decision-event.js.map +1 -0
  592. package/dist/security/transport-decision.d.ts +6 -0
  593. package/dist/security/transport-decision.d.ts.map +1 -0
  594. package/dist/security/transport-decision.js +37 -0
  595. package/dist/security/transport-decision.js.map +1 -0
  596. package/dist/server.d.ts +50 -0
  597. package/dist/server.d.ts.map +1 -0
  598. package/dist/server.js +491 -0
  599. package/dist/server.js.map +1 -0
  600. package/dist/two-factor/cache.d.ts +47 -0
  601. package/dist/two-factor/cache.d.ts.map +1 -0
  602. package/dist/two-factor/cache.js +122 -0
  603. package/dist/two-factor/cache.js.map +1 -0
  604. package/dist/two-factor/crypto.d.ts +41 -0
  605. package/dist/two-factor/crypto.d.ts.map +1 -0
  606. package/dist/two-factor/crypto.js +94 -0
  607. package/dist/two-factor/crypto.js.map +1 -0
  608. package/dist/two-factor/errors.d.ts +82 -0
  609. package/dist/two-factor/errors.d.ts.map +1 -0
  610. package/dist/two-factor/errors.js +112 -0
  611. package/dist/two-factor/errors.js.map +1 -0
  612. package/dist/two-factor/index.d.ts +18 -0
  613. package/dist/two-factor/index.d.ts.map +1 -0
  614. package/dist/two-factor/index.js +25 -0
  615. package/dist/two-factor/index.js.map +1 -0
  616. package/dist/two-factor/otp.d.ts +43 -0
  617. package/dist/two-factor/otp.d.ts.map +1 -0
  618. package/dist/two-factor/otp.js +57 -0
  619. package/dist/two-factor/otp.js.map +1 -0
  620. package/dist/two-factor/recovery.d.ts +45 -0
  621. package/dist/two-factor/recovery.d.ts.map +1 -0
  622. package/dist/two-factor/recovery.js +104 -0
  623. package/dist/two-factor/recovery.js.map +1 -0
  624. package/dist/two-factor/repository.d.ts +156 -0
  625. package/dist/two-factor/repository.d.ts.map +1 -0
  626. package/dist/two-factor/repository.js +251 -0
  627. package/dist/two-factor/repository.js.map +1 -0
  628. package/dist/two-factor/service.d.ts +232 -0
  629. package/dist/two-factor/service.d.ts.map +1 -0
  630. package/dist/two-factor/service.js +599 -0
  631. package/dist/two-factor/service.js.map +1 -0
  632. package/dist/two-factor/totp.d.ts +57 -0
  633. package/dist/two-factor/totp.d.ts.map +1 -0
  634. package/dist/two-factor/totp.js +96 -0
  635. package/dist/two-factor/totp.js.map +1 -0
  636. package/dist/two-factor/types.d.ts +152 -0
  637. package/dist/two-factor/types.d.ts.map +1 -0
  638. package/dist/two-factor/types.js +64 -0
  639. package/dist/two-factor/types.js.map +1 -0
  640. package/dist/users/cache.d.ts +49 -0
  641. package/dist/users/cache.d.ts.map +1 -0
  642. package/dist/users/cache.js +140 -0
  643. package/dist/users/cache.js.map +1 -0
  644. package/dist/users/claims.d.ts +67 -0
  645. package/dist/users/claims.d.ts.map +1 -0
  646. package/dist/users/claims.js +104 -0
  647. package/dist/users/claims.js.map +1 -0
  648. package/dist/users/errors.d.ts +25 -0
  649. package/dist/users/errors.d.ts.map +1 -0
  650. package/dist/users/errors.js +31 -0
  651. package/dist/users/errors.js.map +1 -0
  652. package/dist/users/gdpr.d.ts +110 -0
  653. package/dist/users/gdpr.d.ts.map +1 -0
  654. package/dist/users/gdpr.js +211 -0
  655. package/dist/users/gdpr.js.map +1 -0
  656. package/dist/users/index.d.ts +20 -0
  657. package/dist/users/index.d.ts.map +1 -0
  658. package/dist/users/index.js +19 -0
  659. package/dist/users/index.js.map +1 -0
  660. package/dist/users/password.d.ts +85 -0
  661. package/dist/users/password.d.ts.map +1 -0
  662. package/dist/users/password.js +138 -0
  663. package/dist/users/password.js.map +1 -0
  664. package/dist/users/repository.d.ts +263 -0
  665. package/dist/users/repository.d.ts.map +1 -0
  666. package/dist/users/repository.js +499 -0
  667. package/dist/users/repository.js.map +1 -0
  668. package/dist/users/service.d.ts +299 -0
  669. package/dist/users/service.d.ts.map +1 -0
  670. package/dist/users/service.js +698 -0
  671. package/dist/users/service.js.map +1 -0
  672. package/dist/users/types.d.ts +220 -0
  673. package/dist/users/types.d.ts.map +1 -0
  674. package/dist/users/types.js +77 -0
  675. package/dist/users/types.js.map +1 -0
  676. package/locales/default/en/common.json +10 -0
  677. package/locales/default/en/consent.json +13 -0
  678. package/locales/default/en/emails.json +17 -0
  679. package/locales/default/en/errors.json +21 -0
  680. package/locales/default/en/forgot-password.json +11 -0
  681. package/locales/default/en/invitation.json +18 -0
  682. package/locales/default/en/login.json +17 -0
  683. package/locales/default/en/logout.json +9 -0
  684. package/locales/default/en/magic-link.json +12 -0
  685. package/locales/default/en/reset-password.json +16 -0
  686. package/locales/default/en/two-factor.json +29 -0
  687. package/migrations/.gitkeep +0 -0
  688. package/migrations/001_extensions.sql +22 -0
  689. package/migrations/002_organizations.sql +43 -0
  690. package/migrations/003_applications.sql +50 -0
  691. package/migrations/004_clients.sql +74 -0
  692. package/migrations/005_users.sql +119 -0
  693. package/migrations/006_roles_permissions.sql +80 -0
  694. package/migrations/007_custom_claims.sql +55 -0
  695. package/migrations/008_config.sql +51 -0
  696. package/migrations/009_audit_log.sql +31 -0
  697. package/migrations/010_oidc_adapter.sql +34 -0
  698. package/migrations/011_seed.sql +53 -0
  699. package/migrations/012_two_factor.sql +88 -0
  700. package/migrations/013_client_secret_sha256.sql +22 -0
  701. package/migrations/014_login_methods.sql +34 -0
  702. package/migrations/015_signing_key_encryption.sql +20 -0
  703. package/migrations/016_failed_login_count.sql +27 -0
  704. package/migrations/017_audit_retention.sql +11 -0
  705. package/migrations/018_admin_api_enhancements.sql +78 -0
  706. package/migrations/019_invitation_details.sql +27 -0
  707. package/migrations/020_two_factor_admin_permission.sql +108 -0
  708. package/migrations/021_auth_recovery_jobs.sql +74 -0
  709. package/migrations/022_recovery_artifact_ownership.sql +25 -0
  710. package/migrations/023_magic_link_authority.sql +39 -0
  711. package/package.json +106 -0
  712. package/templates/default/emails/invitation.hbs +39 -0
  713. package/templates/default/emails/invitation.txt.hbs +18 -0
  714. package/templates/default/emails/magic-link.hbs +30 -0
  715. package/templates/default/emails/magic-link.txt.hbs +11 -0
  716. package/templates/default/emails/otp-code.hbs +31 -0
  717. package/templates/default/emails/otp-code.txt.hbs +15 -0
  718. package/templates/default/emails/password-changed.hbs +24 -0
  719. package/templates/default/emails/password-changed.txt.hbs +7 -0
  720. package/templates/default/emails/password-reset.hbs +30 -0
  721. package/templates/default/emails/password-reset.txt.hbs +11 -0
  722. package/templates/default/emails/welcome.hbs +25 -0
  723. package/templates/default/emails/welcome.txt.hbs +9 -0
  724. package/templates/default/layouts/main.hbs +49 -0
  725. package/templates/default/pages/accept-invite.hbs +25 -0
  726. package/templates/default/pages/consent.hbs +27 -0
  727. package/templates/default/pages/error.hbs +15 -0
  728. package/templates/default/pages/forgot-password.hbs +21 -0
  729. package/templates/default/pages/invite-expired.hbs +6 -0
  730. package/templates/default/pages/invite-success.hbs +9 -0
  731. package/templates/default/pages/login.hbs +108 -0
  732. package/templates/default/pages/logout-success.hbs +5 -0
  733. package/templates/default/pages/logout.hbs +24 -0
  734. package/templates/default/pages/magic-link-sent.hbs +10 -0
  735. package/templates/default/pages/magic-link-success.hbs +17 -0
  736. package/templates/default/pages/reset-password.hbs +20 -0
  737. package/templates/default/pages/reset-success.hbs +9 -0
  738. package/templates/default/pages/two-factor-setup.hbs +65 -0
  739. package/templates/default/pages/two-factor-verify.hbs +77 -0
  740. package/templates/default/partials/flash-messages.hbs +10 -0
  741. package/templates/default/partials/footer.hbs +4 -0
  742. package/templates/default/partials/header.hbs +8 -0
@@ -0,0 +1,698 @@
1
+ /**
2
+ * User service — business logic orchestrator.
3
+ *
4
+ * Composes the repository, cache, password utilities, and audit log
5
+ * to provide the complete user management API. All write operations
6
+ * follow the pattern:
7
+ * 1. Validate inputs
8
+ * 2. Perform DB operation (via repository)
9
+ * 3. Invalidate + re-cache (via cache)
10
+ * 4. Write audit log (fire-and-forget)
11
+ *
12
+ * Read operations check cache first (by ID), fall back to DB on miss.
13
+ * Email lookups always hit the DB (not cached — see cache design decision).
14
+ *
15
+ * Status lifecycle rules:
16
+ * - deactivate: active → inactive
17
+ * - reactivate: inactive → active
18
+ * - suspend: active → suspended
19
+ * - unsuspend: suspended → active
20
+ * - lock: active → locked (with reason and timestamp)
21
+ * - unlock: locked → active (clears reason and timestamp)
22
+ */
23
+ import { writeAuditLog } from '../lib/audit-log.js';
24
+ import { getSystemConfigNumber } from '../lib/system-config.js';
25
+ import { cacheUser, getCachedUserById, invalidateUserCache } from './cache.js';
26
+ import { UserNotFoundError, UserValidationError } from './errors.js';
27
+ import { ensureDummyPasswordHash, hashPassword, validatePassword, verifyPassword, } from './password.js';
28
+ import { emailExists, getPasswordHash, incrementFailedLoginCount, insertUser, findUserByEmail as repoFindByEmail, findUserById as repoFindById, listUsers as repoList, listUsersCursor as repoListCursor, updateUser as repoUpdate, resetFailedLoginCount, recordEligiblePasswordFailure, unlockEligiblePasswordAccount, updateLoginStats, } from './repository.js';
29
+ /** Fixed UUID that can never identify a persisted account. */
30
+ const NON_ACCOUNT_USER_ID = '00000000-0000-0000-0000-000000000000';
31
+ // ---------------------------------------------------------------------------
32
+ // Create
33
+ // ---------------------------------------------------------------------------
34
+ /**
35
+ * Create a new user in an organization.
36
+ *
37
+ * 1. Validates email uniqueness within the organization
38
+ * 2. Validates and hashes password if provided
39
+ * 3. Inserts user via repository
40
+ * 4. Caches the new user
41
+ * 5. Writes audit log entry
42
+ *
43
+ * @param input - User creation data (password is plaintext, will be hashed)
44
+ * @param actorId - UUID of the user performing the action (for audit)
45
+ * @returns Created user
46
+ * @throws UserValidationError if email exists or password invalid
47
+ */
48
+ export async function createUser(input, actorId) {
49
+ // Check for duplicate email in the organization
50
+ const exists = await emailExists(input.organizationId, input.email);
51
+ if (exists) {
52
+ throw new UserValidationError('Email already exists in this organization');
53
+ }
54
+ // Hash password if provided
55
+ let passwordHash = null;
56
+ if (input.password) {
57
+ const validation = validatePassword(input.password);
58
+ if (!validation.isValid) {
59
+ throw new UserValidationError(validation.error);
60
+ }
61
+ passwordHash = await hashPassword(input.password);
62
+ }
63
+ // Insert into database
64
+ const user = await insertUser({
65
+ organizationId: input.organizationId,
66
+ email: input.email,
67
+ passwordHash,
68
+ givenName: input.givenName,
69
+ familyName: input.familyName,
70
+ middleName: input.middleName,
71
+ nickname: input.nickname,
72
+ preferredUsername: input.preferredUsername,
73
+ profileUrl: input.profileUrl,
74
+ pictureUrl: input.pictureUrl,
75
+ websiteUrl: input.websiteUrl,
76
+ gender: input.gender,
77
+ birthdate: input.birthdate,
78
+ zoneinfo: input.zoneinfo,
79
+ locale: input.locale,
80
+ phoneNumber: input.phoneNumber,
81
+ addressStreet: input.address?.street,
82
+ addressLocality: input.address?.locality,
83
+ addressRegion: input.address?.region,
84
+ addressPostalCode: input.address?.postalCode,
85
+ addressCountry: input.address?.country,
86
+ });
87
+ // Cache the new user
88
+ await cacheUser(user);
89
+ // Audit log (fire-and-forget)
90
+ await writeAuditLog({
91
+ organizationId: user.organizationId,
92
+ actorId,
93
+ eventType: 'user.created',
94
+ eventCategory: 'admin',
95
+ metadata: { userId: user.id, email: user.email },
96
+ });
97
+ return user;
98
+ }
99
+ // ---------------------------------------------------------------------------
100
+ // Read
101
+ // ---------------------------------------------------------------------------
102
+ /**
103
+ * Find a user by ID.
104
+ * Checks Redis cache first, falls back to database on miss.
105
+ *
106
+ * @param id - User UUID
107
+ * @returns User or null if not found
108
+ */
109
+ export async function getUserById(id) {
110
+ // Try cache first
111
+ const cached = await getCachedUserById(id);
112
+ if (cached)
113
+ return cached;
114
+ // Fall back to database
115
+ const user = await repoFindById(id);
116
+ if (user) {
117
+ await cacheUser(user);
118
+ }
119
+ return user;
120
+ }
121
+ /**
122
+ * Find a user by email within an organization.
123
+ * Always hits the database (email lookups are not cached).
124
+ *
125
+ * @param orgId - Organization UUID
126
+ * @param email - User email address (case-insensitive)
127
+ * @returns User or null if not found
128
+ */
129
+ export async function getUserByEmail(orgId, email) {
130
+ return repoFindByEmail(orgId, email);
131
+ }
132
+ // ---------------------------------------------------------------------------
133
+ // Update
134
+ // ---------------------------------------------------------------------------
135
+ /**
136
+ * Update a user's profile fields.
137
+ *
138
+ * Builds an update data object from the input, checking for email
139
+ * uniqueness if the email is being changed. Address fields are
140
+ * flattened from the nested AddressInput to individual columns.
141
+ *
142
+ * @param id - User UUID
143
+ * @param input - Fields to update
144
+ * @param actorId - UUID of the user performing the action
145
+ * @returns Updated user
146
+ * @throws UserNotFoundError if user not found
147
+ * @throws UserValidationError if new email already exists in org
148
+ */
149
+ export async function updateUser(id, input, actorId) {
150
+ // If email is being changed, verify uniqueness
151
+ if (input.email !== undefined) {
152
+ // We need the current user to know the org ID
153
+ const current = await repoFindById(id);
154
+ if (!current)
155
+ throw new UserNotFoundError(id);
156
+ const taken = await emailExists(current.organizationId, input.email, id);
157
+ if (taken) {
158
+ throw new UserValidationError('Email already exists in this organization');
159
+ }
160
+ }
161
+ // Build update data from input
162
+ const updateData = {};
163
+ if (input.email !== undefined)
164
+ updateData.email = input.email;
165
+ if (input.emailVerified !== undefined)
166
+ updateData.emailVerified = input.emailVerified;
167
+ if (input.givenName !== undefined)
168
+ updateData.givenName = input.givenName;
169
+ if (input.familyName !== undefined)
170
+ updateData.familyName = input.familyName;
171
+ if (input.middleName !== undefined)
172
+ updateData.middleName = input.middleName;
173
+ if (input.nickname !== undefined)
174
+ updateData.nickname = input.nickname;
175
+ if (input.preferredUsername !== undefined)
176
+ updateData.preferredUsername = input.preferredUsername;
177
+ if (input.profileUrl !== undefined)
178
+ updateData.profileUrl = input.profileUrl;
179
+ if (input.pictureUrl !== undefined)
180
+ updateData.pictureUrl = input.pictureUrl;
181
+ if (input.websiteUrl !== undefined)
182
+ updateData.websiteUrl = input.websiteUrl;
183
+ if (input.gender !== undefined)
184
+ updateData.gender = input.gender;
185
+ if (input.birthdate !== undefined)
186
+ updateData.birthdate = input.birthdate;
187
+ if (input.zoneinfo !== undefined)
188
+ updateData.zoneinfo = input.zoneinfo;
189
+ if (input.locale !== undefined)
190
+ updateData.locale = input.locale;
191
+ if (input.phoneNumber !== undefined)
192
+ updateData.phoneNumber = input.phoneNumber;
193
+ if (input.phoneNumberVerified !== undefined)
194
+ updateData.phoneNumberVerified = input.phoneNumberVerified;
195
+ // Flatten address fields from nested input
196
+ if (input.address) {
197
+ if (input.address.street !== undefined)
198
+ updateData.addressStreet = input.address.street;
199
+ if (input.address.locality !== undefined)
200
+ updateData.addressLocality = input.address.locality;
201
+ if (input.address.region !== undefined)
202
+ updateData.addressRegion = input.address.region;
203
+ if (input.address.postalCode !== undefined)
204
+ updateData.addressPostalCode = input.address.postalCode;
205
+ if (input.address.country !== undefined)
206
+ updateData.addressCountry = input.address.country;
207
+ }
208
+ let user;
209
+ try {
210
+ user = await repoUpdate(id, updateData);
211
+ }
212
+ catch (err) {
213
+ if (err instanceof Error && err.message === 'User not found') {
214
+ throw new UserNotFoundError(id);
215
+ }
216
+ throw err;
217
+ }
218
+ // Invalidate old cache and re-cache the updated version
219
+ await invalidateUserCache(user.id);
220
+ await cacheUser(user);
221
+ // Audit log (fire-and-forget)
222
+ await writeAuditLog({
223
+ organizationId: user.organizationId,
224
+ actorId,
225
+ eventType: 'user.updated',
226
+ eventCategory: 'admin',
227
+ metadata: { userId: user.id, fields: Object.keys(updateData) },
228
+ });
229
+ return user;
230
+ }
231
+ // ---------------------------------------------------------------------------
232
+ // List
233
+ // ---------------------------------------------------------------------------
234
+ /**
235
+ * List users within an organization with pagination and filtering.
236
+ * Delegates directly to the repository.
237
+ *
238
+ * @param options - Pagination, filter, and sort options (orgId required)
239
+ * @returns Paginated result
240
+ */
241
+ export async function listUsersByOrganization(options) {
242
+ return repoList(options);
243
+ }
244
+ /**
245
+ * List users with cursor-based keyset pagination.
246
+ * Supports all existing filters (org scope, status, search, sort).
247
+ *
248
+ * @param options - Cursor, limit, filter, and sort options (orgId required)
249
+ * @returns Cursor-paginated result with nextCursor/hasMore
250
+ */
251
+ export async function listUsersCursor(options) {
252
+ return repoListCursor(options);
253
+ }
254
+ // ---------------------------------------------------------------------------
255
+ // Status lifecycle
256
+ // ---------------------------------------------------------------------------
257
+ /**
258
+ * Load a user and validate it exists before a status change.
259
+ * @throws UserNotFoundError if not found
260
+ */
261
+ async function loadUserForStatusChange(id) {
262
+ const user = await repoFindById(id);
263
+ if (!user)
264
+ throw new UserNotFoundError(id);
265
+ return user;
266
+ }
267
+ /**
268
+ * Deactivate a user (active → inactive).
269
+ *
270
+ * @param id - User UUID
271
+ * @param actorId - UUID of the user performing the action
272
+ * @throws UserNotFoundError if not found
273
+ * @throws UserValidationError if not currently active
274
+ */
275
+ export async function deactivateUser(id, actorId) {
276
+ const user = await loadUserForStatusChange(id);
277
+ if (user.status !== 'active') {
278
+ throw new UserValidationError(`Cannot deactivate user from status: ${user.status}`);
279
+ }
280
+ await repoUpdate(id, { status: 'inactive' });
281
+ await invalidateUserCache(id);
282
+ await writeAuditLog({
283
+ organizationId: user.organizationId,
284
+ actorId,
285
+ eventType: 'user.deactivated',
286
+ eventCategory: 'admin',
287
+ metadata: { userId: id },
288
+ });
289
+ }
290
+ /**
291
+ * Reactivate a user (inactive → active).
292
+ *
293
+ * @param id - User UUID
294
+ * @param actorId - UUID of the user performing the action
295
+ * @throws UserNotFoundError if not found
296
+ * @throws UserValidationError if not currently inactive
297
+ */
298
+ export async function reactivateUser(id, actorId) {
299
+ const user = await loadUserForStatusChange(id);
300
+ if (user.status !== 'inactive') {
301
+ throw new UserValidationError(`Cannot reactivate user from status: ${user.status}`);
302
+ }
303
+ await repoUpdate(id, { status: 'active' });
304
+ await invalidateUserCache(id);
305
+ await writeAuditLog({
306
+ organizationId: user.organizationId,
307
+ actorId,
308
+ eventType: 'user.reactivated',
309
+ eventCategory: 'admin',
310
+ metadata: { userId: id },
311
+ });
312
+ }
313
+ /**
314
+ * Suspend a user (active → suspended).
315
+ *
316
+ * @param id - User UUID
317
+ * @param reason - Optional reason for suspension
318
+ * @param actorId - UUID of the user performing the action
319
+ * @throws UserNotFoundError if not found
320
+ * @throws UserValidationError if not currently active
321
+ */
322
+ export async function suspendUser(id, reason, actorId) {
323
+ const user = await loadUserForStatusChange(id);
324
+ if (user.status !== 'active') {
325
+ throw new UserValidationError(`Cannot suspend user from status: ${user.status}`);
326
+ }
327
+ await repoUpdate(id, { status: 'suspended' });
328
+ await invalidateUserCache(id);
329
+ await writeAuditLog({
330
+ organizationId: user.organizationId,
331
+ actorId,
332
+ eventType: 'user.suspended',
333
+ eventCategory: 'admin',
334
+ metadata: { userId: id, reason: reason ?? null },
335
+ });
336
+ }
337
+ /**
338
+ * Unsuspend a user (suspended → active).
339
+ *
340
+ * @param id - User UUID
341
+ * @param actorId - UUID of the user performing the action
342
+ * @throws UserNotFoundError if not found
343
+ * @throws UserValidationError if not currently suspended
344
+ */
345
+ export async function unsuspendUser(id, actorId) {
346
+ const user = await loadUserForStatusChange(id);
347
+ if (user.status !== 'suspended') {
348
+ throw new UserValidationError(`Cannot unsuspend user from status: ${user.status}`);
349
+ }
350
+ await repoUpdate(id, { status: 'active' });
351
+ await invalidateUserCache(id);
352
+ await writeAuditLog({
353
+ organizationId: user.organizationId,
354
+ actorId,
355
+ eventType: 'user.activated',
356
+ eventCategory: 'admin',
357
+ metadata: { userId: id },
358
+ });
359
+ }
360
+ /**
361
+ * Lock a user (active → locked).
362
+ *
363
+ * Sets the locked_at timestamp and locked_reason. Locked users
364
+ * cannot authenticate (getPasswordHash only returns for active users).
365
+ *
366
+ * @param id - User UUID
367
+ * @param reason - Reason for locking the account
368
+ * @param actorId - UUID of the user performing the action
369
+ * @throws UserNotFoundError if not found
370
+ * @throws UserValidationError if not currently active
371
+ */
372
+ export async function lockUser(id, reason, actorId) {
373
+ const user = await loadUserForStatusChange(id);
374
+ if (user.status !== 'active') {
375
+ throw new UserValidationError(`Cannot lock user from status: ${user.status}`);
376
+ }
377
+ await repoUpdate(id, {
378
+ status: 'locked',
379
+ lockedAt: new Date(),
380
+ lockedReason: reason,
381
+ });
382
+ await invalidateUserCache(id);
383
+ await writeAuditLog({
384
+ organizationId: user.organizationId,
385
+ actorId,
386
+ eventType: 'user.locked',
387
+ eventCategory: 'admin',
388
+ metadata: { userId: id, reason },
389
+ });
390
+ }
391
+ /**
392
+ * Unlock a user (locked → active).
393
+ *
394
+ * Clears the locked_at timestamp and locked_reason.
395
+ *
396
+ * @param id - User UUID
397
+ * @param actorId - UUID of the user performing the action
398
+ * @throws UserNotFoundError if not found
399
+ * @throws UserValidationError if not currently locked
400
+ */
401
+ export async function unlockUser(id, actorId) {
402
+ const user = await loadUserForStatusChange(id);
403
+ if (user.status !== 'locked') {
404
+ throw new UserValidationError(`Cannot unlock user from status: ${user.status}`);
405
+ }
406
+ await repoUpdate(id, {
407
+ status: 'active',
408
+ lockedAt: null,
409
+ lockedReason: null,
410
+ });
411
+ await invalidateUserCache(id);
412
+ await writeAuditLog({
413
+ organizationId: user.organizationId,
414
+ actorId,
415
+ eventType: 'user.unlocked',
416
+ eventCategory: 'admin',
417
+ metadata: { userId: id },
418
+ });
419
+ }
420
+ // ---------------------------------------------------------------------------
421
+ // Password management
422
+ // ---------------------------------------------------------------------------
423
+ /**
424
+ * Set a user's password.
425
+ *
426
+ * Validates the password, hashes it with Argon2id, and stores the hash.
427
+ * Also sets password_changed_at to the current timestamp.
428
+ *
429
+ * @param id - User UUID
430
+ * @param password - New plaintext password
431
+ * @param actorId - UUID of the user performing the action
432
+ * @throws UserValidationError if password fails validation
433
+ */
434
+ export async function setUserPassword(id, password, actorId) {
435
+ const validation = validatePassword(password);
436
+ if (!validation.isValid) {
437
+ throw new UserValidationError(validation.error);
438
+ }
439
+ const hash = await hashPassword(password);
440
+ await repoUpdate(id, {
441
+ passwordHash: hash,
442
+ passwordChangedAt: new Date(),
443
+ });
444
+ await invalidateUserCache(id);
445
+ await writeAuditLog({
446
+ actorId,
447
+ eventType: 'user.password.set',
448
+ eventCategory: 'admin',
449
+ metadata: { userId: id },
450
+ });
451
+ }
452
+ /**
453
+ * Verify a user's password.
454
+ *
455
+ * Returns false if the user has no password (passwordless), is not
456
+ * active, or if the password doesn't match. No audit log is written —
457
+ * login tracking is handled separately by recordLogin().
458
+ *
459
+ * @param id - User UUID
460
+ * @param password - Plaintext password to verify
461
+ * @returns true if the password matches
462
+ */
463
+ export async function verifyUserPassword(id, password) {
464
+ const hash = await getPasswordHash(id);
465
+ if (!hash)
466
+ return false;
467
+ return verifyPassword(hash, password);
468
+ }
469
+ /**
470
+ * Verify exactly one account or dummy Argon2id hash for an admitted login attempt.
471
+ *
472
+ * @param id - Eligible active account UUID, or null for every non-eligible identity state.
473
+ * @param password - Submitted plaintext password.
474
+ * @param dependencies - Production hash boundaries, replaceable by pass-through observers.
475
+ * @returns True only when an eligible account hash matched.
476
+ */
477
+ export async function verifyLoginPassword(id, password, dependencies) {
478
+ const boundaries = dependencies ?? {
479
+ loadAccountHash: getPasswordHash,
480
+ loadDummyHash: ensureDummyPasswordHash,
481
+ verifyHash: verifyPassword,
482
+ };
483
+ const hash = await boundaries.loadAccountHash(id ?? NON_ACCOUNT_USER_ID);
484
+ const selectedHash = hash ?? (await boundaries.loadDummyHash());
485
+ const matched = await boundaries.verifyHash(selectedHash, password);
486
+ return id !== null && hash !== null && matched;
487
+ }
488
+ /**
489
+ * Resolve the password-login account through a fixed sequence of database and cache operations.
490
+ *
491
+ * The initial lookup identifies a possible elapsed automatic lock. The conditional update and
492
+ * cache invalidation execute for every admitted identity, using a non-account UUID when absent.
493
+ * A second lookup then observes the post-cooldown state without an identity-dependent branch.
494
+ *
495
+ * @param organizationId - Resolved tenant authority.
496
+ * @param email - Normalized login address.
497
+ * @returns Current account state, or null when no account exists.
498
+ */
499
+ export async function prepareUserForPasswordLogin(organizationId, email) {
500
+ const candidate = await getUserByEmail(organizationId, email);
501
+ const cooldownSeconds = await getSystemConfigNumber('lockout_duration_seconds', 900);
502
+ const candidateId = candidate?.id ?? NON_ACCOUNT_USER_ID;
503
+ const unlocked = await unlockEligiblePasswordAccount(candidateId, new Date(Date.now() - cooldownSeconds * 1_000));
504
+ await invalidateUserCache(candidateId);
505
+ if (unlocked && candidate) {
506
+ void writeAuditLog({
507
+ organizationId: candidate.organizationId,
508
+ userId: candidate.id,
509
+ eventType: 'user.auto_unlocked',
510
+ eventCategory: 'security',
511
+ description: 'Account auto-unlocked after configured cooldown',
512
+ });
513
+ }
514
+ return getUserByEmail(organizationId, email);
515
+ }
516
+ /**
517
+ * Clear a user's password (convert to passwordless-only).
518
+ *
519
+ * Sets password_hash to NULL and clears password_changed_at.
520
+ *
521
+ * @param id - User UUID
522
+ * @param actorId - UUID of the user performing the action
523
+ */
524
+ export async function clearUserPassword(id, actorId) {
525
+ await repoUpdate(id, {
526
+ passwordHash: null,
527
+ passwordChangedAt: null,
528
+ });
529
+ await invalidateUserCache(id);
530
+ await writeAuditLog({
531
+ actorId,
532
+ eventType: 'user.password.cleared',
533
+ eventCategory: 'admin',
534
+ metadata: { userId: id },
535
+ });
536
+ }
537
+ // ---------------------------------------------------------------------------
538
+ // Email verification
539
+ // ---------------------------------------------------------------------------
540
+ /**
541
+ * Mark a user's email as verified.
542
+ *
543
+ * @param id - User UUID
544
+ * @param actorId - UUID of the user performing the action
545
+ */
546
+ export async function markEmailVerified(id, actorId) {
547
+ await repoUpdate(id, { emailVerified: true });
548
+ await invalidateUserCache(id);
549
+ await writeAuditLog({
550
+ actorId,
551
+ eventType: 'user.email.verified',
552
+ eventCategory: 'admin',
553
+ metadata: { userId: id },
554
+ });
555
+ }
556
+ /**
557
+ * Mark a user's email as unverified.
558
+ * Typically called when the email address changes.
559
+ *
560
+ * @param id - User UUID
561
+ */
562
+ export async function markEmailUnverified(id) {
563
+ await repoUpdate(id, { emailVerified: false });
564
+ await invalidateUserCache(id);
565
+ }
566
+ // ---------------------------------------------------------------------------
567
+ // Login tracking
568
+ // ---------------------------------------------------------------------------
569
+ /**
570
+ * Record a successful login for a user.
571
+ *
572
+ * Increments login_count and sets last_login_at. Also resets
573
+ * failed_login_count and last_failed_login_at. Invalidates cache
574
+ * because the login stats have changed.
575
+ *
576
+ * @param id - User UUID
577
+ */
578
+ export async function recordLogin(id) {
579
+ await updateLoginStats(id);
580
+ await invalidateUserCache(id);
581
+ }
582
+ /**
583
+ * Record a failed login attempt and auto-lock if threshold exceeded.
584
+ *
585
+ * Uses an atomic SQL UPDATE to increment the counter and conditionally
586
+ * flip status to 'locked' with reason 'auto_lockout' in a single query.
587
+ * Returns whether the account was just locked so the caller can show
588
+ * an appropriate message and write an audit event.
589
+ *
590
+ * The threshold comes from system_config `max_failed_logins` (default 5).
591
+ *
592
+ * @param user - The user whose login failed
593
+ * @returns Object with `locked: true` if the account was just auto-locked
594
+ */
595
+ export async function recordFailedLogin(user) {
596
+ const maxAttempts = await getSystemConfigNumber('max_failed_logins', 5);
597
+ const result = await incrementFailedLoginCount(user.id, maxAttempts);
598
+ await invalidateUserCache(user.id);
599
+ const justLocked = result.status === 'locked' && user.status === 'active';
600
+ if (justLocked) {
601
+ void writeAuditLog({
602
+ organizationId: user.organizationId,
603
+ userId: user.id,
604
+ eventType: 'user.auto_locked',
605
+ eventCategory: 'security',
606
+ description: `Account auto-locked after ${result.failedLoginCount} failed login attempts`,
607
+ metadata: { failedCount: result.failedLoginCount, threshold: maxAttempts },
608
+ });
609
+ }
610
+ return { locked: justLocked, failedCount: result.failedLoginCount };
611
+ }
612
+ /**
613
+ * Execute one fixed-shape failure-accounting operation for an admitted password rejection.
614
+ *
615
+ * @param user - Eligible active account, or null for every other identity state.
616
+ * @param dependencies - Production persistence/cache boundaries, replaceable by pass-through observers.
617
+ * @returns Neutral or real account-lock result without disclosing identity publicly.
618
+ */
619
+ export async function recordPasswordFailure(user, dependencies) {
620
+ const boundaries = dependencies ?? {
621
+ recordEligibleFailure: recordEligiblePasswordFailure,
622
+ invalidateCache: invalidateUserCache,
623
+ };
624
+ const maxAttempts = await getSystemConfigNumber('max_failed_logins', 5);
625
+ const userId = user?.id ?? NON_ACCOUNT_USER_ID;
626
+ const result = await boundaries.recordEligibleFailure(userId, maxAttempts);
627
+ await boundaries.invalidateCache(userId);
628
+ const locked = result.status === 'locked' && user !== null;
629
+ if (locked && user) {
630
+ void writeAuditLog({
631
+ organizationId: user.organizationId,
632
+ userId: user.id,
633
+ eventType: 'user.auto_locked',
634
+ eventCategory: 'security',
635
+ description: 'Account auto-locked after failed login threshold',
636
+ metadata: { failedCount: result.failedLoginCount, threshold: maxAttempts },
637
+ });
638
+ }
639
+ return { locked, failedCount: result.failedLoginCount };
640
+ }
641
+ /**
642
+ * Check whether an auto-locked account should be unlocked (cooldown elapsed).
643
+ *
644
+ * Only applies to users with `status = 'locked'` and
645
+ * `lockedReason = 'auto_lockout'`. Manual locks are never auto-unlocked.
646
+ *
647
+ * The cooldown duration comes from system_config `lockout_duration_seconds`
648
+ * (default 900 = 15 minutes).
649
+ *
650
+ * @param user - User to check
651
+ * @returns `true` if the account was auto-unlocked
652
+ */
653
+ export async function checkAutoUnlock(user) {
654
+ if (user.status !== 'locked' || user.lockedReason !== 'auto_lockout') {
655
+ return false;
656
+ }
657
+ if (!user.lockedAt)
658
+ return false;
659
+ const cooldownSeconds = await getSystemConfigNumber('lockout_duration_seconds', 900);
660
+ const elapsed = (Date.now() - user.lockedAt.getTime()) / 1000;
661
+ if (elapsed < cooldownSeconds) {
662
+ return false;
663
+ }
664
+ // Cooldown elapsed — unlock and reset counter
665
+ await resetFailedLoginCount(user.id);
666
+ await invalidateUserCache(user.id);
667
+ writeAuditLog({
668
+ organizationId: user.organizationId,
669
+ userId: user.id,
670
+ eventType: 'user.auto_unlocked',
671
+ eventCategory: 'security',
672
+ description: `Account auto-unlocked after ${cooldownSeconds}s cooldown`,
673
+ });
674
+ return true;
675
+ }
676
+ // ---------------------------------------------------------------------------
677
+ // OIDC integration
678
+ // ---------------------------------------------------------------------------
679
+ /**
680
+ * Find a user for OIDC token/userinfo operations.
681
+ *
682
+ * Returns the user only if their status is 'active'. Non-active users
683
+ * should not be able to obtain tokens or access the userinfo endpoint.
684
+ * Used by the upgraded account-finder.ts.
685
+ *
686
+ * @param sub - Subject identifier (user UUID)
687
+ * @returns Active user or null
688
+ */
689
+ export async function findUserForOidc(sub) {
690
+ const user = await getUserById(sub);
691
+ if (!user)
692
+ return null;
693
+ // Only active users can interact with OIDC endpoints
694
+ if (user.status !== 'active')
695
+ return null;
696
+ return user;
697
+ }
698
+ //# sourceMappingURL=service.js.map