@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,206 @@
1
+ /**
2
+ * Signing key management for the OIDC provider.
3
+ *
4
+ * Handles loading ES256 key pairs from the database, converting between
5
+ * PEM and JWK formats, and generating new key pairs for bootstrapping
6
+ * and rotation.
7
+ *
8
+ * Key format:
9
+ * - Database stores PEM-encoded keys (public_key, private_key columns)
10
+ * - node-oidc-provider requires JWK format (jwks.keys configuration)
11
+ * - Node.js crypto module handles PEM ↔ JWK conversion
12
+ *
13
+ * Key lifecycle:
14
+ * active → Used for signing new tokens, published in JWKS
15
+ * retired → Not used for signing, still published in JWKS for verification
16
+ * revoked → Not loaded, not published
17
+ */
18
+ import { createPrivateKey, generateKeyPairSync, createHash } from 'node:crypto';
19
+ import { config } from '../config/index.js';
20
+ import { getPool } from './database.js';
21
+ import { logger } from './logger.js';
22
+ import { encryptPrivateKey, decryptPrivateKey } from './signing-key-crypto.js';
23
+ // ---------------------------------------------------------------------------
24
+ // Cached JWKS for JWT verification (admin auth middleware, etc.)
25
+ // ---------------------------------------------------------------------------
26
+ /** In-memory cache for the active JWK set — avoids a DB round-trip per request */
27
+ let cachedJwks = null;
28
+ let jwksCacheTimestamp = 0;
29
+ /** Cache TTL for the active JWK set — 60 seconds matches system-config cache */
30
+ const JWKS_CACHE_TTL_MS = 60_000;
31
+ /**
32
+ * Get the active JWK set (public + private keys) for JWT verification.
33
+ *
34
+ * Loads signing keys from the database on first call, then serves from
35
+ * an in-memory cache for 60 seconds. Used by the admin auth middleware
36
+ * to verify Bearer tokens without a DB query on every request.
37
+ *
38
+ * @returns JWK key set containing all active and retired keys
39
+ */
40
+ export async function getActiveJwks() {
41
+ const now = Date.now();
42
+ if (cachedJwks && now - jwksCacheTimestamp < JWKS_CACHE_TTL_MS) {
43
+ return cachedJwks;
44
+ }
45
+ const records = await loadSigningKeysFromDb();
46
+ cachedJwks = signingKeysToJwks(records);
47
+ jwksCacheTimestamp = now;
48
+ return cachedJwks;
49
+ }
50
+ /**
51
+ * Clear the cached JWK set — useful for testing and after key rotation.
52
+ */
53
+ export function clearJwksCache() {
54
+ cachedJwks = null;
55
+ jwksCacheTimestamp = 0;
56
+ }
57
+ /**
58
+ * Generate a new ES256 (ECDSA P-256) key pair.
59
+ *
60
+ * Creates a cryptographically secure key pair suitable for JWT signing.
61
+ * Returns PEM-encoded keys for database storage and a deterministic kid
62
+ * derived from the public key hash.
63
+ *
64
+ * @returns Object containing PEM keys, kid, and algorithm identifier
65
+ */
66
+ export function generateES256KeyPair() {
67
+ // Generate ECDSA P-256 key pair using Node.js crypto
68
+ const { publicKey, privateKey } = generateKeyPairSync('ec', {
69
+ namedCurve: 'P-256',
70
+ });
71
+ // Export as PEM format for database storage
72
+ const publicKeyPem = publicKey.export({ type: 'spki', format: 'pem' });
73
+ const privateKeyPem = privateKey.export({ type: 'pkcs8', format: 'pem' });
74
+ // Generate deterministic kid from public key hash (first 16 hex chars of SHA-256).
75
+ // This ensures uniqueness while keeping the kid short enough for JWKS headers.
76
+ const kid = createHash('sha256').update(publicKeyPem).digest('hex').substring(0, 16);
77
+ return {
78
+ kid,
79
+ algorithm: 'ES256',
80
+ publicKeyPem,
81
+ privateKeyPem,
82
+ };
83
+ }
84
+ /**
85
+ * Convert a PEM-encoded private key to JWK format for node-oidc-provider.
86
+ *
87
+ * Uses Node.js crypto module to parse PEM and export as JWK. The resulting
88
+ * JWK includes the private key (d parameter) which is needed by the provider
89
+ * for token signing.
90
+ *
91
+ * @param privatePem - PEM-encoded private key (PKCS#8 format)
92
+ * @param kid - Key ID to include in the JWK
93
+ * @returns JWK object with both public and private key parameters
94
+ */
95
+ export function pemToJwk(privatePem, kid) {
96
+ // Parse PEM into a Node.js KeyObject
97
+ const keyObject = createPrivateKey(privatePem);
98
+ // Export as JWK — includes both public (x, y) and private (d) parameters
99
+ const jwk = keyObject.export({ format: 'jwk' });
100
+ return {
101
+ ...jwk,
102
+ kid,
103
+ use: 'sig',
104
+ alg: 'ES256',
105
+ };
106
+ }
107
+ /**
108
+ * Convert signing key records from the database into JWK format
109
+ * suitable for node-oidc-provider's jwks.keys configuration.
110
+ *
111
+ * All active and retired keys are included — the provider uses the first key
112
+ * for signing and all keys for verification (supporting rotation).
113
+ *
114
+ * @param records - Array of signing key records from the database
115
+ * @returns JWK key set with keys array
116
+ */
117
+ export function signingKeysToJwks(records) {
118
+ const keys = [];
119
+ for (const record of records) {
120
+ try {
121
+ keys.push(pemToJwk(record.privateKey, record.kid));
122
+ }
123
+ catch (error) {
124
+ // Skip invalid PEM keys rather than crashing — log error and continue
125
+ // with remaining keys so the provider can still start
126
+ logger.error({ kid: record.kid, error }, 'Failed to convert signing key PEM to JWK, skipping');
127
+ }
128
+ }
129
+ return { keys };
130
+ }
131
+ /**
132
+ * Load signing keys from the database.
133
+ *
134
+ * Queries the signing_keys table for active and retired keys that
135
+ * haven't expired. Returns them sorted by activated_at DESC (newest first)
136
+ * so the provider uses the newest active key for signing.
137
+ *
138
+ * @returns Array of signing key records, newest first
139
+ */
140
+ export async function loadSigningKeysFromDb() {
141
+ const pool = getPool();
142
+ const result = await pool.query(`SELECT id, kid, algorithm, public_key, private_key,
143
+ private_key_iv, private_key_tag, encrypted,
144
+ status, activated_at, retired_at, expires_at
145
+ FROM signing_keys
146
+ WHERE status IN ('active', 'retired')
147
+ AND (expires_at IS NULL OR expires_at > NOW())
148
+ ORDER BY activated_at DESC`);
149
+ // Map snake_case DB columns to camelCase TypeScript interface.
150
+ // Decrypt private keys that are stored encrypted; pass through plaintext legacy keys.
151
+ return result.rows.map((row) => {
152
+ let privateKey;
153
+ if (row.encrypted && row.private_key_iv && row.private_key_tag) {
154
+ // Encrypted key — decrypt with AES-256-GCM
155
+ privateKey = decryptPrivateKey(row.private_key, row.private_key_iv, row.private_key_tag, config.signingKeyEncryptionKey);
156
+ }
157
+ else {
158
+ // Legacy plaintext key — use as-is
159
+ privateKey = row.private_key;
160
+ }
161
+ return {
162
+ id: row.id,
163
+ kid: row.kid,
164
+ algorithm: row.algorithm,
165
+ publicKey: row.public_key,
166
+ privateKey,
167
+ status: row.status,
168
+ activatedAt: row.activated_at,
169
+ retiredAt: row.retired_at,
170
+ expiresAt: row.expires_at,
171
+ };
172
+ });
173
+ }
174
+ /**
175
+ * Ensure at least one active signing key exists in the database.
176
+ *
177
+ * Called at application startup. If no active keys are found:
178
+ * 1. Generate a new ES256 key pair
179
+ * 2. Insert it into the signing_keys table with status='active'
180
+ * 3. Log a warning that a key was auto-generated
181
+ *
182
+ * This guarantees the OIDC provider can always start, even on a fresh database
183
+ * that has no signing keys yet (e.g., first run after migrations).
184
+ *
185
+ * @returns The JWK key set containing all active and retired keys
186
+ */
187
+ export async function ensureSigningKeys() {
188
+ let records = await loadSigningKeysFromDb();
189
+ // Check if there are any active keys
190
+ const hasActiveKey = records.some((r) => r.status === 'active');
191
+ if (!hasActiveKey) {
192
+ logger.warn('No active signing keys found — auto-generating a new ES256 key pair');
193
+ const keyPair = generateES256KeyPair();
194
+ const pool = getPool();
195
+ // Encrypt the private key before storage (AES-256-GCM)
196
+ const { encrypted, iv, tag } = encryptPrivateKey(keyPair.privateKeyPem, config.signingKeyEncryptionKey);
197
+ // Insert the new key with encrypted private key
198
+ await pool.query(`INSERT INTO signing_keys (kid, algorithm, public_key, private_key, private_key_iv, private_key_tag, encrypted, status, activated_at)
199
+ VALUES ($1, $2, $3, $4, $5, $6, true, 'active', NOW())`, [keyPair.kid, keyPair.algorithm, keyPair.publicKeyPem, encrypted, iv, tag]);
200
+ logger.info({ kid: keyPair.kid }, 'Auto-generated signing key inserted into database');
201
+ // Reload keys from DB to get the full record including generated UUID
202
+ records = await loadSigningKeysFromDb();
203
+ }
204
+ return signingKeysToJwks(records);
205
+ }
206
+ //# sourceMappingURL=signing-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signing-keys.js","sourceRoot":"","sources":["../../src/lib/signing-keys.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Dashboard statistics service.
3
+ *
4
+ * Provides system-wide and per-organization statistics for the admin
5
+ * dashboard. All functions are standalone (no class), use parameterized
6
+ * SQL queries, and return typed result objects.
7
+ *
8
+ * Login activity metrics are derived from the `audit_log` table using
9
+ * event type filters. Entity counts use `GROUP BY status` for efficient
10
+ * aggregation.
11
+ *
12
+ * @module stats
13
+ * @see 05-dashboard-sessions-history.md
14
+ */
15
+ /** Count breakdown by entity status */
16
+ export interface StatusCounts {
17
+ total: number;
18
+ [status: string]: number;
19
+ }
20
+ /** Login activity for a time window */
21
+ export interface LoginActivity {
22
+ successful: number;
23
+ failed: number;
24
+ }
25
+ /** System health check results */
26
+ export interface SystemHealth {
27
+ database: boolean;
28
+ redis: boolean;
29
+ }
30
+ /** Full stats overview response */
31
+ export interface StatsOverview {
32
+ organizations: StatusCounts;
33
+ users: StatusCounts & {
34
+ newLast7d: number;
35
+ newLast30d: number;
36
+ activeLast30d: number;
37
+ };
38
+ applications: StatusCounts;
39
+ clients: StatusCounts;
40
+ loginActivity: {
41
+ last24h: LoginActivity;
42
+ last7d: LoginActivity;
43
+ last30d: LoginActivity;
44
+ };
45
+ systemHealth: SystemHealth;
46
+ generatedAt: string;
47
+ }
48
+ /** Per-org stats response (scoped to a single organization) */
49
+ export interface OrgStats {
50
+ organizationId: string;
51
+ users: StatusCounts & {
52
+ newLast7d: number;
53
+ newLast30d: number;
54
+ activeLast30d: number;
55
+ };
56
+ clients: StatusCounts;
57
+ loginActivity: {
58
+ last24h: LoginActivity;
59
+ last7d: LoginActivity;
60
+ last30d: LoginActivity;
61
+ };
62
+ generatedAt: string;
63
+ }
64
+ /**
65
+ * Get system-wide dashboard statistics.
66
+ *
67
+ * Aggregates counts across all organizations, user activity metrics,
68
+ * login event history from the audit log, and system health checks.
69
+ *
70
+ * @returns Full stats overview
71
+ */
72
+ export declare function getStatsOverview(): Promise<StatsOverview>;
73
+ /**
74
+ * Get per-organization dashboard statistics.
75
+ *
76
+ * All counts are scoped to the specified organization. Login activity
77
+ * is filtered by `organization_id` in the audit log.
78
+ *
79
+ * @param orgId - Organization UUID to scope statistics to
80
+ * @returns Per-org stats
81
+ */
82
+ export declare function getOrgStats(orgId: string): Promise<OrgStats>;
83
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/lib/stats.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,uCAAuC;AACvC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,kCAAkC;AAClC,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,mCAAmC;AACnC,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,YAAY,CAAC;IAC5B,KAAK,EAAE,YAAY,GAAG;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IACvF,YAAY,EAAE,YAAY,CAAC;IAC3B,OAAO,EAAE,YAAY,CAAC;IACtB,aAAa,EAAE;QACb,OAAO,EAAE,aAAa,CAAC;QACvB,MAAM,EAAE,aAAa,CAAC;QACtB,OAAO,EAAE,aAAa,CAAC;KACxB,CAAC;IACF,YAAY,EAAE,YAAY,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,+DAA+D;AAC/D,MAAM,WAAW,QAAQ;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,YAAY,GAAG;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IACvF,OAAO,EAAE,YAAY,CAAC;IACtB,aAAa,EAAE;QACb,OAAO,EAAE,aAAa,CAAC;QACvB,MAAM,EAAE,aAAa,CAAC;QACtB,OAAO,EAAE,aAAa,CAAC;KACxB,CAAC;IACF,WAAW,EAAE,MAAM,CAAC;CACrB;AAgHD;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC,CAkD/D;AAED;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqClE"}
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Dashboard statistics service.
3
+ *
4
+ * Provides system-wide and per-organization statistics for the admin
5
+ * dashboard. All functions are standalone (no class), use parameterized
6
+ * SQL queries, and return typed result objects.
7
+ *
8
+ * Login activity metrics are derived from the `audit_log` table using
9
+ * event type filters. Entity counts use `GROUP BY status` for efficient
10
+ * aggregation.
11
+ *
12
+ * @module stats
13
+ * @see 05-dashboard-sessions-history.md
14
+ */
15
+ import { getPool } from './database.js';
16
+ // ============================================================================
17
+ // Internal helpers
18
+ // ============================================================================
19
+ /**
20
+ * Count rows grouped by status for an entity table.
21
+ * Returns { total, active, suspended, ... } with all statuses found.
22
+ */
23
+ async function countByStatus(table, orgFilter) {
24
+ const pool = getPool();
25
+ const conditions = orgFilter ? `WHERE ${orgFilter.column} = $1` : '';
26
+ const params = orgFilter ? [orgFilter.value] : [];
27
+ const { rows } = await pool.query(`SELECT status, COUNT(*)::text AS count FROM ${table} ${conditions} GROUP BY status`, params);
28
+ const result = { total: 0 };
29
+ for (const row of rows) {
30
+ const count = parseInt(row.count, 10);
31
+ result[row.status] = count;
32
+ result.total += count;
33
+ }
34
+ return result;
35
+ }
36
+ /**
37
+ * Count new users created within a given interval.
38
+ */
39
+ async function countNewUsers(interval, orgId) {
40
+ const pool = getPool();
41
+ const orgFilter = orgId ? ' AND organization_id = $1' : '';
42
+ const params = orgId ? [orgId] : [];
43
+ const { rows } = await pool.query(`SELECT COUNT(*)::text AS count FROM users WHERE created_at > NOW() - $${orgId ? '2' : '1'}::interval${orgFilter}`, [...params, interval]);
44
+ return parseInt(rows[0].count, 10);
45
+ }
46
+ /**
47
+ * Count users active (logged in) within a given interval.
48
+ */
49
+ async function countActiveUsers(interval, orgId) {
50
+ const pool = getPool();
51
+ const orgFilter = orgId ? ' AND organization_id = $1' : '';
52
+ const params = orgId ? [orgId] : [];
53
+ const { rows } = await pool.query(`SELECT COUNT(*)::text AS count FROM users WHERE last_login_at > NOW() - $${orgId ? '2' : '1'}::interval${orgFilter}`, [...params, interval]);
54
+ return parseInt(rows[0].count, 10);
55
+ }
56
+ /**
57
+ * Count login events from audit_log for a time window.
58
+ * Successful: event_type LIKE 'user.login.%' AND NOT LIKE '%.failed'
59
+ * Failed: event_type LIKE 'user.login.%.failed'
60
+ */
61
+ async function countLoginActivity(interval, orgId) {
62
+ const pool = getPool();
63
+ const orgFilter = orgId ? ' AND organization_id = $1' : '';
64
+ const baseParams = orgId ? [orgId] : [];
65
+ const intervalParam = `$${orgId ? '2' : '1'}`;
66
+ const { rows } = await pool.query(`SELECT
67
+ CASE
68
+ WHEN event_type LIKE '%.failed' THEN 'failed'
69
+ ELSE 'successful'
70
+ END AS category,
71
+ COUNT(*)::text AS count
72
+ FROM audit_log
73
+ WHERE event_type LIKE 'user.login.%'
74
+ AND created_at > NOW() - ${intervalParam}::interval
75
+ ${orgFilter}
76
+ GROUP BY category`, [...baseParams, interval]);
77
+ const result = { successful: 0, failed: 0 };
78
+ for (const row of rows) {
79
+ if (row.category === 'successful')
80
+ result.successful = parseInt(row.count, 10);
81
+ if (row.category === 'failed')
82
+ result.failed = parseInt(row.count, 10);
83
+ }
84
+ return result;
85
+ }
86
+ /**
87
+ * Check system health (database connectivity).
88
+ * Redis health is checked separately since it may not be available
89
+ * from a pure database context.
90
+ */
91
+ async function checkDatabaseHealth() {
92
+ try {
93
+ const pool = getPool();
94
+ await pool.query('SELECT 1');
95
+ return true;
96
+ }
97
+ catch {
98
+ return false;
99
+ }
100
+ }
101
+ // ============================================================================
102
+ // Public API
103
+ // ============================================================================
104
+ /**
105
+ * Get system-wide dashboard statistics.
106
+ *
107
+ * Aggregates counts across all organizations, user activity metrics,
108
+ * login event history from the audit log, and system health checks.
109
+ *
110
+ * @returns Full stats overview
111
+ */
112
+ export async function getStatsOverview() {
113
+ const [organizations, usersBase, applications, clients, newUsers7d, newUsers30d, activeUsers30d, loginLast24h, loginLast7d, loginLast30d, dbHealth,] = await Promise.all([
114
+ countByStatus('organizations'),
115
+ countByStatus('users'),
116
+ countByStatus('applications'),
117
+ countByStatus('clients'),
118
+ countNewUsers('7 days'),
119
+ countNewUsers('30 days'),
120
+ countActiveUsers('30 days'),
121
+ countLoginActivity('24 hours'),
122
+ countLoginActivity('7 days'),
123
+ countLoginActivity('30 days'),
124
+ checkDatabaseHealth(),
125
+ ]);
126
+ return {
127
+ organizations,
128
+ users: {
129
+ ...usersBase,
130
+ newLast7d: newUsers7d,
131
+ newLast30d: newUsers30d,
132
+ activeLast30d: activeUsers30d,
133
+ },
134
+ applications,
135
+ clients,
136
+ loginActivity: {
137
+ last24h: loginLast24h,
138
+ last7d: loginLast7d,
139
+ last30d: loginLast30d,
140
+ },
141
+ systemHealth: {
142
+ database: dbHealth,
143
+ // Redis health is checked via the health endpoint; we report true here
144
+ // since if we got this far the DB is reachable. Full health check is at GET /health.
145
+ redis: true,
146
+ },
147
+ generatedAt: new Date().toISOString(),
148
+ };
149
+ }
150
+ /**
151
+ * Get per-organization dashboard statistics.
152
+ *
153
+ * All counts are scoped to the specified organization. Login activity
154
+ * is filtered by `organization_id` in the audit log.
155
+ *
156
+ * @param orgId - Organization UUID to scope statistics to
157
+ * @returns Per-org stats
158
+ */
159
+ export async function getOrgStats(orgId) {
160
+ const [usersBase, clientsCounts, newUsers7d, newUsers30d, activeUsers30d, loginLast24h, loginLast7d, loginLast30d,] = await Promise.all([
161
+ countByStatus('users', { column: 'organization_id', value: orgId }),
162
+ countByStatus('clients', { column: 'organization_id', value: orgId }),
163
+ countNewUsers('7 days', orgId),
164
+ countNewUsers('30 days', orgId),
165
+ countActiveUsers('30 days', orgId),
166
+ countLoginActivity('24 hours', orgId),
167
+ countLoginActivity('7 days', orgId),
168
+ countLoginActivity('30 days', orgId),
169
+ ]);
170
+ return {
171
+ organizationId: orgId,
172
+ users: {
173
+ ...usersBase,
174
+ newLast7d: newUsers7d,
175
+ newLast30d: newUsers30d,
176
+ activeLast30d: activeUsers30d,
177
+ },
178
+ clients: clientsCounts,
179
+ loginActivity: {
180
+ last24h: loginLast24h,
181
+ last7d: loginLast7d,
182
+ last30d: loginLast30d,
183
+ },
184
+ generatedAt: new Date().toISOString(),
185
+ };
186
+ }
187
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/lib/stats.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Super-admin user protection module.
3
+ *
4
+ * The super-admin user (created during `porta init`) is the system bootstrap
5
+ * user and cannot be subjected to destructive operations. This module provides
6
+ * detection and guard functions to enforce this protection.
7
+ *
8
+ * The super-admin user ID is stored in `system_config` under the key
9
+ * `super_admin_user_id` during `porta init`. This is the single source
10
+ * of truth for super-admin detection.
11
+ *
12
+ * Protected operations (all throw 403 Forbidden):
13
+ * - Delete
14
+ * - Suspend
15
+ * - Lock
16
+ * - Deactivate
17
+ * - Remove porta-super-admin role
18
+ * - Manage 2FA (disable, reset, regenerate recovery codes)
19
+ *
20
+ * @module lib/super-admin-protection
21
+ */
22
+ /** System config key where the super-admin user ID is stored */
23
+ export declare const SUPER_ADMIN_USER_ID_KEY = "super_admin_user_id";
24
+ /**
25
+ * Operations that are forbidden on the super-admin user.
26
+ * Used in error messages to clearly communicate what was attempted.
27
+ */
28
+ export declare const PROTECTED_OPERATIONS: readonly ['delete', 'suspend', 'lock', 'deactivate', 'remove-super-admin-role', 'manage-2fa'];
29
+ export type ProtectedOperation = (typeof PROTECTED_OPERATIONS)[number];
30
+ /**
31
+ * Check if a user is the protected super-admin user.
32
+ *
33
+ * Queries `system_config` for the `super_admin_user_id` key (with 60s cache)
34
+ * and compares it to the given user ID. Returns false if no super-admin user
35
+ * has been configured (i.e., before `porta init` has been run).
36
+ *
37
+ * @param userId - The user ID to check
38
+ * @returns true if the user is the super-admin
39
+ */
40
+ export declare function isSuperAdminUser(userId: string): Promise<boolean>;
41
+ /**
42
+ * Error thrown when a protected operation is attempted on the super-admin user.
43
+ * Carries a 403 status code for HTTP responses.
44
+ */
45
+ export declare class SuperAdminProtectionError extends Error {
46
+ /** HTTP status code (always 403) */
47
+ readonly status = 403;
48
+ /** The operation that was attempted */
49
+ readonly operation: ProtectedOperation;
50
+ constructor(operation: ProtectedOperation);
51
+ }
52
+ /**
53
+ * Guard function — throws 403 if attempting a forbidden action on the super-admin user.
54
+ *
55
+ * Call this before executing any destructive operation on a user:
56
+ * ```typescript
57
+ * await guardSuperAdmin(userId, 'suspend');
58
+ * // ... proceed with suspend logic
59
+ * ```
60
+ *
61
+ * If the user is not the super-admin, this function is a no-op.
62
+ * If the user IS the super-admin, it throws a `SuperAdminProtectionError`.
63
+ *
64
+ * @param userId - The user ID being acted upon
65
+ * @param operation - The operation being attempted
66
+ * @throws SuperAdminProtectionError if the user is the super-admin
67
+ */
68
+ export declare function guardSuperAdmin(userId: string, operation: ProtectedOperation): Promise<void>;
69
+ //# sourceMappingURL=super-admin-protection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"super-admin-protection.d.ts","sourceRoot":"","sources":["../../src/lib/super-admin-protection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAQH,gEAAgE;AAChE,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAE7D;;;GAGG;AACH,eAAO,MAAM,oBAAoB,YAC/B,QAAQ,EACR,SAAS,EACT,MAAM,EACN,YAAY,EACZ,yBAAyB,EACzB,YAAY,CACJ,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAMvE;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQvE;AAMD;;;GAGG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,oCAAoC;IACpC,QAAQ,CAAC,MAAM,OAAO;IACtB,uCAAuC;IACvC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IAEvC,YAAY,SAAS,EAAE,kBAAkB,EAIxC;CACF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,kBAAkB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAKf"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Super-admin user protection module.
3
+ *
4
+ * The super-admin user (created during `porta init`) is the system bootstrap
5
+ * user and cannot be subjected to destructive operations. This module provides
6
+ * detection and guard functions to enforce this protection.
7
+ *
8
+ * The super-admin user ID is stored in `system_config` under the key
9
+ * `super_admin_user_id` during `porta init`. This is the single source
10
+ * of truth for super-admin detection.
11
+ *
12
+ * Protected operations (all throw 403 Forbidden):
13
+ * - Delete
14
+ * - Suspend
15
+ * - Lock
16
+ * - Deactivate
17
+ * - Remove porta-super-admin role
18
+ * - Manage 2FA (disable, reset, regenerate recovery codes)
19
+ *
20
+ * @module lib/super-admin-protection
21
+ */
22
+ import { getSystemConfigString } from './system-config.js';
23
+ // ============================================================================
24
+ // Constants
25
+ // ============================================================================
26
+ /** System config key where the super-admin user ID is stored */
27
+ export const SUPER_ADMIN_USER_ID_KEY = 'super_admin_user_id';
28
+ /**
29
+ * Operations that are forbidden on the super-admin user.
30
+ * Used in error messages to clearly communicate what was attempted.
31
+ */
32
+ export const PROTECTED_OPERATIONS = [
33
+ 'delete',
34
+ 'suspend',
35
+ 'lock',
36
+ 'deactivate',
37
+ 'remove-super-admin-role',
38
+ 'manage-2fa',
39
+ ];
40
+ // ============================================================================
41
+ // Detection
42
+ // ============================================================================
43
+ /**
44
+ * Check if a user is the protected super-admin user.
45
+ *
46
+ * Queries `system_config` for the `super_admin_user_id` key (with 60s cache)
47
+ * and compares it to the given user ID. Returns false if no super-admin user
48
+ * has been configured (i.e., before `porta init` has been run).
49
+ *
50
+ * @param userId - The user ID to check
51
+ * @returns true if the user is the super-admin
52
+ */
53
+ export async function isSuperAdminUser(userId) {
54
+ // getSystemConfigString returns the fallback when the key doesn't exist.
55
+ // Empty string fallback means: if no super-admin is configured, nobody matches.
56
+ const superAdminUserId = await getSystemConfigString(SUPER_ADMIN_USER_ID_KEY, '');
57
+ if (!superAdminUserId) {
58
+ return false;
59
+ }
60
+ return superAdminUserId === userId;
61
+ }
62
+ // ============================================================================
63
+ // Guard
64
+ // ============================================================================
65
+ /**
66
+ * Error thrown when a protected operation is attempted on the super-admin user.
67
+ * Carries a 403 status code for HTTP responses.
68
+ */
69
+ export class SuperAdminProtectionError extends Error {
70
+ /** HTTP status code (always 403) */
71
+ status = 403;
72
+ /** The operation that was attempted */
73
+ operation;
74
+ constructor(operation) {
75
+ super(`Cannot ${operation} the super-admin user`);
76
+ this.name = 'SuperAdminProtectionError';
77
+ this.operation = operation;
78
+ }
79
+ }
80
+ /**
81
+ * Guard function — throws 403 if attempting a forbidden action on the super-admin user.
82
+ *
83
+ * Call this before executing any destructive operation on a user:
84
+ * ```typescript
85
+ * await guardSuperAdmin(userId, 'suspend');
86
+ * // ... proceed with suspend logic
87
+ * ```
88
+ *
89
+ * If the user is not the super-admin, this function is a no-op.
90
+ * If the user IS the super-admin, it throws a `SuperAdminProtectionError`.
91
+ *
92
+ * @param userId - The user ID being acted upon
93
+ * @param operation - The operation being attempted
94
+ * @throws SuperAdminProtectionError if the user is the super-admin
95
+ */
96
+ export async function guardSuperAdmin(userId, operation) {
97
+ const isProtected = await isSuperAdminUser(userId);
98
+ if (isProtected) {
99
+ throw new SuperAdminProtectionError(operation);
100
+ }
101
+ }
102
+ //# sourceMappingURL=super-admin-protection.js.map