@portaidentity/server 1.7.3 → 1.9.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 (494) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/applications/index.d.ts +2 -2
  3. package/dist/applications/index.d.ts.map +1 -1
  4. package/dist/applications/index.js +2 -2
  5. package/dist/applications/index.js.map +1 -1
  6. package/dist/applications/repository.d.ts +49 -4
  7. package/dist/applications/repository.d.ts.map +1 -1
  8. package/dist/applications/repository.js +148 -10
  9. package/dist/applications/repository.js.map +1 -1
  10. package/dist/applications/service.d.ts +35 -17
  11. package/dist/applications/service.d.ts.map +1 -1
  12. package/dist/applications/service.js +144 -35
  13. package/dist/applications/service.js.map +1 -1
  14. package/dist/applications/types.d.ts +1 -1
  15. package/dist/applications/types.d.ts.map +1 -1
  16. package/dist/applications/validators.d.ts +14 -0
  17. package/dist/applications/validators.d.ts.map +1 -0
  18. package/dist/applications/validators.js +14 -0
  19. package/dist/applications/validators.js.map +1 -0
  20. package/dist/auth/effective-branding.d.ts +35 -0
  21. package/dist/auth/effective-branding.d.ts.map +1 -0
  22. package/dist/auth/effective-branding.js +81 -0
  23. package/dist/auth/effective-branding.js.map +1 -0
  24. package/dist/auth/email-service.d.ts +3 -8
  25. package/dist/auth/email-service.d.ts.map +1 -1
  26. package/dist/auth/email-service.js +22 -30
  27. package/dist/auth/email-service.js.map +1 -1
  28. package/dist/auth/i18n.d.ts +5 -0
  29. package/dist/auth/i18n.d.ts.map +1 -1
  30. package/dist/auth/i18n.js +6 -5
  31. package/dist/auth/i18n.js.map +1 -1
  32. package/dist/auth/rate-limiter.d.ts +6 -10
  33. package/dist/auth/rate-limiter.d.ts.map +1 -1
  34. package/dist/auth/rate-limiter.js +12 -16
  35. package/dist/auth/rate-limiter.js.map +1 -1
  36. package/dist/auth/recovery-job-processor.d.ts.map +1 -1
  37. package/dist/auth/recovery-job-processor.js +1 -1
  38. package/dist/auth/recovery-job-processor.js.map +1 -1
  39. package/dist/auth/template-engine.d.ts +3 -1
  40. package/dist/auth/template-engine.d.ts.map +1 -1
  41. package/dist/auth/template-engine.js +1 -1
  42. package/dist/auth/template-engine.js.map +1 -1
  43. package/dist/auth/token-repository.d.ts +9 -3
  44. package/dist/auth/token-repository.d.ts.map +1 -1
  45. package/dist/auth/token-repository.js +17 -8
  46. package/dist/auth/token-repository.js.map +1 -1
  47. package/dist/cli/commands/init.d.ts +1 -1
  48. package/dist/cli/commands/init.d.ts.map +1 -1
  49. package/dist/cli/commands/init.js +12 -12
  50. package/dist/cli/commands/init.js.map +1 -1
  51. package/dist/cli/error-handler.d.ts +3 -2
  52. package/dist/cli/error-handler.d.ts.map +1 -1
  53. package/dist/cli/error-handler.js +9 -2
  54. package/dist/cli/error-handler.js.map +1 -1
  55. package/dist/cli/index.d.ts +1 -1
  56. package/dist/cli/index.d.ts.map +1 -1
  57. package/dist/cli/index.js +1 -1
  58. package/dist/cli/index.js.map +1 -1
  59. package/dist/clients/index.d.ts +1 -1
  60. package/dist/clients/index.js +1 -1
  61. package/dist/clients/repository.d.ts +24 -0
  62. package/dist/clients/repository.d.ts.map +1 -1
  63. package/dist/clients/repository.js +39 -0
  64. package/dist/clients/repository.js.map +1 -1
  65. package/dist/clients/secret-repository.d.ts +35 -8
  66. package/dist/clients/secret-repository.d.ts.map +1 -1
  67. package/dist/clients/secret-repository.js +69 -16
  68. package/dist/clients/secret-repository.js.map +1 -1
  69. package/dist/clients/secret-service.d.ts +5 -9
  70. package/dist/clients/secret-service.d.ts.map +1 -1
  71. package/dist/clients/secret-service.js +13 -22
  72. package/dist/clients/secret-service.js.map +1 -1
  73. package/dist/clients/service.d.ts +10 -13
  74. package/dist/clients/service.d.ts.map +1 -1
  75. package/dist/clients/service.js +121 -42
  76. package/dist/clients/service.js.map +1 -1
  77. package/dist/clients/types.d.ts +14 -1
  78. package/dist/clients/types.d.ts.map +1 -1
  79. package/dist/clients/types.js +1 -0
  80. package/dist/clients/types.js.map +1 -1
  81. package/dist/clients/validators.d.ts +83 -1
  82. package/dist/clients/validators.d.ts.map +1 -1
  83. package/dist/clients/validators.js +110 -0
  84. package/dist/clients/validators.js.map +1 -1
  85. package/dist/config/index.d.ts +1 -0
  86. package/dist/config/index.d.ts.map +1 -1
  87. package/dist/config/index.js +1 -0
  88. package/dist/config/index.js.map +1 -1
  89. package/dist/config/schema.d.ts +1 -0
  90. package/dist/config/schema.d.ts.map +1 -1
  91. package/dist/config/schema.js +23 -1
  92. package/dist/config/schema.js.map +1 -1
  93. package/dist/custom-claims/repository.d.ts +14 -9
  94. package/dist/custom-claims/repository.d.ts.map +1 -1
  95. package/dist/custom-claims/repository.js +38 -11
  96. package/dist/custom-claims/repository.js.map +1 -1
  97. package/dist/custom-claims/service.d.ts +11 -6
  98. package/dist/custom-claims/service.d.ts.map +1 -1
  99. package/dist/custom-claims/service.js +43 -20
  100. package/dist/custom-claims/service.js.map +1 -1
  101. package/dist/index.js +8 -2
  102. package/dist/index.js.map +1 -1
  103. package/dist/lib/admin-permissions.d.ts +12 -7
  104. package/dist/lib/admin-permissions.d.ts.map +1 -1
  105. package/dist/lib/admin-permissions.js +26 -16
  106. package/dist/lib/admin-permissions.js.map +1 -1
  107. package/dist/lib/audit-log.d.ts +2 -2
  108. package/dist/lib/audit-log.d.ts.map +1 -1
  109. package/dist/lib/audit-log.js +1 -1
  110. package/dist/lib/audit-log.js.map +1 -1
  111. package/dist/lib/authority-revocation.d.ts +18 -0
  112. package/dist/lib/authority-revocation.d.ts.map +1 -0
  113. package/dist/lib/authority-revocation.js +37 -0
  114. package/dist/lib/authority-revocation.js.map +1 -0
  115. package/dist/lib/branding-assets.d.ts +35 -5
  116. package/dist/lib/branding-assets.d.ts.map +1 -1
  117. package/dist/lib/branding-assets.js +33 -26
  118. package/dist/lib/branding-assets.js.map +1 -1
  119. package/dist/lib/bulk-operations.d.ts +1 -1
  120. package/dist/lib/bulk-operations.d.ts.map +1 -1
  121. package/dist/lib/bulk-operations.js +1 -13
  122. package/dist/lib/bulk-operations.js.map +1 -1
  123. package/dist/lib/cursor.d.ts +2 -0
  124. package/dist/lib/cursor.d.ts.map +1 -1
  125. package/dist/lib/cursor.js +2 -7
  126. package/dist/lib/cursor.js.map +1 -1
  127. package/dist/lib/database.d.ts.map +1 -1
  128. package/dist/lib/database.js +5 -0
  129. package/dist/lib/database.js.map +1 -1
  130. package/dist/lib/deletion-cleanup.d.ts +43 -0
  131. package/dist/lib/deletion-cleanup.d.ts.map +1 -0
  132. package/dist/lib/deletion-cleanup.js +207 -0
  133. package/dist/lib/deletion-cleanup.js.map +1 -0
  134. package/dist/lib/image-validator.d.ts +3 -1
  135. package/dist/lib/image-validator.d.ts.map +1 -1
  136. package/dist/lib/image-validator.js +32 -19
  137. package/dist/lib/image-validator.js.map +1 -1
  138. package/dist/lib/log-format.d.ts +20 -0
  139. package/dist/lib/log-format.d.ts.map +1 -0
  140. package/dist/lib/log-format.js +22 -0
  141. package/dist/lib/log-format.js.map +1 -0
  142. package/dist/lib/logger.d.ts +3 -1
  143. package/dist/lib/logger.d.ts.map +1 -1
  144. package/dist/lib/logger.js +21 -4
  145. package/dist/lib/logger.js.map +1 -1
  146. package/dist/lib/redis.d.ts.map +1 -1
  147. package/dist/lib/redis.js +3 -0
  148. package/dist/lib/redis.js.map +1 -1
  149. package/dist/lib/session-tracking.d.ts +73 -11
  150. package/dist/lib/session-tracking.d.ts.map +1 -1
  151. package/dist/lib/session-tracking.js +76 -32
  152. package/dist/lib/session-tracking.js.map +1 -1
  153. package/dist/lib/signing-key-crypto.d.ts.map +1 -1
  154. package/dist/lib/signing-key-crypto.js +4 -5
  155. package/dist/lib/signing-key-crypto.js.map +1 -1
  156. package/dist/lib/signing-keys.d.ts +3 -4
  157. package/dist/lib/signing-keys.d.ts.map +1 -1
  158. package/dist/lib/signing-keys.js +66 -41
  159. package/dist/lib/signing-keys.js.map +1 -1
  160. package/dist/lib/super-admin-protection.d.ts +5 -5
  161. package/dist/lib/super-admin-protection.d.ts.map +1 -1
  162. package/dist/lib/super-admin-protection.js +6 -9
  163. package/dist/lib/super-admin-protection.js.map +1 -1
  164. package/dist/lib/system-config-catalog.d.ts +268 -0
  165. package/dist/lib/system-config-catalog.d.ts.map +1 -0
  166. package/dist/lib/system-config-catalog.js +90 -0
  167. package/dist/lib/system-config-catalog.js.map +1 -0
  168. package/dist/lib/system-config.d.ts +30 -33
  169. package/dist/lib/system-config.d.ts.map +1 -1
  170. package/dist/lib/system-config.js +78 -93
  171. package/dist/lib/system-config.js.map +1 -1
  172. package/dist/lib/with-timeout.d.ts +32 -0
  173. package/dist/lib/with-timeout.d.ts.map +1 -0
  174. package/dist/lib/with-timeout.js +45 -0
  175. package/dist/lib/with-timeout.js.map +1 -0
  176. package/dist/middleware/admin-auth.d.ts +6 -5
  177. package/dist/middleware/admin-auth.d.ts.map +1 -1
  178. package/dist/middleware/admin-auth.js +30 -20
  179. package/dist/middleware/admin-auth.js.map +1 -1
  180. package/dist/middleware/admin-cors.d.ts +3 -3
  181. package/dist/middleware/admin-cors.js +3 -3
  182. package/dist/middleware/admin-mutation-audit.d.ts +3 -2
  183. package/dist/middleware/admin-mutation-audit.d.ts.map +1 -1
  184. package/dist/middleware/admin-mutation-audit.js +12 -5
  185. package/dist/middleware/admin-mutation-audit.js.map +1 -1
  186. package/dist/middleware/admin-rate-limiter.d.ts +7 -8
  187. package/dist/middleware/admin-rate-limiter.d.ts.map +1 -1
  188. package/dist/middleware/admin-rate-limiter.js +7 -8
  189. package/dist/middleware/admin-rate-limiter.js.map +1 -1
  190. package/dist/middleware/client-secret-hash.d.ts +10 -29
  191. package/dist/middleware/client-secret-hash.d.ts.map +1 -1
  192. package/dist/middleware/client-secret-hash.js +161 -59
  193. package/dist/middleware/client-secret-hash.js.map +1 -1
  194. package/dist/middleware/health.d.ts +17 -0
  195. package/dist/middleware/health.d.ts.map +1 -1
  196. package/dist/middleware/health.js +25 -21
  197. package/dist/middleware/health.js.map +1 -1
  198. package/dist/middleware/ready.d.ts.map +1 -1
  199. package/dist/middleware/ready.js +1 -12
  200. package/dist/middleware/ready.js.map +1 -1
  201. package/dist/middleware/require-existing-organization.d.ts +18 -0
  202. package/dist/middleware/require-existing-organization.d.ts.map +1 -0
  203. package/dist/middleware/require-existing-organization.js +38 -0
  204. package/dist/middleware/require-existing-organization.js.map +1 -0
  205. package/dist/middleware/require-permission.d.ts.map +1 -1
  206. package/dist/middleware/require-permission.js +35 -1
  207. package/dist/middleware/require-permission.js.map +1 -1
  208. package/dist/middleware/require-user-organization.d.ts.map +1 -1
  209. package/dist/middleware/require-user-organization.js +4 -2
  210. package/dist/middleware/require-user-organization.js.map +1 -1
  211. package/dist/middleware/security-headers.d.ts +18 -0
  212. package/dist/middleware/security-headers.d.ts.map +1 -1
  213. package/dist/middleware/security-headers.js +32 -1
  214. package/dist/middleware/security-headers.js.map +1 -1
  215. package/dist/middleware/tenant-resolver.d.ts +1 -2
  216. package/dist/middleware/tenant-resolver.d.ts.map +1 -1
  217. package/dist/middleware/tenant-resolver.js +1 -5
  218. package/dist/middleware/tenant-resolver.js.map +1 -1
  219. package/dist/middleware/token-rate-limiter.d.ts +37 -16
  220. package/dist/middleware/token-rate-limiter.d.ts.map +1 -1
  221. package/dist/middleware/token-rate-limiter.js +126 -54
  222. package/dist/middleware/token-rate-limiter.js.map +1 -1
  223. package/dist/oidc/account-finder.d.ts +5 -5
  224. package/dist/oidc/account-finder.d.ts.map +1 -1
  225. package/dist/oidc/account-finder.js +31 -23
  226. package/dist/oidc/account-finder.js.map +1 -1
  227. package/dist/oidc/adapter-factory.d.ts.map +1 -1
  228. package/dist/oidc/adapter-factory.js +83 -3
  229. package/dist/oidc/adapter-factory.js.map +1 -1
  230. package/dist/oidc/configuration.d.ts +3 -8
  231. package/dist/oidc/configuration.d.ts.map +1 -1
  232. package/dist/oidc/configuration.js +67 -50
  233. package/dist/oidc/configuration.js.map +1 -1
  234. package/dist/oidc/provider.d.ts.map +1 -1
  235. package/dist/oidc/provider.js +7 -0
  236. package/dist/oidc/provider.js.map +1 -1
  237. package/dist/oidc/redis-adapter.d.ts +2 -3
  238. package/dist/oidc/redis-adapter.d.ts.map +1 -1
  239. package/dist/oidc/redis-adapter.js +54 -46
  240. package/dist/oidc/redis-adapter.js.map +1 -1
  241. package/dist/organizations/branding-url.d.ts +17 -0
  242. package/dist/organizations/branding-url.d.ts.map +1 -0
  243. package/dist/organizations/branding-url.js +38 -0
  244. package/dist/organizations/branding-url.js.map +1 -0
  245. package/dist/organizations/index.d.ts +2 -2
  246. package/dist/organizations/index.d.ts.map +1 -1
  247. package/dist/organizations/index.js +1 -1
  248. package/dist/organizations/index.js.map +1 -1
  249. package/dist/organizations/repository.d.ts +22 -32
  250. package/dist/organizations/repository.d.ts.map +1 -1
  251. package/dist/organizations/repository.js +64 -45
  252. package/dist/organizations/repository.js.map +1 -1
  253. package/dist/organizations/service.d.ts +14 -46
  254. package/dist/organizations/service.d.ts.map +1 -1
  255. package/dist/organizations/service.js +60 -112
  256. package/dist/organizations/service.js.map +1 -1
  257. package/dist/organizations/types.d.ts +1 -15
  258. package/dist/organizations/types.d.ts.map +1 -1
  259. package/dist/organizations/validators.d.ts +41 -0
  260. package/dist/organizations/validators.d.ts.map +1 -0
  261. package/dist/organizations/validators.js +48 -0
  262. package/dist/organizations/validators.js.map +1 -0
  263. package/dist/portability/apply.d.ts +15 -0
  264. package/dist/portability/apply.d.ts.map +1 -0
  265. package/dist/portability/apply.js +274 -0
  266. package/dist/portability/apply.js.map +1 -0
  267. package/dist/portability/authorization.d.ts +30 -0
  268. package/dist/portability/authorization.d.ts.map +1 -0
  269. package/dist/portability/authorization.js +130 -0
  270. package/dist/portability/authorization.js.map +1 -0
  271. package/dist/portability/cleanup.d.ts +29 -0
  272. package/dist/portability/cleanup.d.ts.map +1 -0
  273. package/dist/portability/cleanup.js +167 -0
  274. package/dist/portability/cleanup.js.map +1 -0
  275. package/dist/portability/export.d.ts +18 -0
  276. package/dist/portability/export.d.ts.map +1 -0
  277. package/dist/portability/export.js +125 -0
  278. package/dist/portability/export.js.map +1 -0
  279. package/dist/portability/import-repository.d.ts +204 -0
  280. package/dist/portability/import-repository.d.ts.map +1 -0
  281. package/dist/portability/import-repository.js +348 -0
  282. package/dist/portability/import-repository.js.map +1 -0
  283. package/dist/portability/import-user-client-writers.d.ts +40 -0
  284. package/dist/portability/import-user-client-writers.d.ts.map +1 -0
  285. package/dist/portability/import-user-client-writers.js +171 -0
  286. package/dist/portability/import-user-client-writers.js.map +1 -0
  287. package/dist/portability/index.d.ts +8 -0
  288. package/dist/portability/index.d.ts.map +1 -0
  289. package/dist/portability/index.js +8 -0
  290. package/dist/portability/index.js.map +1 -0
  291. package/dist/portability/plan-support.d.ts +137 -0
  292. package/dist/portability/plan-support.d.ts.map +1 -0
  293. package/dist/portability/plan-support.js +337 -0
  294. package/dist/portability/plan-support.js.map +1 -0
  295. package/dist/portability/plan.d.ts +34 -0
  296. package/dist/portability/plan.d.ts.map +1 -0
  297. package/dist/portability/plan.js +450 -0
  298. package/dist/portability/plan.js.map +1 -0
  299. package/dist/portability/repository.d.ts +95 -0
  300. package/dist/portability/repository.d.ts.map +1 -0
  301. package/dist/portability/repository.js +390 -0
  302. package/dist/portability/repository.js.map +1 -0
  303. package/dist/portability/schema.d.ts +591 -0
  304. package/dist/portability/schema.d.ts.map +1 -0
  305. package/dist/portability/schema.js +459 -0
  306. package/dist/portability/schema.js.map +1 -0
  307. package/dist/portability/types.d.ts +382 -0
  308. package/dist/portability/types.d.ts.map +1 -0
  309. package/dist/portability/types.js +19 -0
  310. package/dist/portability/types.js.map +1 -0
  311. package/dist/rbac/errors.d.ts +4 -0
  312. package/dist/rbac/errors.d.ts.map +1 -1
  313. package/dist/rbac/errors.js +7 -0
  314. package/dist/rbac/errors.js.map +1 -1
  315. package/dist/rbac/index.d.ts +3 -3
  316. package/dist/rbac/index.d.ts.map +1 -1
  317. package/dist/rbac/index.js +3 -3
  318. package/dist/rbac/index.js.map +1 -1
  319. package/dist/rbac/mapping-repository.d.ts +84 -9
  320. package/dist/rbac/mapping-repository.d.ts.map +1 -1
  321. package/dist/rbac/mapping-repository.js +222 -55
  322. package/dist/rbac/mapping-repository.js.map +1 -1
  323. package/dist/rbac/permission-repository.d.ts +36 -19
  324. package/dist/rbac/permission-repository.d.ts.map +1 -1
  325. package/dist/rbac/permission-repository.js +93 -35
  326. package/dist/rbac/permission-repository.js.map +1 -1
  327. package/dist/rbac/permission-service.d.ts +18 -19
  328. package/dist/rbac/permission-service.d.ts.map +1 -1
  329. package/dist/rbac/permission-service.js +100 -57
  330. package/dist/rbac/permission-service.js.map +1 -1
  331. package/dist/rbac/role-repository.d.ts +29 -24
  332. package/dist/rbac/role-repository.d.ts.map +1 -1
  333. package/dist/rbac/role-repository.js +66 -43
  334. package/dist/rbac/role-repository.js.map +1 -1
  335. package/dist/rbac/role-service.d.ts +35 -32
  336. package/dist/rbac/role-service.d.ts.map +1 -1
  337. package/dist/rbac/role-service.js +210 -96
  338. package/dist/rbac/role-service.js.map +1 -1
  339. package/dist/rbac/slugs.d.ts +16 -27
  340. package/dist/rbac/slugs.d.ts.map +1 -1
  341. package/dist/rbac/slugs.js +36 -59
  342. package/dist/rbac/slugs.js.map +1 -1
  343. package/dist/rbac/types.d.ts +2 -4
  344. package/dist/rbac/types.d.ts.map +1 -1
  345. package/dist/rbac/types.js.map +1 -1
  346. package/dist/rbac/user-role-service.d.ts +23 -24
  347. package/dist/rbac/user-role-service.d.ts.map +1 -1
  348. package/dist/rbac/user-role-service.js +117 -61
  349. package/dist/rbac/user-role-service.js.map +1 -1
  350. package/dist/routes/applications.d.ts +4 -2
  351. package/dist/routes/applications.d.ts.map +1 -1
  352. package/dist/routes/applications.js +57 -19
  353. package/dist/routes/applications.js.map +1 -1
  354. package/dist/routes/audit.js +1 -1
  355. package/dist/routes/audit.js.map +1 -1
  356. package/dist/routes/branding.d.ts +3 -3
  357. package/dist/routes/branding.d.ts.map +1 -1
  358. package/dist/routes/branding.js +44 -16
  359. package/dist/routes/branding.js.map +1 -1
  360. package/dist/routes/bulk.d.ts +0 -5
  361. package/dist/routes/bulk.d.ts.map +1 -1
  362. package/dist/routes/bulk.js +3 -5
  363. package/dist/routes/bulk.js.map +1 -1
  364. package/dist/routes/clients.d.ts +4 -4
  365. package/dist/routes/clients.d.ts.map +1 -1
  366. package/dist/routes/clients.js +113 -52
  367. package/dist/routes/clients.js.map +1 -1
  368. package/dist/routes/config.d.ts +17 -12
  369. package/dist/routes/config.d.ts.map +1 -1
  370. package/dist/routes/config.js +165 -64
  371. package/dist/routes/config.js.map +1 -1
  372. package/dist/routes/custom-claims.d.ts.map +1 -1
  373. package/dist/routes/custom-claims.js +8 -2
  374. package/dist/routes/custom-claims.js.map +1 -1
  375. package/dist/routes/exports.d.ts +2 -1
  376. package/dist/routes/exports.d.ts.map +1 -1
  377. package/dist/routes/exports.js +57 -1
  378. package/dist/routes/exports.js.map +1 -1
  379. package/dist/routes/imports.d.ts +2 -2
  380. package/dist/routes/imports.d.ts.map +1 -1
  381. package/dist/routes/imports.js +95 -40
  382. package/dist/routes/imports.js.map +1 -1
  383. package/dist/routes/interactions.d.ts.map +1 -1
  384. package/dist/routes/interactions.js +113 -87
  385. package/dist/routes/interactions.js.map +1 -1
  386. package/dist/routes/invitation.d.ts.map +1 -1
  387. package/dist/routes/invitation.js +28 -27
  388. package/dist/routes/invitation.js.map +1 -1
  389. package/dist/routes/keys.d.ts.map +1 -1
  390. package/dist/routes/keys.js +26 -13
  391. package/dist/routes/keys.js.map +1 -1
  392. package/dist/routes/magic-link.d.ts.map +1 -1
  393. package/dist/routes/magic-link.js +5 -17
  394. package/dist/routes/magic-link.js.map +1 -1
  395. package/dist/routes/organizations.d.ts +1 -2
  396. package/dist/routes/organizations.d.ts.map +1 -1
  397. package/dist/routes/organizations.js +43 -104
  398. package/dist/routes/organizations.js.map +1 -1
  399. package/dist/routes/password-reset.d.ts.map +1 -1
  400. package/dist/routes/password-reset.js +13 -35
  401. package/dist/routes/password-reset.js.map +1 -1
  402. package/dist/routes/permissions.d.ts +1 -1
  403. package/dist/routes/permissions.d.ts.map +1 -1
  404. package/dist/routes/permissions.js +58 -17
  405. package/dist/routes/permissions.js.map +1 -1
  406. package/dist/routes/public-branding.d.ts +16 -0
  407. package/dist/routes/public-branding.d.ts.map +1 -0
  408. package/dist/routes/public-branding.js +52 -0
  409. package/dist/routes/public-branding.js.map +1 -0
  410. package/dist/routes/roles.d.ts +1 -1
  411. package/dist/routes/roles.d.ts.map +1 -1
  412. package/dist/routes/roles.js +63 -21
  413. package/dist/routes/roles.js.map +1 -1
  414. package/dist/routes/sessions.d.ts.map +1 -1
  415. package/dist/routes/sessions.js +23 -12
  416. package/dist/routes/sessions.js.map +1 -1
  417. package/dist/routes/two-factor.d.ts.map +1 -1
  418. package/dist/routes/two-factor.js +91 -38
  419. package/dist/routes/two-factor.js.map +1 -1
  420. package/dist/routes/user-roles.d.ts.map +1 -1
  421. package/dist/routes/user-roles.js +56 -9
  422. package/dist/routes/user-roles.js.map +1 -1
  423. package/dist/routes/users.d.ts +4 -8
  424. package/dist/routes/users.d.ts.map +1 -1
  425. package/dist/routes/users.js +108 -265
  426. package/dist/routes/users.js.map +1 -1
  427. package/dist/server.d.ts.map +1 -1
  428. package/dist/server.js +53 -37
  429. package/dist/server.js.map +1 -1
  430. package/dist/two-factor/errors.d.ts +9 -0
  431. package/dist/two-factor/errors.d.ts.map +1 -1
  432. package/dist/two-factor/errors.js +12 -0
  433. package/dist/two-factor/errors.js.map +1 -1
  434. package/dist/two-factor/index.d.ts +5 -4
  435. package/dist/two-factor/index.d.ts.map +1 -1
  436. package/dist/two-factor/index.js +4 -4
  437. package/dist/two-factor/index.js.map +1 -1
  438. package/dist/two-factor/repository.d.ts +17 -6
  439. package/dist/two-factor/repository.d.ts.map +1 -1
  440. package/dist/two-factor/repository.js +37 -9
  441. package/dist/two-factor/repository.js.map +1 -1
  442. package/dist/two-factor/service.d.ts.map +1 -1
  443. package/dist/two-factor/service.js +26 -19
  444. package/dist/two-factor/service.js.map +1 -1
  445. package/dist/two-factor/totp.d.ts +13 -2
  446. package/dist/two-factor/totp.d.ts.map +1 -1
  447. package/dist/two-factor/totp.js +20 -7
  448. package/dist/two-factor/totp.js.map +1 -1
  449. package/dist/two-factor/types.d.ts +3 -0
  450. package/dist/two-factor/types.d.ts.map +1 -1
  451. package/dist/two-factor/types.js +5 -0
  452. package/dist/two-factor/types.js.map +1 -1
  453. package/dist/users/gdpr.d.ts +2 -40
  454. package/dist/users/gdpr.d.ts.map +1 -1
  455. package/dist/users/gdpr.js +3 -110
  456. package/dist/users/gdpr.js.map +1 -1
  457. package/dist/users/index.d.ts +5 -4
  458. package/dist/users/index.d.ts.map +1 -1
  459. package/dist/users/index.js +5 -4
  460. package/dist/users/index.js.map +1 -1
  461. package/dist/users/repository.d.ts +49 -3
  462. package/dist/users/repository.d.ts.map +1 -1
  463. package/dist/users/repository.js +169 -18
  464. package/dist/users/repository.js.map +1 -1
  465. package/dist/users/service.d.ts +19 -51
  466. package/dist/users/service.d.ts.map +1 -1
  467. package/dist/users/service.js +72 -122
  468. package/dist/users/service.js.map +1 -1
  469. package/dist/users/types.d.ts +4 -5
  470. package/dist/users/types.d.ts.map +1 -1
  471. package/dist/users/types.js.map +1 -1
  472. package/dist/users/validators.d.ts +28 -0
  473. package/dist/users/validators.d.ts.map +1 -0
  474. package/dist/users/validators.js +25 -0
  475. package/dist/users/validators.js.map +1 -0
  476. package/locales/default/en/errors.json +2 -1
  477. package/migrations/024_application_client_correction.sql +39 -0
  478. package/migrations/025_record_deletion_lifecycle.sql +26 -0
  479. package/migrations/026_simplify_user_lifecycle.sql +33 -0
  480. package/migrations/027_branding_asset_size_limits.sql +19 -0
  481. package/migrations/028_totp_replay_protection.sql +17 -0
  482. package/migrations/029_public_session_identifiers.sql +16 -0
  483. package/migrations/030_global_configuration_catalog.sql +39 -0
  484. package/migrations/031_monotonic_updated_at.sql +28 -0
  485. package/migrations/032_client_require_consent.sql +15 -0
  486. package/package.json +2 -2
  487. package/dist/lib/data-import-plan.d.ts +0 -50
  488. package/dist/lib/data-import-plan.d.ts.map +0 -1
  489. package/dist/lib/data-import-plan.js +0 -235
  490. package/dist/lib/data-import-plan.js.map +0 -1
  491. package/dist/lib/data-import.d.ts +0 -276
  492. package/dist/lib/data-import.d.ts.map +0 -1
  493. package/dist/lib/data-import.js +0 -1694
  494. package/dist/lib/data-import.js.map +0 -1
@@ -1,70 +1,130 @@
1
1
  /**
2
2
  * Session tracking repository.
3
3
  *
4
- * PostgreSQL mirror of Redis OIDC sessions for admin listing, filtering,
5
- * and revocation. All operations are fire-and-forget — tracking failures
6
- * must NEVER break the OIDC flow.
4
+ * PostgreSQL authority record for Redis-backed OIDC sessions. Session
5
+ * publication waits for this record so a Redis payload cannot outlive the
6
+ * database state used to revoke it.
7
7
  *
8
8
  * The Redis adapter calls these hooks when modelName === 'Session'.
9
9
  *
10
10
  * @module session-tracking
11
- * @see 05-dashboard-sessions-history.md
12
11
  */
13
- /** Input for creating/upserting a session tracking record */
12
+ /** Input for creating or updating a session tracking record. */
14
13
  export interface SessionTrackingInput {
14
+ /** OIDC Session identifier, shared with the Redis payload key. */
15
15
  sessionId: string;
16
+ /** User that authenticated the Session, when authentication has completed. */
16
17
  userId?: string;
18
+ /** Internal client UUID when a single client can be identified. */
17
19
  clientId?: string;
20
+ /** Organization that owns the authenticated user. */
18
21
  organizationId?: string;
22
+ /** Current grant identifier when one is available at publication time. */
19
23
  grantId?: string;
24
+ /** Source IP address retained for administrative session inspection. */
20
25
  ipAddress?: string;
26
+ /** Source user agent retained for administrative session inspection. */
21
27
  userAgent?: string;
28
+ /** Absolute time after which the Session no longer has authority. */
22
29
  expiresAt: Date;
23
30
  }
24
- /** A tracked session record */
31
+ /** A tracked session record returned from PostgreSQL. */
25
32
  export interface TrackedSession {
33
+ /** OIDC Session identifier. */
26
34
  sessionId: string;
35
+ /** Authenticated user UUID, or null before authentication completes. */
27
36
  userId: string | null;
37
+ /** Internal client UUID when the Session has one tracked client. */
28
38
  clientId: string | null;
39
+ /** Organization UUID associated with the Session. */
29
40
  organizationId: string | null;
41
+ /** Grant identifier captured for administrative inspection. */
30
42
  grantId: string | null;
43
+ /** Source IP address, when captured. */
31
44
  ipAddress: string | null;
45
+ /** Source user agent, when captured. */
32
46
  userAgent: string | null;
47
+ /** Time the tracking row was first created. */
33
48
  createdAt: Date;
49
+ /** Absolute authority expiry. */
34
50
  expiresAt: Date;
51
+ /** Time the tracking row was last refreshed. */
35
52
  lastActivityAt: Date;
53
+ /** Revocation time, or null while the Session remains live. */
36
54
  revokedAt: Date | null;
37
55
  }
38
- /** Options for listing sessions */
56
+ /** Filters and pagination controls for session listing. */
39
57
  export interface ListSessionsOptions {
58
+ /** Limit results to one user UUID. */
40
59
  userId?: string;
60
+ /** Limit results to one organization UUID. */
41
61
  organizationId?: string;
62
+ /** Limit results to one internal client UUID. */
42
63
  clientId?: string;
64
+ /** Include revoked and expired rows when false. Defaults to true. */
43
65
  activeOnly?: boolean;
66
+ /** One-based result page. */
44
67
  page?: number;
68
+ /** Number of rows per page, clamped to 1 through 100. */
45
69
  pageSize?: number;
46
70
  }
47
71
  /**
48
- * Upsert a session tracking record (called on session create/update).
49
- * Fire-and-forget — errors are logged but never thrown.
72
+ * Create or update the authority record for an OIDC Session.
73
+ *
74
+ * The caller must await this operation before publishing the corresponding
75
+ * Redis payload. Database errors intentionally propagate because an
76
+ * untracked Session could otherwise continue after administrative revocation.
77
+ *
78
+ * @param input - Session identifiers and absolute expiry.
79
+ * @throws The PostgreSQL error when the authority record cannot be persisted.
50
80
  */
51
81
  export declare function upsertSession(input: SessionTrackingInput): Promise<void>;
52
82
  /**
53
- * Mark a session as revoked (called on session destroy).
54
- * Fire-and-forget — errors are logged but never thrown.
83
+ * Mark a session as revoked before its Redis payload is deleted.
84
+ *
85
+ * Database errors propagate so callers cannot report a completed logout while
86
+ * the durable authority record remains live.
87
+ *
88
+ * @param sessionId - OIDC Session identifier.
55
89
  */
56
90
  export declare function revokeSession(sessionId: string): Promise<void>;
91
+ /**
92
+ * Revoke a session by its non-secret administrative identifier.
93
+ *
94
+ * The Redis key is returned only to the trusted route so it can remove the
95
+ * corresponding live session after the database update commits.
96
+ *
97
+ * @param publicId - UUID exposed by the administrative API.
98
+ * @returns The private Redis session key, or null when no active row matched.
99
+ */
100
+ export declare function revokeSessionByPublicId(publicId: string): Promise<string | null>;
57
101
  /**
58
102
  * Revoke all active sessions for a user.
103
+ *
104
+ * @param userId - User UUID whose active tracking rows are revoked.
59
105
  * Returns the number of sessions revoked.
106
+ * @returns Number of rows changed by PostgreSQL.
60
107
  */
61
108
  export declare function revokeUserSessions(userId: string): Promise<number>;
62
109
  /**
63
110
  * Get a single tracked session by ID.
111
+ *
112
+ * @param sessionId - OIDC Session identifier.
113
+ * @returns The tracking record, or null when no record exists.
64
114
  */
65
115
  export declare function getSession(sessionId: string): Promise<TrackedSession | null>;
116
+ /**
117
+ * Read a tracked session through its non-secret administrative identifier.
118
+ *
119
+ * @param publicId - UUID exposed by the administrative API.
120
+ * @returns The public tracking record, or null when no row exists.
121
+ */
122
+ export declare function getSessionByPublicId(publicId: string): Promise<TrackedSession | null>;
66
123
  /**
67
124
  * List tracked sessions with filtering and pagination.
125
+ *
126
+ * @param options - Optional filters and bounded pagination controls.
127
+ * @returns Matching tracking rows and pagination metadata.
68
128
  */
69
129
  export declare function listSessions(options?: ListSessionsOptions): Promise<{
70
130
  data: TrackedSession[];
@@ -76,6 +136,8 @@ export declare function listSessions(options?: ListSessionsOptions): Promise<{
76
136
  * Clean up expired session tracking records older than 7 days.
77
137
  * Keeps revoked sessions for audit purposes for 7 days.
78
138
  * Fire-and-forget — designed to be called periodically.
139
+ *
140
+ * @returns Number of expired rows removed, or zero when cleanup fails.
79
141
  */
80
142
  export declare function purgeExpiredSessions(): Promise<number>;
81
143
  //# sourceMappingURL=session-tracking.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-tracking.d.ts","sourceRoot":"","sources":["../../src/lib/session-tracking.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AASH,6DAA6D;AAC7D,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,+BAA+B;AAC/B,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,cAAc,EAAE,IAAI,CAAC;IACrB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB;AAED,mCAAmC;AACnC,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAMD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0B9E;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUpE;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOxE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAYlF;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC;IAC7E,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAmDD;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC,CAW5D"}
1
+ {"version":3,"file":"session-tracking.d.ts","sourceRoot":"","sources":["../../src/lib/session-tracking.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,13 @@
1
1
  /**
2
2
  * Session tracking repository.
3
3
  *
4
- * PostgreSQL mirror of Redis OIDC sessions for admin listing, filtering,
5
- * and revocation. All operations are fire-and-forget — tracking failures
6
- * must NEVER break the OIDC flow.
4
+ * PostgreSQL authority record for Redis-backed OIDC sessions. Session
5
+ * publication waits for this record so a Redis payload cannot outlive the
6
+ * database state used to revoke it.
7
7
  *
8
8
  * The Redis adapter calls these hooks when modelName === 'Session'.
9
9
  *
10
10
  * @module session-tracking
11
- * @see 05-dashboard-sessions-history.md
12
11
  */
13
12
  import { getPool } from './database.js';
14
13
  import { logger } from './logger.js';
@@ -16,50 +15,71 @@ import { logger } from './logger.js';
16
15
  // Repository functions
17
16
  // ============================================================================
18
17
  /**
19
- * Upsert a session tracking record (called on session create/update).
20
- * Fire-and-forget — errors are logged but never thrown.
18
+ * Create or update the authority record for an OIDC Session.
19
+ *
20
+ * The caller must await this operation before publishing the corresponding
21
+ * Redis payload. Database errors intentionally propagate because an
22
+ * untracked Session could otherwise continue after administrative revocation.
23
+ *
24
+ * @param input - Session identifiers and absolute expiry.
25
+ * @throws The PostgreSQL error when the authority record cannot be persisted.
21
26
  */
22
27
  export async function upsertSession(input) {
23
- try {
24
- const pool = getPool();
25
- await pool.query(`INSERT INTO admin_sessions (session_id, user_id, client_id, organization_id, grant_id, ip_address, user_agent, expires_at, last_activity_at)
28
+ const pool = getPool();
29
+ await pool.query(`INSERT INTO admin_sessions (session_id, user_id, client_id, organization_id, grant_id, ip_address, user_agent, expires_at, last_activity_at)
26
30
  VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW())
27
31
  ON CONFLICT (session_id) DO UPDATE SET
28
32
  user_id = COALESCE(EXCLUDED.user_id, admin_sessions.user_id),
29
33
  client_id = COALESCE(EXCLUDED.client_id, admin_sessions.client_id),
30
34
  organization_id = COALESCE(EXCLUDED.organization_id, admin_sessions.organization_id),
31
35
  grant_id = COALESCE(EXCLUDED.grant_id, admin_sessions.grant_id),
36
+ expires_at = EXCLUDED.expires_at,
32
37
  last_activity_at = NOW()`, [
33
- input.sessionId,
34
- input.userId ?? null,
35
- input.clientId ?? null,
36
- input.organizationId ?? null,
37
- input.grantId ?? null,
38
- input.ipAddress ?? null,
39
- input.userAgent ?? null,
40
- input.expiresAt,
41
- ]);
42
- }
43
- catch (err) {
44
- logger.warn({ err, sessionId: input.sessionId }, 'Failed to upsert session tracking record');
45
- }
38
+ input.sessionId,
39
+ input.userId ?? null,
40
+ input.clientId ?? null,
41
+ input.organizationId ?? null,
42
+ input.grantId ?? null,
43
+ input.ipAddress ?? null,
44
+ input.userAgent ?? null,
45
+ input.expiresAt,
46
+ ]);
46
47
  }
47
48
  /**
48
- * Mark a session as revoked (called on session destroy).
49
- * Fire-and-forget — errors are logged but never thrown.
49
+ * Mark a session as revoked before its Redis payload is deleted.
50
+ *
51
+ * Database errors propagate so callers cannot report a completed logout while
52
+ * the durable authority record remains live.
53
+ *
54
+ * @param sessionId - OIDC Session identifier.
50
55
  */
51
56
  export async function revokeSession(sessionId) {
52
- try {
53
- const pool = getPool();
54
- await pool.query(`UPDATE admin_sessions SET revoked_at = NOW() WHERE session_id = $1 AND revoked_at IS NULL`, [sessionId]);
55
- }
56
- catch (err) {
57
- logger.warn({ err, sessionId }, 'Failed to revoke session tracking record');
58
- }
57
+ const pool = getPool();
58
+ await pool.query(`UPDATE admin_sessions SET revoked_at = NOW() WHERE session_id = $1 AND revoked_at IS NULL`, [sessionId]);
59
+ }
60
+ /**
61
+ * Revoke a session by its non-secret administrative identifier.
62
+ *
63
+ * The Redis key is returned only to the trusted route so it can remove the
64
+ * corresponding live session after the database update commits.
65
+ *
66
+ * @param publicId - UUID exposed by the administrative API.
67
+ * @returns The private Redis session key, or null when no active row matched.
68
+ */
69
+ export async function revokeSessionByPublicId(publicId) {
70
+ const pool = getPool();
71
+ const { rows } = await pool.query(`UPDATE admin_sessions
72
+ SET revoked_at = NOW()
73
+ WHERE public_id = $1 AND revoked_at IS NULL
74
+ RETURNING session_id AS "sessionId"`, [publicId]);
75
+ return rows[0]?.sessionId ?? null;
59
76
  }
60
77
  /**
61
78
  * Revoke all active sessions for a user.
79
+ *
80
+ * @param userId - User UUID whose active tracking rows are revoked.
62
81
  * Returns the number of sessions revoked.
82
+ * @returns Number of rows changed by PostgreSQL.
63
83
  */
64
84
  export async function revokeUserSessions(userId) {
65
85
  const pool = getPool();
@@ -68,6 +88,9 @@ export async function revokeUserSessions(userId) {
68
88
  }
69
89
  /**
70
90
  * Get a single tracked session by ID.
91
+ *
92
+ * @param sessionId - OIDC Session identifier.
93
+ * @returns The tracking record, or null when no record exists.
71
94
  */
72
95
  export async function getSession(sessionId) {
73
96
  const pool = getPool();
@@ -79,8 +102,27 @@ export async function getSession(sessionId) {
79
102
  FROM admin_sessions WHERE session_id = $1`, [sessionId]);
80
103
  return rows[0] ?? null;
81
104
  }
105
+ /**
106
+ * Read a tracked session through its non-secret administrative identifier.
107
+ *
108
+ * @param publicId - UUID exposed by the administrative API.
109
+ * @returns The public tracking record, or null when no row exists.
110
+ */
111
+ export async function getSessionByPublicId(publicId) {
112
+ const pool = getPool();
113
+ const { rows } = await pool.query(`SELECT public_id AS "sessionId", user_id AS "userId", client_id AS "clientId",
114
+ organization_id AS "organizationId", grant_id AS "grantId",
115
+ ip_address AS "ipAddress", user_agent AS "userAgent",
116
+ created_at AS "createdAt", expires_at AS "expiresAt",
117
+ last_activity_at AS "lastActivityAt", revoked_at AS "revokedAt"
118
+ FROM admin_sessions WHERE public_id = $1`, [publicId]);
119
+ return rows[0] ?? null;
120
+ }
82
121
  /**
83
122
  * List tracked sessions with filtering and pagination.
123
+ *
124
+ * @param options - Optional filters and bounded pagination controls.
125
+ * @returns Matching tracking rows and pagination metadata.
84
126
  */
85
127
  export async function listSessions(options = {}) {
86
128
  const pool = getPool();
@@ -110,7 +152,7 @@ export async function listSessions(options = {}) {
110
152
  const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';
111
153
  const [countResult, dataResult] = await Promise.all([
112
154
  pool.query(`SELECT COUNT(*)::text AS count FROM admin_sessions ${where}`, params),
113
- pool.query(`SELECT session_id AS "sessionId", user_id AS "userId", client_id AS "clientId",
155
+ pool.query(`SELECT public_id AS "sessionId", user_id AS "userId", client_id AS "clientId",
114
156
  organization_id AS "organizationId", grant_id AS "grantId",
115
157
  ip_address AS "ipAddress", user_agent AS "userAgent",
116
158
  created_at AS "createdAt", expires_at AS "expiresAt",
@@ -130,6 +172,8 @@ export async function listSessions(options = {}) {
130
172
  * Clean up expired session tracking records older than 7 days.
131
173
  * Keeps revoked sessions for audit purposes for 7 days.
132
174
  * Fire-and-forget — designed to be called periodically.
175
+ *
176
+ * @returns Number of expired rows removed, or zero when cleanup fails.
133
177
  */
134
178
  export async function purgeExpiredSessions() {
135
179
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"session-tracking.js","sourceRoot":"","sources":["../../src/lib/session-tracking.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-tracking.js","sourceRoot":"","sources":["../../src/lib/session-tracking.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"signing-key-crypto.d.ts","sourceRoot":"","sources":["../../src/lib/signing-key-crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AASH;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,GACpB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAyBhD;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,GACpB,MAAM,CA0BR"}
1
+ {"version":3,"file":"signing-key-crypto.d.ts","sourceRoot":"","sources":["../../src/lib/signing-key-crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AASH;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,GACpB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAyBhD;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,GACpB,MAAM,CAyBR"}
@@ -85,11 +85,10 @@ export function decryptPrivateKey(encrypted, iv, tag, encryptionKey) {
85
85
  ]);
86
86
  return decrypted.toString('utf8');
87
87
  }
88
- catch (error) {
89
- // Re-throw as a domain error — the underlying error may be
90
- // "Unsupported state or unable to authenticate data" (wrong key/tag)
91
- const message = error instanceof Error ? error.message : 'Unknown decryption error';
92
- throw new SigningKeyCryptoError(`Decryption failed: ${message}`);
88
+ catch {
89
+ // Cryptographic library errors can contain operational details. Callers receive one stable
90
+ // domain message so neither logs nor CLI output accidentally disclose those details.
91
+ throw new SigningKeyCryptoError('Signing key record is invalid');
93
92
  }
94
93
  }
95
94
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"signing-key-crypto.js","sourceRoot":"","sources":["../../src/lib/signing-key-crypto.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"signing-key-crypto.js","sourceRoot":"","sources":["../../src/lib/signing-key-crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE5E,oCAAoC;AACpC,MAAM,SAAS,GAAG,aAAa,CAAC;AAChC,MAAM,SAAS,GAAG,EAAE,CAAC,CAAC,gCAAgC;AACtD,MAAM,UAAU,GAAG,EAAE,CAAC,CAAC,6BAA6B;AAEpD;;;;;GAKG;AACH,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAC/B,aAAqB,EACrB,aAAqB;IAErB,4DAA4D;IAC5D,MAAM,SAAS,GAAG,kBAAkB,CAAC,aAAa,CAAC,CAAC;IAEpD,iEAAiE;IACjE,MAAM,EAAE,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAElC,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE;QACtD,aAAa,EAAE,UAAU;KAC1B,CAAC,CAAC;IAEH,sCAAsC;IACtC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC;QACpC,MAAM,CAAC,KAAK,EAAE;KACf,CAAC,CAAC;IAEH,6DAA6D;IAC7D,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAEhC,OAAO;QACL,SAAS,EAAE,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC;QACpC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;QACtB,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC;KACzB,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAAiB,EACjB,EAAU,EACV,GAAW,EACX,aAAqB;IAErB,MAAM,SAAS,GAAG,kBAAkB,CAAC,aAAa,CAAC,CAAC;IAEpD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,gBAAgB,CAC/B,SAAS,EACT,SAAS,EACT,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,EACtB,EAAE,aAAa,EAAE,UAAU,EAAE,CAC9B,CAAC;QAEF,8DAA8D;QAC9D,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;QAE7C,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC;YAC9B,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YAC9C,QAAQ,CAAC,KAAK,EAAE;SACjB,CAAC,CAAC;QAEH,OAAO,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,2FAA2F;QAC3F,qFAAqF;QACrF,MAAM,IAAI,qBAAqB,CAAC,+BAA+B,CAAC,CAAC;IACnE,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAS,kBAAkB,CAAC,MAAc;IACxC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,qBAAqB,CAC7B,8DAA8D,CAC/D,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AACpC,CAAC"}
@@ -125,10 +125,9 @@ export declare function loadSigningKeysFromDb(): Promise<SigningKeyRecord[]>;
125
125
  /**
126
126
  * Ensure at least one active signing key exists in the database.
127
127
  *
128
- * Called at application startup. If no active keys are found:
129
- * 1. Generate a new ES256 key pair
130
- * 2. Insert it into the signing_keys table with status='active'
131
- * 3. Log a warning that a key was auto-generated
128
+ * Called at application startup. A transaction-scoped table lock serializes the active-key
129
+ * recheck and optional encrypted insert, so simultaneous new processes cannot each create a
130
+ * bootstrap key. Every caller reloads the committed rows after releasing the lock.
132
131
  *
133
132
  * This guarantees the OIDC provider can always start, even on a fresh database
134
133
  * that has no signing keys yet (e.g., first run after migrations).
@@ -1 +1 @@
1
- {"version":3,"file":"signing-keys.d.ts","sourceRoot":"","sources":["../../src/lib/signing-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAmBH;;;;;;;;GAQG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC;IAAE,IAAI,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC,CAUrE;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAGrC;AAED,+DAA+D;AAC/D,MAAM,WAAW,gBAAgB;IAC/B,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;IACZ,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IACzC,iCAAiC;IACjC,WAAW,EAAE,IAAI,CAAC;IAClB,sDAAsD;IACtD,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,uEAAuE;IACvE,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB;AAED,mFAAmF;AACnF,MAAM,WAAW,UAAU;IACzB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,iCAAiC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,CAAC,EAAE,MAAM,CAAC;IACV,kDAAkD;IAClD,CAAC,EAAE,MAAM,CAAC;IACV,4EAA4E;IAC5E,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,IAAI;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB,CAoBA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAapE;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG;IAAE,IAAI,EAAE,UAAU,EAAE,CAAA;CAAE,CAcrF;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAuDzE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IAAE,IAAI,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC,CAgCzE"}
1
+ {"version":3,"file":"signing-keys.d.ts","sourceRoot":"","sources":["../../src/lib/signing-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA0BH;;;;;;;;GAQG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC;IAAE,IAAI,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC,CAkBrE;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAIrC;AAED,+DAA+D;AAC/D,MAAM,WAAW,gBAAgB;IAC/B,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;IACZ,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IACzC,iCAAiC;IACjC,WAAW,EAAE,IAAI,CAAC;IAClB,sDAAsD;IACtD,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,uEAAuE;IACvE,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB;AAED,mFAAmF;AACnF,MAAM,WAAW,UAAU;IACzB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,iCAAiC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,CAAC,EAAE,MAAM,CAAC;IACV,kDAAkD;IAClD,CAAC,EAAE,MAAM,CAAC;IACV,4EAA4E;IAC5E,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,IAAI;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB,CAoBA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAapE;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG;IAAE,IAAI,EAAE,UAAU,EAAE,CAAA;CAAE,CAYrF;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAyDzE;AAgBD;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IAAE,IAAI,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC,CAiCzE"}
@@ -17,15 +17,18 @@
17
17
  */
18
18
  import { createPrivateKey, generateKeyPairSync, createHash } from 'node:crypto';
19
19
  import { config } from '../config/index.js';
20
- import { getPool } from './database.js';
20
+ import { getPool, runDatabaseTransaction } from './database.js';
21
21
  import { logger } from './logger.js';
22
- import { encryptPrivateKey, decryptPrivateKey } from './signing-key-crypto.js';
22
+ import { encryptPrivateKey, decryptPrivateKey, SigningKeyCryptoError, } from './signing-key-crypto.js';
23
23
  // ---------------------------------------------------------------------------
24
24
  // Cached JWKS for JWT verification (admin auth middleware, etc.)
25
25
  // ---------------------------------------------------------------------------
26
26
  /** In-memory cache for the active JWK set — avoids a DB round-trip per request */
27
27
  let cachedJwks = null;
28
+ /** Millisecond timestamp of the currently installed cache entry. */
28
29
  let jwksCacheTimestamp = 0;
30
+ /** Monotonic invalidation marker used to reject stale in-flight cache installations. */
31
+ let jwksCacheGeneration = 0;
29
32
  /** Cache TTL for the active JWK set — 60 seconds matches system-config cache */
30
33
  const JWKS_CACHE_TTL_MS = 60_000;
31
34
  /**
@@ -42,15 +45,22 @@ export async function getActiveJwks() {
42
45
  if (cachedJwks && now - jwksCacheTimestamp < JWKS_CACHE_TTL_MS) {
43
46
  return cachedJwks;
44
47
  }
48
+ const loadGeneration = jwksCacheGeneration;
45
49
  const records = await loadSigningKeysFromDb();
46
- cachedJwks = signingKeysToJwks(records);
47
- jwksCacheTimestamp = now;
48
- return cachedJwks;
50
+ const loadedJwks = signingKeysToJwks(records);
51
+ // A mutation may invalidate the cache while this database read is pending. The caller may use
52
+ // its completed snapshot, but installing it would make later callers observe stale key state.
53
+ if (loadGeneration === jwksCacheGeneration) {
54
+ cachedJwks = loadedJwks;
55
+ jwksCacheTimestamp = now;
56
+ }
57
+ return loadedJwks;
49
58
  }
50
59
  /**
51
60
  * Clear the cached JWK set — useful for testing and after key rotation.
52
61
  */
53
62
  export function clearJwksCache() {
63
+ jwksCacheGeneration += 1;
54
64
  cachedJwks = null;
55
65
  jwksCacheTimestamp = 0;
56
66
  }
@@ -120,10 +130,8 @@ export function signingKeysToJwks(records) {
120
130
  try {
121
131
  keys.push(pemToJwk(record.privateKey, record.kid));
122
132
  }
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');
133
+ catch {
134
+ throw invalidSigningKeyRecord(record.kid);
127
135
  }
128
136
  }
129
137
  return { keys };
@@ -146,38 +154,51 @@ export async function loadSigningKeysFromDb() {
146
154
  WHERE status IN ('active', 'retired')
147
155
  AND (expires_at IS NULL OR expires_at > NOW())
148
156
  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.
157
+ // Map snake_case DB columns to camelCase TypeScript interface. Every private key must use the
158
+ // authenticated encrypted representation; accepting a partial or plaintext row would weaken
159
+ // the storage guarantee and could expose key material through later error handling.
151
160
  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);
161
+ if (!row.encrypted || !row.private_key_iv || !row.private_key_tag) {
162
+ throw invalidSigningKeyRecord(row.kid);
163
+ }
164
+ try {
165
+ const privateKey = decryptPrivateKey(row.private_key, row.private_key_iv, row.private_key_tag, config.signingKeyEncryptionKey);
166
+ return {
167
+ id: row.id,
168
+ kid: row.kid,
169
+ algorithm: row.algorithm,
170
+ publicKey: row.public_key,
171
+ privateKey,
172
+ status: row.status,
173
+ activatedAt: row.activated_at,
174
+ retiredAt: row.retired_at,
175
+ expiresAt: row.expires_at,
176
+ };
156
177
  }
157
- else {
158
- // Legacy plaintext key — use as-is
159
- privateKey = row.private_key;
178
+ catch {
179
+ throw invalidSigningKeyRecord(row.kid);
160
180
  }
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
181
  });
173
182
  }
183
+ /**
184
+ * Creates and records the fixed failure used for any unusable stored signing key.
185
+ *
186
+ * Only the public key identifier is logged. Ciphertext, metadata, parser errors, and stack traces
187
+ * are deliberately omitted because they can reveal secrets or internal deployment details.
188
+ *
189
+ * @param kid - Public identifier of the unusable database row.
190
+ * @returns A safe domain error suitable for propagation to startup and CLI boundaries.
191
+ */
192
+ function invalidSigningKeyRecord(kid) {
193
+ logger.error({ event: 'signing-key-record-invalid', kid }, 'Signing key record is invalid');
194
+ return new SigningKeyCryptoError('Signing key record is invalid');
195
+ }
174
196
  /**
175
197
  * Ensure at least one active signing key exists in the database.
176
198
  *
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
199
+ * Called at application startup. A transaction-scoped table lock serializes the active-key
200
+ * recheck and optional encrypted insert, so simultaneous new processes cannot each create a
201
+ * bootstrap key. Every caller reloads the committed rows after releasing the lock.
181
202
  *
182
203
  * This guarantees the OIDC provider can always start, even on a fresh database
183
204
  * that has no signing keys yet (e.g., first run after migrations).
@@ -185,22 +206,26 @@ export async function loadSigningKeysFromDb() {
185
206
  * @returns The JWK key set containing all active and retired keys
186
207
  */
187
208
  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) {
209
+ await runDatabaseTransaction(async () => {
210
+ const pool = getPool();
211
+ await pool.query('LOCK TABLE signing_keys IN SHARE ROW EXCLUSIVE MODE');
212
+ // Recheck under the lock because another process may have inserted the first key while this
213
+ // process was waiting. Only the lock holder that still sees no active key may create one.
214
+ const lockedRecords = await loadSigningKeysFromDb();
215
+ if (lockedRecords.some((record) => record.status === 'active'))
216
+ return;
192
217
  logger.warn('No active signing keys found — auto-generating a new ES256 key pair');
193
218
  const keyPair = generateES256KeyPair();
194
- const pool = getPool();
195
219
  // Encrypt the private key before storage (AES-256-GCM)
196
220
  const { encrypted, iv, tag } = encryptPrivateKey(keyPair.privateKeyPem, config.signingKeyEncryptionKey);
197
221
  // Insert the new key with encrypted private key
198
222
  await pool.query(`INSERT INTO signing_keys (kid, algorithm, public_key, private_key, private_key_iv, private_key_tag, encrypted, status, activated_at)
199
223
  VALUES ($1, $2, $3, $4, $5, $6, true, 'active', NOW())`, [keyPair.kid, keyPair.algorithm, keyPair.publicKeyPem, encrypted, iv, tag]);
200
224
  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
- }
225
+ });
226
+ // Every caller reloads after the lock transaction commits. This gives both the inserting caller
227
+ // and any waiter the same authoritative winner, including its database-generated identifier.
228
+ const records = await loadSigningKeysFromDb();
204
229
  return signingKeysToJwks(records);
205
230
  }
206
231
  //# sourceMappingURL=signing-keys.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"signing-keys.js","sourceRoot":"","sources":["../../src/lib/signing-keys.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"signing-keys.js","sourceRoot":"","sources":["../../src/lib/signing-keys.ts"],"names":[],"mappings":"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"}