@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,43 @@
1
+ /**
2
+ * Email OTP code utilities.
3
+ *
4
+ * Generates, hashes, and verifies 6-digit one-time passwords sent via email.
5
+ * Codes are cryptographically random (not sequential) and stored as SHA-256
6
+ * hashes — the plaintext is only ever sent to the user's email and never
7
+ * persisted in the database.
8
+ *
9
+ * SHA-256 is appropriate here (rather than Argon2id) because OTP codes are
10
+ * short-lived (10 minutes) and brute-forcing a 6-digit code against a fast
11
+ * hash is still rate-limited at the application level.
12
+ */
13
+ /**
14
+ * Generate a cryptographically random 6-digit OTP code.
15
+ *
16
+ * Uses Node.js `crypto.randomInt()` for uniform random distribution.
17
+ * The result is zero-padded to always be exactly 6 characters.
18
+ *
19
+ * @returns A 6-digit string (e.g., "042819", "123456")
20
+ */
21
+ export declare function generateOtpCode(): string;
22
+ /**
23
+ * Hash an OTP code with SHA-256 for database storage.
24
+ *
25
+ * @param code - The 6-digit OTP code to hash
26
+ * @returns Hex-encoded SHA-256 hash (64 characters)
27
+ */
28
+ export declare function hashOtpCode(code: string): string;
29
+ /**
30
+ * Verify an OTP code against its SHA-256 hash.
31
+ *
32
+ * Performs a constant-time-equivalent comparison by computing the hash
33
+ * of the provided code and comparing hex strings. While timing attacks
34
+ * on SHA-256 hex comparisons are extremely unlikely to be practical for
35
+ * 6-digit codes, we hash-then-compare rather than using the raw input
36
+ * to avoid any theoretical leakage.
37
+ *
38
+ * @param code - The OTP code to verify
39
+ * @param hash - The stored SHA-256 hash to verify against
40
+ * @returns True if the code matches the hash
41
+ */
42
+ export declare function verifyOtpCode(code: string, hash: string): boolean;
43
+ //# sourceMappingURL=otp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otp.d.ts","sourceRoot":"","sources":["../../src/two-factor/otp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH;;;;;;;GAOG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAIxC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGjE"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Email OTP code utilities.
3
+ *
4
+ * Generates, hashes, and verifies 6-digit one-time passwords sent via email.
5
+ * Codes are cryptographically random (not sequential) and stored as SHA-256
6
+ * hashes — the plaintext is only ever sent to the user's email and never
7
+ * persisted in the database.
8
+ *
9
+ * SHA-256 is appropriate here (rather than Argon2id) because OTP codes are
10
+ * short-lived (10 minutes) and brute-forcing a 6-digit code against a fast
11
+ * hash is still rate-limited at the application level.
12
+ */
13
+ import { createHash, randomInt } from 'node:crypto';
14
+ /** OTP code length — 6 digits, zero-padded. */
15
+ const OTP_CODE_LENGTH = 6;
16
+ /** Maximum value for a 6-digit code (exclusive). */
17
+ const OTP_MAX_VALUE = 1_000_000;
18
+ /**
19
+ * Generate a cryptographically random 6-digit OTP code.
20
+ *
21
+ * Uses Node.js `crypto.randomInt()` for uniform random distribution.
22
+ * The result is zero-padded to always be exactly 6 characters.
23
+ *
24
+ * @returns A 6-digit string (e.g., "042819", "123456")
25
+ */
26
+ export function generateOtpCode() {
27
+ // randomInt(max) returns a uniform random integer in [0, max)
28
+ const code = randomInt(OTP_MAX_VALUE);
29
+ return code.toString().padStart(OTP_CODE_LENGTH, '0');
30
+ }
31
+ /**
32
+ * Hash an OTP code with SHA-256 for database storage.
33
+ *
34
+ * @param code - The 6-digit OTP code to hash
35
+ * @returns Hex-encoded SHA-256 hash (64 characters)
36
+ */
37
+ export function hashOtpCode(code) {
38
+ return createHash('sha256').update(code).digest('hex');
39
+ }
40
+ /**
41
+ * Verify an OTP code against its SHA-256 hash.
42
+ *
43
+ * Performs a constant-time-equivalent comparison by computing the hash
44
+ * of the provided code and comparing hex strings. While timing attacks
45
+ * on SHA-256 hex comparisons are extremely unlikely to be practical for
46
+ * 6-digit codes, we hash-then-compare rather than using the raw input
47
+ * to avoid any theoretical leakage.
48
+ *
49
+ * @param code - The OTP code to verify
50
+ * @param hash - The stored SHA-256 hash to verify against
51
+ * @returns True if the code matches the hash
52
+ */
53
+ export function verifyOtpCode(code, hash) {
54
+ const computedHash = hashOtpCode(code);
55
+ return computedHash === hash;
56
+ }
57
+ //# sourceMappingURL=otp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otp.js","sourceRoot":"","sources":["../../src/two-factor/otp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEpD,+CAA+C;AAC/C,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,oDAAoD;AACpD,MAAM,aAAa,GAAG,SAAS,CAAC;AAEhC;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe;IAC7B,8DAA8D;IAC9D,MAAM,IAAI,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC;IACtC,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;AACxD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,IAAY;IACtD,MAAM,YAAY,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,YAAY,KAAK,IAAI,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Recovery code utilities.
3
+ *
4
+ * Generates, hashes, and verifies single-use recovery codes for 2FA backup.
5
+ * Users receive 10 codes during 2FA setup, each in XXXX-XXXX format
6
+ * (8 alphanumeric characters). Codes are hashed with Argon2id before storage
7
+ * because they are long-lived and relatively high-entropy — they must resist
8
+ * offline brute-force attacks if the database is compromised.
9
+ *
10
+ * Uses the same Argon2id parameters as password hashing (via the `argon2`
11
+ * library's defaults) for consistency and security.
12
+ */
13
+ /**
14
+ * Generate a set of recovery codes in XXXX-XXXX format.
15
+ *
16
+ * Each code is 8 characters (split into two 4-character groups with a
17
+ * dash separator) drawn from an unambiguous alphanumeric alphabet.
18
+ * The dash is cosmetic and stripped during verification.
19
+ *
20
+ * @param count - Number of codes to generate (default: 10)
21
+ * @returns Array of plaintext recovery codes
22
+ */
23
+ export declare function generateRecoveryCodes(count?: number): string[];
24
+ /**
25
+ * Hash a recovery code with Argon2id for database storage.
26
+ *
27
+ * The code is normalized (uppercase, dash stripped) before hashing
28
+ * so that verification is case-insensitive and dash-insensitive.
29
+ *
30
+ * @param code - Plaintext recovery code (e.g., "ABCD-1234")
31
+ * @returns Argon2id hash string
32
+ */
33
+ export declare function hashRecoveryCode(code: string): Promise<string>;
34
+ /**
35
+ * Verify a recovery code against an Argon2id hash.
36
+ *
37
+ * The code is normalized before verification so that "abcd-1234",
38
+ * "ABCD1234", and "ABCD-1234" are all equivalent.
39
+ *
40
+ * @param code - Plaintext recovery code to verify
41
+ * @param codeHash - Stored Argon2id hash
42
+ * @returns True if the code matches the hash
43
+ */
44
+ export declare function verifyRecoveryCode(code: string, codeHash: string): Promise<boolean>;
45
+ //# sourceMappingURL=recovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../src/two-factor/recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAiBH;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,GAAE,MAAoC,GAAG,MAAM,EAAE,CAQ3F;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGpE;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQzF"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Recovery code utilities.
3
+ *
4
+ * Generates, hashes, and verifies single-use recovery codes for 2FA backup.
5
+ * Users receive 10 codes during 2FA setup, each in XXXX-XXXX format
6
+ * (8 alphanumeric characters). Codes are hashed with Argon2id before storage
7
+ * because they are long-lived and relatively high-entropy — they must resist
8
+ * offline brute-force attacks if the database is compromised.
9
+ *
10
+ * Uses the same Argon2id parameters as password hashing (via the `argon2`
11
+ * library's defaults) for consistency and security.
12
+ */
13
+ import { hash, verify } from 'argon2';
14
+ import { randomBytes } from 'node:crypto';
15
+ /** Default number of recovery codes to generate. */
16
+ const DEFAULT_RECOVERY_CODE_COUNT = 10;
17
+ /**
18
+ * Characters used in recovery codes — uppercase alphanumeric, excluding
19
+ * visually ambiguous characters (0/O, 1/I/L) for easier manual entry.
20
+ */
21
+ const CODE_CHARS = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789';
22
+ /** Length of each half of a recovery code (XXXX-XXXX). */
23
+ const HALF_LENGTH = 4;
24
+ /**
25
+ * Generate a set of recovery codes in XXXX-XXXX format.
26
+ *
27
+ * Each code is 8 characters (split into two 4-character groups with a
28
+ * dash separator) drawn from an unambiguous alphanumeric alphabet.
29
+ * The dash is cosmetic and stripped during verification.
30
+ *
31
+ * @param count - Number of codes to generate (default: 10)
32
+ * @returns Array of plaintext recovery codes
33
+ */
34
+ export function generateRecoveryCodes(count = DEFAULT_RECOVERY_CODE_COUNT) {
35
+ const codes = [];
36
+ for (let i = 0; i < count; i++) {
37
+ codes.push(generateSingleCode());
38
+ }
39
+ return codes;
40
+ }
41
+ /**
42
+ * Hash a recovery code with Argon2id for database storage.
43
+ *
44
+ * The code is normalized (uppercase, dash stripped) before hashing
45
+ * so that verification is case-insensitive and dash-insensitive.
46
+ *
47
+ * @param code - Plaintext recovery code (e.g., "ABCD-1234")
48
+ * @returns Argon2id hash string
49
+ */
50
+ export async function hashRecoveryCode(code) {
51
+ const normalized = normalizeCode(code);
52
+ return hash(normalized);
53
+ }
54
+ /**
55
+ * Verify a recovery code against an Argon2id hash.
56
+ *
57
+ * The code is normalized before verification so that "abcd-1234",
58
+ * "ABCD1234", and "ABCD-1234" are all equivalent.
59
+ *
60
+ * @param code - Plaintext recovery code to verify
61
+ * @param codeHash - Stored Argon2id hash
62
+ * @returns True if the code matches the hash
63
+ */
64
+ export async function verifyRecoveryCode(code, codeHash) {
65
+ const normalized = normalizeCode(code);
66
+ try {
67
+ return await verify(codeHash, normalized);
68
+ }
69
+ catch {
70
+ // Argon2id verify throws on malformed hashes — treat as non-match
71
+ return false;
72
+ }
73
+ }
74
+ /**
75
+ * Generate a single recovery code in XXXX-XXXX format.
76
+ *
77
+ * Uses crypto.randomBytes to generate random indices into the
78
+ * character set, then formats as two 4-character groups.
79
+ */
80
+ function generateSingleCode() {
81
+ const totalLength = HALF_LENGTH * 2;
82
+ const bytes = randomBytes(totalLength);
83
+ let code = '';
84
+ for (let i = 0; i < totalLength; i++) {
85
+ // Use modulo to map each random byte to a character index.
86
+ // Slight bias is negligible for this use case (recovery codes
87
+ // don't need perfectly uniform distribution).
88
+ code += CODE_CHARS[bytes[i] % CODE_CHARS.length];
89
+ }
90
+ // Insert dash between halves: "ABCD1234" → "ABCD-1234"
91
+ return `${code.slice(0, HALF_LENGTH)}-${code.slice(HALF_LENGTH)}`;
92
+ }
93
+ /**
94
+ * Normalize a recovery code for hashing/verification.
95
+ * Converts to uppercase and strips dashes so that entry format
96
+ * doesn't matter ("abcd-1234" === "ABCD1234" === "ABCD-1234").
97
+ *
98
+ * @param code - Raw recovery code input
99
+ * @returns Normalized uppercase string without dashes
100
+ */
101
+ function normalizeCode(code) {
102
+ return code.toUpperCase().replace(/-/g, '');
103
+ }
104
+ //# sourceMappingURL=recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../src/two-factor/recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,oDAAoD;AACpD,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAEvC;;;GAGG;AACH,MAAM,UAAU,GAAG,iCAAiC,CAAC;AAErD,0DAA0D;AAC1D,MAAM,WAAW,GAAG,CAAC,CAAC;AAEtB;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAK,GAAW,2BAA2B;IAC/E,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY;IACjD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAY,EAAE,QAAgB;IACrE,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,kEAAkE;QAClE,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,kBAAkB;IACzB,MAAM,WAAW,GAAG,WAAW,GAAG,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IACvC,IAAI,IAAI,GAAG,EAAE,CAAC;IAEd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,2DAA2D;QAC3D,8DAA8D;QAC9D,8CAA8C;QAC9C,IAAI,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC;IAED,uDAAuD;IACvD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;AACpE,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Two-factor authentication repository — PostgreSQL data access layer.
3
+ *
4
+ * Provides CRUD operations for the three 2FA tables:
5
+ * - user_totp: TOTP configuration per user (encrypted secrets)
6
+ * - two_factor_otp_codes: Email OTP codes (SHA-256 hashed, short-lived)
7
+ * - two_factor_recovery_codes: Single-use recovery codes (Argon2id hashed)
8
+ *
9
+ * Each function acquires the pool via `getPool()` and returns mapped
10
+ * objects using the mapping functions from types.ts. Follows the same
11
+ * patterns as the organization and user repositories.
12
+ */
13
+ import type { UserTotp, OtpCode, RecoveryCode, InsertTotpData } from './types.js';
14
+ /**
15
+ * Insert a new TOTP configuration for a user.
16
+ *
17
+ * The encrypted secret, IV, and auth tag are stored as-is (hex-encoded
18
+ * strings from the crypto module). The UNIQUE constraint on user_id
19
+ * ensures only one TOTP config per user.
20
+ *
21
+ * @param data - TOTP configuration data (secrets already encrypted)
22
+ * @returns The newly created TOTP configuration
23
+ * @throws If user already has a TOTP config (unique constraint violation)
24
+ */
25
+ export declare function insertTotp(data: InsertTotpData): Promise<UserTotp>;
26
+ /**
27
+ * Find TOTP configuration for a user.
28
+ *
29
+ * Returns the encrypted TOTP config if one exists, or null.
30
+ * The caller must decrypt the secret using the crypto module.
31
+ *
32
+ * @param userId - User UUID
33
+ * @returns TOTP configuration or null if not configured
34
+ */
35
+ export declare function findTotpByUserId(userId: string): Promise<UserTotp | null>;
36
+ /**
37
+ * Mark a user's TOTP configuration as verified.
38
+ *
39
+ * Called after the user successfully enters their first TOTP code
40
+ * during setup. This confirms that the user's authenticator app
41
+ * is correctly configured.
42
+ *
43
+ * @param userId - User UUID
44
+ */
45
+ export declare function markTotpVerified(userId: string): Promise<void>;
46
+ /**
47
+ * Delete a user's TOTP configuration.
48
+ *
49
+ * Called when disabling 2FA or resetting TOTP setup.
50
+ * The CASCADE on user_id means this is also cleaned up if the user is deleted.
51
+ *
52
+ * @param userId - User UUID
53
+ */
54
+ export declare function deleteTotp(userId: string): Promise<void>;
55
+ /**
56
+ * Insert a new email OTP code.
57
+ *
58
+ * The code is stored as a SHA-256 hash — the plaintext is only sent
59
+ * via email. The expires_at timestamp determines when the code becomes
60
+ * invalid (typically 10 minutes from creation).
61
+ *
62
+ * @param userId - User UUID
63
+ * @param codeHash - SHA-256 hex hash of the 6-digit OTP code
64
+ * @param expiresAt - Expiration timestamp
65
+ * @returns The newly created OTP code record
66
+ */
67
+ export declare function insertOtpCode(userId: string, codeHash: string, expiresAt: Date): Promise<OtpCode>;
68
+ /**
69
+ * Find all active (unused, not expired) OTP codes for a user.
70
+ *
71
+ * Returns codes that are both unused (used_at IS NULL) and not yet
72
+ * expired (expires_at > NOW()). Ordered by creation time descending
73
+ * so the most recent code is first.
74
+ *
75
+ * @param userId - User UUID
76
+ * @returns Array of active OTP codes (may be empty)
77
+ */
78
+ export declare function findActiveOtpCodes(userId: string): Promise<OtpCode[]>;
79
+ /**
80
+ * Mark an OTP code as used.
81
+ *
82
+ * Sets the used_at timestamp to the current time, preventing reuse.
83
+ * Called after successful verification.
84
+ *
85
+ * @param codeId - OTP code UUID
86
+ */
87
+ export declare function markOtpCodeUsed(codeId: string): Promise<void>;
88
+ /**
89
+ * Delete expired OTP codes for a user.
90
+ *
91
+ * Cleans up codes that have passed their expires_at timestamp or
92
+ * have already been used. Prevents table bloat over time.
93
+ *
94
+ * @param userId - User UUID
95
+ * @returns Number of codes deleted
96
+ */
97
+ export declare function deleteExpiredOtpCodes(userId: string): Promise<number>;
98
+ /**
99
+ * Count active (unused, not expired) OTP codes for a user.
100
+ *
101
+ * Used for rate limiting — prevents generating too many codes
102
+ * in a short time period.
103
+ *
104
+ * @param userId - User UUID
105
+ * @returns Number of active OTP codes
106
+ */
107
+ export declare function countActiveOtpCodes(userId: string): Promise<number>;
108
+ /**
109
+ * Insert a batch of recovery codes for a user.
110
+ *
111
+ * Inserts all code hashes in a single query using a multi-row VALUES
112
+ * clause for efficiency. Called during 2FA setup and regeneration.
113
+ *
114
+ * @param userId - User UUID
115
+ * @param codeHashes - Array of Argon2id hashes of recovery codes
116
+ */
117
+ export declare function insertRecoveryCodes(userId: string, codeHashes: string[]): Promise<void>;
118
+ /**
119
+ * Find all unused recovery codes for a user.
120
+ *
121
+ * Returns codes where used_at IS NULL. The caller iterates these
122
+ * and verifies each hash to find a match (since Argon2id hashes
123
+ * include random salt, we can't do a direct DB lookup).
124
+ *
125
+ * @param userId - User UUID
126
+ * @returns Array of unused recovery codes (may be empty)
127
+ */
128
+ export declare function findUnusedRecoveryCodes(userId: string): Promise<RecoveryCode[]>;
129
+ /**
130
+ * Mark a recovery code as used.
131
+ *
132
+ * Sets the used_at timestamp to the current time, preventing reuse.
133
+ * Recovery codes are single-use — once used, they cannot be used again.
134
+ *
135
+ * @param codeId - Recovery code UUID
136
+ */
137
+ export declare function markRecoveryCodeUsed(codeId: string): Promise<void>;
138
+ /**
139
+ * Delete all recovery codes for a user.
140
+ *
141
+ * Called when disabling 2FA or regenerating recovery codes
142
+ * (old codes are deleted, then new ones are inserted).
143
+ *
144
+ * @param userId - User UUID
145
+ */
146
+ export declare function deleteAllRecoveryCodes(userId: string): Promise<void>;
147
+ /**
148
+ * Count unused recovery codes for a user.
149
+ *
150
+ * Used in the 2FA status response to show how many codes remain.
151
+ *
152
+ * @param userId - User UUID
153
+ * @returns Number of unused recovery codes
154
+ */
155
+ export declare function countUnusedRecoveryCodes(userId: string): Promise<number>;
156
+ //# sourceMappingURL=repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/two-factor/repository.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EACV,QAAQ,EAER,OAAO,EAEP,YAAY,EAEZ,cAAc,EACf,MAAM,YAAY,CAAC;AAWpB;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC,CAsBxE;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAU/E;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpE;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO9D;AAMD;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,IAAI,GACd,OAAO,CAAC,OAAO,CAAC,CAWlB;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAW3E;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOnE;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAU3E;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUzE;AAMD;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAWrF;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOxE;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO1E;AAED;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAU9E"}
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Two-factor authentication repository — PostgreSQL data access layer.
3
+ *
4
+ * Provides CRUD operations for the three 2FA tables:
5
+ * - user_totp: TOTP configuration per user (encrypted secrets)
6
+ * - two_factor_otp_codes: Email OTP codes (SHA-256 hashed, short-lived)
7
+ * - two_factor_recovery_codes: Single-use recovery codes (Argon2id hashed)
8
+ *
9
+ * Each function acquires the pool via `getPool()` and returns mapped
10
+ * objects using the mapping functions from types.ts. Follows the same
11
+ * patterns as the organization and user repositories.
12
+ */
13
+ import { getPool } from '../lib/database.js';
14
+ import { mapRowToUserTotp, mapRowToOtpCode, mapRowToRecoveryCode, } from './types.js';
15
+ // ===========================================================================
16
+ // TOTP operations
17
+ // ===========================================================================
18
+ /**
19
+ * Insert a new TOTP configuration for a user.
20
+ *
21
+ * The encrypted secret, IV, and auth tag are stored as-is (hex-encoded
22
+ * strings from the crypto module). The UNIQUE constraint on user_id
23
+ * ensures only one TOTP config per user.
24
+ *
25
+ * @param data - TOTP configuration data (secrets already encrypted)
26
+ * @returns The newly created TOTP configuration
27
+ * @throws If user already has a TOTP config (unique constraint violation)
28
+ */
29
+ export async function insertTotp(data) {
30
+ const pool = getPool();
31
+ const result = await pool.query(`INSERT INTO user_totp (
32
+ user_id, encrypted_secret, encryption_iv, encryption_tag,
33
+ algorithm, digits, period
34
+ )
35
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
36
+ RETURNING *`, [
37
+ data.userId,
38
+ data.encryptedSecret,
39
+ data.encryptionIv,
40
+ data.encryptionTag,
41
+ data.algorithm ?? 'SHA1',
42
+ data.digits ?? 6,
43
+ data.period ?? 30,
44
+ ]);
45
+ return mapRowToUserTotp(result.rows[0]);
46
+ }
47
+ /**
48
+ * Find TOTP configuration for a user.
49
+ *
50
+ * Returns the encrypted TOTP config if one exists, or null.
51
+ * The caller must decrypt the secret using the crypto module.
52
+ *
53
+ * @param userId - User UUID
54
+ * @returns TOTP configuration or null if not configured
55
+ */
56
+ export async function findTotpByUserId(userId) {
57
+ const pool = getPool();
58
+ const result = await pool.query('SELECT * FROM user_totp WHERE user_id = $1', [userId]);
59
+ if (result.rows.length === 0)
60
+ return null;
61
+ return mapRowToUserTotp(result.rows[0]);
62
+ }
63
+ /**
64
+ * Mark a user's TOTP configuration as verified.
65
+ *
66
+ * Called after the user successfully enters their first TOTP code
67
+ * during setup. This confirms that the user's authenticator app
68
+ * is correctly configured.
69
+ *
70
+ * @param userId - User UUID
71
+ */
72
+ export async function markTotpVerified(userId) {
73
+ const pool = getPool();
74
+ await pool.query('UPDATE user_totp SET verified = true WHERE user_id = $1', [userId]);
75
+ }
76
+ /**
77
+ * Delete a user's TOTP configuration.
78
+ *
79
+ * Called when disabling 2FA or resetting TOTP setup.
80
+ * The CASCADE on user_id means this is also cleaned up if the user is deleted.
81
+ *
82
+ * @param userId - User UUID
83
+ */
84
+ export async function deleteTotp(userId) {
85
+ const pool = getPool();
86
+ await pool.query('DELETE FROM user_totp WHERE user_id = $1', [userId]);
87
+ }
88
+ // ===========================================================================
89
+ // OTP code operations
90
+ // ===========================================================================
91
+ /**
92
+ * Insert a new email OTP code.
93
+ *
94
+ * The code is stored as a SHA-256 hash — the plaintext is only sent
95
+ * via email. The expires_at timestamp determines when the code becomes
96
+ * invalid (typically 10 minutes from creation).
97
+ *
98
+ * @param userId - User UUID
99
+ * @param codeHash - SHA-256 hex hash of the 6-digit OTP code
100
+ * @param expiresAt - Expiration timestamp
101
+ * @returns The newly created OTP code record
102
+ */
103
+ export async function insertOtpCode(userId, codeHash, expiresAt) {
104
+ const pool = getPool();
105
+ const result = await pool.query(`INSERT INTO two_factor_otp_codes (user_id, code_hash, expires_at)
106
+ VALUES ($1, $2, $3)
107
+ RETURNING *`, [userId, codeHash, expiresAt]);
108
+ return mapRowToOtpCode(result.rows[0]);
109
+ }
110
+ /**
111
+ * Find all active (unused, not expired) OTP codes for a user.
112
+ *
113
+ * Returns codes that are both unused (used_at IS NULL) and not yet
114
+ * expired (expires_at > NOW()). Ordered by creation time descending
115
+ * so the most recent code is first.
116
+ *
117
+ * @param userId - User UUID
118
+ * @returns Array of active OTP codes (may be empty)
119
+ */
120
+ export async function findActiveOtpCodes(userId) {
121
+ const pool = getPool();
122
+ const result = await pool.query(`SELECT * FROM two_factor_otp_codes
123
+ WHERE user_id = $1 AND used_at IS NULL AND expires_at > NOW()
124
+ ORDER BY created_at DESC`, [userId]);
125
+ return result.rows.map(mapRowToOtpCode);
126
+ }
127
+ /**
128
+ * Mark an OTP code as used.
129
+ *
130
+ * Sets the used_at timestamp to the current time, preventing reuse.
131
+ * Called after successful verification.
132
+ *
133
+ * @param codeId - OTP code UUID
134
+ */
135
+ export async function markOtpCodeUsed(codeId) {
136
+ const pool = getPool();
137
+ await pool.query('UPDATE two_factor_otp_codes SET used_at = NOW() WHERE id = $1', [codeId]);
138
+ }
139
+ /**
140
+ * Delete expired OTP codes for a user.
141
+ *
142
+ * Cleans up codes that have passed their expires_at timestamp or
143
+ * have already been used. Prevents table bloat over time.
144
+ *
145
+ * @param userId - User UUID
146
+ * @returns Number of codes deleted
147
+ */
148
+ export async function deleteExpiredOtpCodes(userId) {
149
+ const pool = getPool();
150
+ const result = await pool.query(`DELETE FROM two_factor_otp_codes
151
+ WHERE user_id = $1 AND (expires_at <= NOW() OR used_at IS NOT NULL)`, [userId]);
152
+ return result.rowCount ?? 0;
153
+ }
154
+ /**
155
+ * Count active (unused, not expired) OTP codes for a user.
156
+ *
157
+ * Used for rate limiting — prevents generating too many codes
158
+ * in a short time period.
159
+ *
160
+ * @param userId - User UUID
161
+ * @returns Number of active OTP codes
162
+ */
163
+ export async function countActiveOtpCodes(userId) {
164
+ const pool = getPool();
165
+ const result = await pool.query(`SELECT COUNT(*) as count FROM two_factor_otp_codes
166
+ WHERE user_id = $1 AND used_at IS NULL AND expires_at > NOW()`, [userId]);
167
+ return parseInt(result.rows[0].count, 10);
168
+ }
169
+ // ===========================================================================
170
+ // Recovery code operations
171
+ // ===========================================================================
172
+ /**
173
+ * Insert a batch of recovery codes for a user.
174
+ *
175
+ * Inserts all code hashes in a single query using a multi-row VALUES
176
+ * clause for efficiency. Called during 2FA setup and regeneration.
177
+ *
178
+ * @param userId - User UUID
179
+ * @param codeHashes - Array of Argon2id hashes of recovery codes
180
+ */
181
+ export async function insertRecoveryCodes(userId, codeHashes) {
182
+ if (codeHashes.length === 0)
183
+ return;
184
+ const pool = getPool();
185
+ // Build multi-row VALUES clause: ($1, $2), ($1, $3), ($1, $4), ...
186
+ // $1 is always the userId; each hash gets its own parameter
187
+ const valuePlaceholders = [];
188
+ const params = [userId];
189
+ for (let i = 0; i < codeHashes.length; i++) {
190
+ valuePlaceholders.push(`($1, $${i + 2})`);
191
+ params.push(codeHashes[i]);
192
+ }
193
+ await pool.query(`INSERT INTO two_factor_recovery_codes (user_id, code_hash)
194
+ VALUES ${valuePlaceholders.join(', ')}`, params);
195
+ }
196
+ /**
197
+ * Find all unused recovery codes for a user.
198
+ *
199
+ * Returns codes where used_at IS NULL. The caller iterates these
200
+ * and verifies each hash to find a match (since Argon2id hashes
201
+ * include random salt, we can't do a direct DB lookup).
202
+ *
203
+ * @param userId - User UUID
204
+ * @returns Array of unused recovery codes (may be empty)
205
+ */
206
+ export async function findUnusedRecoveryCodes(userId) {
207
+ const pool = getPool();
208
+ const result = await pool.query(`SELECT * FROM two_factor_recovery_codes
209
+ WHERE user_id = $1 AND used_at IS NULL
210
+ ORDER BY created_at ASC`, [userId]);
211
+ return result.rows.map(mapRowToRecoveryCode);
212
+ }
213
+ /**
214
+ * Mark a recovery code as used.
215
+ *
216
+ * Sets the used_at timestamp to the current time, preventing reuse.
217
+ * Recovery codes are single-use — once used, they cannot be used again.
218
+ *
219
+ * @param codeId - Recovery code UUID
220
+ */
221
+ export async function markRecoveryCodeUsed(codeId) {
222
+ const pool = getPool();
223
+ await pool.query('UPDATE two_factor_recovery_codes SET used_at = NOW() WHERE id = $1', [codeId]);
224
+ }
225
+ /**
226
+ * Delete all recovery codes for a user.
227
+ *
228
+ * Called when disabling 2FA or regenerating recovery codes
229
+ * (old codes are deleted, then new ones are inserted).
230
+ *
231
+ * @param userId - User UUID
232
+ */
233
+ export async function deleteAllRecoveryCodes(userId) {
234
+ const pool = getPool();
235
+ await pool.query('DELETE FROM two_factor_recovery_codes WHERE user_id = $1', [userId]);
236
+ }
237
+ /**
238
+ * Count unused recovery codes for a user.
239
+ *
240
+ * Used in the 2FA status response to show how many codes remain.
241
+ *
242
+ * @param userId - User UUID
243
+ * @returns Number of unused recovery codes
244
+ */
245
+ export async function countUnusedRecoveryCodes(userId) {
246
+ const pool = getPool();
247
+ const result = await pool.query(`SELECT COUNT(*) as count FROM two_factor_recovery_codes
248
+ WHERE user_id = $1 AND used_at IS NULL`, [userId]);
249
+ return parseInt(result.rows[0].count, 10);
250
+ }
251
+ //# sourceMappingURL=repository.js.map