@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
@@ -5,11 +5,10 @@
5
5
  * `/api/admin/*`) against brute-force attacks and automated abuse.
6
6
  * GET requests are read-only and pass through without rate limiting.
7
7
  *
8
- * Key format: `ratelimit:admin:{ip}` — per-IP only since admin-auth
9
- * already restricts access to authenticated super-admin users. There
10
- * is no per-user key because the IP-based counter is sufficient:
11
- * legitimate admins won't make 60 write requests per minute, and
12
- * attackers from the same IP are blocked regardless of identity.
8
+ * Key format: `ratelimit:admin:{ip}`. The limiter intentionally runs before
9
+ * body parsing and authentication so rejected large requests consume minimal
10
+ * resources. A per-IP counter covers both unauthenticated abuse and legitimate
11
+ * administrators without requiring an authenticated user identifier.
13
12
  *
14
13
  * Limit: 60 requests / 60 seconds (sliding window).
15
14
  *
@@ -17,9 +16,9 @@
17
16
  * provides Redis INCR+EXPIRE sliding window and graceful degradation
18
17
  * on Redis failure (allows request + logs warning).
19
18
  *
20
- * Placement: mount as global middleware in `src/server.ts` before the
21
- * admin route routers. The path + method check ensures only write
22
- * requests to `/api/admin/*` are counted.
19
+ * Placement: mount after Admin CORS and before body parsing, authentication,
20
+ * and Admin route routers. The path + method check ensures only write requests
21
+ * to `/api/admin/*` are counted.
23
22
  *
24
23
  * @module middleware/admin-rate-limiter
25
24
  */
@@ -1 +1 @@
1
- {"version":3,"file":"admin-rate-limiter.d.ts","sourceRoot":"","sources":["../../src/middleware/admin-rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAEtC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAO/D,wCAAwC;AACxC,eAAO,MAAM,iBAAiB,gBAAgB,CAAC;AAE/C;;;GAGG;AACH,eAAO,MAAM,mBAAmB,aAA8C,CAAC;AAE/E;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAG9B,CAAC;AAMF;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAyC7C"}
1
+ {"version":3,"file":"admin-rate-limiter.d.ts","sourceRoot":"","sources":["../../src/middleware/admin-rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAEtC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAO/D,wCAAwC;AACxC,eAAO,MAAM,iBAAiB,gBAAgB,CAAC;AAE/C;;;GAGG;AACH,eAAO,MAAM,mBAAmB,aAA8C,CAAC;AAE/E;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAG9B,CAAC;AAMF;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAyC7C"}
@@ -5,11 +5,10 @@
5
5
  * `/api/admin/*`) against brute-force attacks and automated abuse.
6
6
  * GET requests are read-only and pass through without rate limiting.
7
7
  *
8
- * Key format: `ratelimit:admin:{ip}` — per-IP only since admin-auth
9
- * already restricts access to authenticated super-admin users. There
10
- * is no per-user key because the IP-based counter is sufficient:
11
- * legitimate admins won't make 60 write requests per minute, and
12
- * attackers from the same IP are blocked regardless of identity.
8
+ * Key format: `ratelimit:admin:{ip}`. The limiter intentionally runs before
9
+ * body parsing and authentication so rejected large requests consume minimal
10
+ * resources. A per-IP counter covers both unauthenticated abuse and legitimate
11
+ * administrators without requiring an authenticated user identifier.
13
12
  *
14
13
  * Limit: 60 requests / 60 seconds (sliding window).
15
14
  *
@@ -17,9 +16,9 @@
17
16
  * provides Redis INCR+EXPIRE sliding window and graceful degradation
18
17
  * on Redis failure (allows request + logs warning).
19
18
  *
20
- * Placement: mount as global middleware in `src/server.ts` before the
21
- * admin route routers. The path + method check ensures only write
22
- * requests to `/api/admin/*` are counted.
19
+ * Placement: mount after Admin CORS and before body parsing, authentication,
20
+ * and Admin route routers. The path + method check ensures only write requests
21
+ * to `/api/admin/*` are counted.
23
22
  *
24
23
  * @module middleware/admin-rate-limiter
25
24
  */
@@ -1 +1 @@
1
- {"version":3,"file":"admin-rate-limiter.js","sourceRoot":"","sources":["../../src/middleware/admin-rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,8EAA8E;AAC9E,6DAA6D;AAC7D,8EAA8E;AAE9E,wCAAwC;AACxC,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE/E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAoB;IAC/C,GAAG,EAAE,EAAE;IACP,aAAa,EAAE,EAAE;CAClB,CAAC;AAEF,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,KAAK,UAAU,0BAA0B,CAAC,GAAG,EAAE,IAAI;QACxD,8DAA8D;QAC9D,mEAAmE;QACnE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACpF,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAED,kEAAkE;QAClE,8DAA8D;QAC9D,sDAAsD;QACtD,MAAM,GAAG,GAAG,mBAAmB,GAAG,CAAC,EAAE,EAAE,CAAC;QAExC,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;QAE3D,+DAA+D;QAC/D,iDAAiD;QACjD,GAAG,CAAC,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3D,GAAG,CAAC,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;QAE3D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,IAAI,CACT;gBACE,MAAM,EAAE,2BAA2B;gBACnC,MAAM,EAAE,GAAG,CAAC,MAAM;aACnB,EACD,+BAA+B,CAChC,CAAC;YAEF,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;YACjB,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;YAClD,GAAG,CAAC,IAAI,GAAG;gBACT,KAAK,EAAE,mBAAmB;gBAC1B,OAAO,EAAE,wDAAwD;gBACjE,WAAW,EAAE,MAAM,CAAC,UAAU;aAC/B,CAAC;YACF,OAAO;QACT,CAAC;QAED,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"admin-rate-limiter.js","sourceRoot":"","sources":["../../src/middleware/admin-rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,8EAA8E;AAC9E,6DAA6D;AAC7D,8EAA8E;AAE9E,wCAAwC;AACxC,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE/E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAoB;IAC/C,GAAG,EAAE,EAAE;IACP,aAAa,EAAE,EAAE;CAClB,CAAC;AAEF,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,KAAK,UAAU,0BAA0B,CAAC,GAAG,EAAE,IAAI;QACxD,8DAA8D;QAC9D,mEAAmE;QACnE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACpF,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAED,kEAAkE;QAClE,8DAA8D;QAC9D,sDAAsD;QACtD,MAAM,GAAG,GAAG,mBAAmB,GAAG,CAAC,EAAE,EAAE,CAAC;QAExC,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;QAE3D,+DAA+D;QAC/D,iDAAiD;QACjD,GAAG,CAAC,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3D,GAAG,CAAC,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;QAE3D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,IAAI,CACT;gBACE,MAAM,EAAE,2BAA2B;gBACnC,MAAM,EAAE,GAAG,CAAC,MAAM;aACnB,EACD,+BAA+B,CAChC,CAAC;YAEF,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;YACjB,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;YAClD,GAAG,CAAC,IAAI,GAAG;gBACT,KAAK,EAAE,mBAAmB;gBAC1B,OAAO,EAAE,wDAAwD;gBACjE,WAAW,EAAE,MAAM,CAAC,UAAU;aAC/B,CAAC;YACF,OAAO;QACT,CAAC;QAED,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC"}
@@ -1,34 +1,15 @@
1
- /**
2
- * SHA-256 client secret pre-hashing middleware.
3
- *
4
- * Transforms client_secret values in incoming requests by replacing
5
- * the plaintext with its SHA-256 hash BEFORE oidc-provider processes them.
6
- *
7
- * Supports:
8
- * - client_secret_basic: Authorization: Basic base64(client_id:secret)
9
- * - client_secret_post: client_secret in POST body
10
- *
11
- * This allows oidc-provider's built-in string comparison to work
12
- * with our SHA-256-hashed secret storage — no monkey-patching needed.
13
- *
14
- * How it works:
15
- * 1. At secret generation: SHA-256(secret) is stored in the DB
16
- * 2. At request time: This middleware hashes the presented secret
17
- * 3. In the adapter: findForOidc() returns SHA-256 hash as client_secret
18
- * 4. oidc-provider compares: SHA-256(presented) === SHA-256(stored) → ✅ MATCH
19
- *
20
- * Security: SHA-256 is appropriate for machine-generated, high-entropy
21
- * client secrets (48 bytes / 384 bits of randomness).
22
- */
1
+ /** Confidential-client active-secret bridge for oidc-provider. */
23
2
  import type { Middleware } from 'koa';
3
+ /** Optional controls for the active-secret bridge. */
4
+ export interface ClientSecretBridgeOptions {
5
+ /** Credential-free test hook awaited after validation and immediately before provider handoff. */
6
+ afterCredentialValidation?: () => Promise<void>;
7
+ }
24
8
  /**
25
- * Create middleware that pre-hashes client secrets with SHA-256.
26
- *
27
- * Must be mounted BEFORE oidc-provider's callback handler on the
28
- * OIDC router. Transparently transforms secrets so oidc-provider
29
- * can compare them against stored SHA-256 hashes.
9
+ * Create the bounded active-secret bridge mounted immediately before oidc-provider.
30
10
  *
31
- * @returns Koa middleware function
11
+ * @param options - Optional credential-free validation/handoff test hook
12
+ * @returns Koa middleware
32
13
  */
33
- export declare function clientSecretHash(): Middleware;
14
+ export declare function clientSecretHash(options?: ClientSecretBridgeOptions): Middleware;
34
15
  //# sourceMappingURL=client-secret-hash.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client-secret-hash.d.ts","sourceRoot":"","sources":["../../src/middleware/client-secret-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAStC;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAqC7C"}
1
+ {"version":3,"file":"client-secret-hash.d.ts","sourceRoot":"","sources":["../../src/middleware/client-secret-hash.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAElE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAiBtC,sDAAsD;AACtD,MAAM,WAAW,yBAAyB;IACxC,kGAAkG;IAClG,yBAAyB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AA6HD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,yBAA8B,GAAG,UAAU,CAoEpF"}
@@ -1,74 +1,176 @@
1
- /**
2
- * SHA-256 client secret pre-hashing middleware.
3
- *
4
- * Transforms client_secret values in incoming requests by replacing
5
- * the plaintext with its SHA-256 hash BEFORE oidc-provider processes them.
6
- *
7
- * Supports:
8
- * - client_secret_basic: Authorization: Basic base64(client_id:secret)
9
- * - client_secret_post: client_secret in POST body
10
- *
11
- * This allows oidc-provider's built-in string comparison to work
12
- * with our SHA-256-hashed secret storage — no monkey-patching needed.
13
- *
14
- * How it works:
15
- * 1. At secret generation: SHA-256(secret) is stored in the DB
16
- * 2. At request time: This middleware hashes the presented secret
17
- * 3. In the adapter: findForOidc() returns SHA-256 hash as client_secret
18
- * 4. oidc-provider compares: SHA-256(presented) === SHA-256(stored) → ✅ MATCH
19
- *
20
- * Security: SHA-256 is appropriate for machine-generated, high-entropy
21
- * client secrets (48 bytes / 384 bits of randomness).
22
- */
23
- import { createHash } from 'node:crypto';
24
- /**
25
- * Compute SHA-256 hash of a string, returned as hex.
26
- */
27
- function sha256(input) {
28
- return createHash('sha256').update(input).digest('hex');
1
+ /** Confidential-client active-secret bridge for oidc-provider. */
2
+ import * as clientCrypto from '../clients/crypto.js';
3
+ import { getClientByClientId } from '../clients/service.js';
4
+ import { findActiveSecretIdBySha256, getActiveLegacySecretHashes, getLatestActiveSha256, } from '../clients/secret-repository.js';
5
+ import { checkRateLimitStrict } from '../auth/rate-limiter.js';
6
+ import { TOKEN_RATE_LIMIT } from './token-rate-limiter.js';
7
+ const MAX_CLIENT_ID_LENGTH = 255;
8
+ const MAX_SECRET_LENGTH = 512;
9
+ const BUSY_RETRY_SECONDS = 1;
10
+ let legacyBatchActive = false;
11
+ /** Identify a plain request-body record without coercing arrays or scalar values. */
12
+ function isRequestBody(value) {
13
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
14
+ }
15
+ /** Return a parsed object body without coercing arrays or scalar values. */
16
+ function requestBody(ctx) {
17
+ const body = ctx.request.body;
18
+ return isRequestBody(body) ? body : undefined;
19
+ }
20
+ /** Parse bounded HTTP Basic credentials while leaving malformed input to the provider. */
21
+ function basicCredential(ctx) {
22
+ const authorization = ctx.headers.authorization;
23
+ if (authorization === undefined || !authorization.startsWith('Basic '))
24
+ return undefined;
25
+ try {
26
+ const decoded = Buffer.from(authorization.slice(6), 'base64').toString('utf8');
27
+ const separator = decoded.indexOf(':');
28
+ const clientId = decoded.slice(0, separator);
29
+ const plaintext = decoded.slice(separator + 1);
30
+ if (separator < 1 ||
31
+ clientId.length > MAX_CLIENT_ID_LENGTH ||
32
+ plaintext.length < 1 ||
33
+ plaintext.length > MAX_SECRET_LENGTH) {
34
+ return undefined;
35
+ }
36
+ return {
37
+ clientId,
38
+ plaintext,
39
+ method: 'client_secret_basic',
40
+ replace(canonicalValue) {
41
+ const encoded = Buffer.from(`${clientId}:${canonicalValue}`).toString('base64');
42
+ ctx.headers.authorization = `Basic ${encoded}`;
43
+ ctx.req.headers.authorization = `Basic ${encoded}`;
44
+ },
45
+ };
46
+ }
47
+ catch {
48
+ return undefined;
49
+ }
50
+ }
51
+ /** Parse bounded form credentials from the body already parsed by the OIDC router. */
52
+ function postCredential(ctx) {
53
+ const body = requestBody(ctx);
54
+ const clientId = body?.client_id;
55
+ const plaintext = body?.client_secret;
56
+ if (body === undefined ||
57
+ typeof clientId !== 'string' ||
58
+ clientId.length < 1 ||
59
+ clientId.length > MAX_CLIENT_ID_LENGTH ||
60
+ typeof plaintext !== 'string' ||
61
+ plaintext.length < 1 ||
62
+ plaintext.length > MAX_SECRET_LENGTH) {
63
+ return undefined;
64
+ }
65
+ const mutableBody = body;
66
+ return {
67
+ clientId,
68
+ plaintext,
69
+ method: 'client_secret_post',
70
+ replace(canonicalValue) {
71
+ mutableBody.client_secret = canonicalValue;
72
+ const rawBody = Reflect.get(ctx.req, 'body');
73
+ if (typeof rawBody === 'object' && rawBody !== null && !Array.isArray(rawBody)) {
74
+ Reflect.set(rawBody, 'client_secret', canonicalValue);
75
+ }
76
+ },
77
+ };
78
+ }
79
+ /** Return one supported credential source and reject ambiguous dual-mechanism input. */
80
+ function presentedCredential(ctx) {
81
+ const basic = basicCredential(ctx);
82
+ const post = postCredential(ctx);
83
+ if (basic !== undefined && post !== undefined)
84
+ return undefined;
85
+ return basic ?? post;
86
+ }
87
+ /** Return the resolved tenant identifier without trusting request-controlled text. */
88
+ function organizationId(ctx) {
89
+ const organization = ctx.state?.organization;
90
+ if (typeof organization !== 'object' || organization === null || Array.isArray(organization)) {
91
+ return undefined;
92
+ }
93
+ const id = Reflect.get(organization, 'id');
94
+ return typeof id === 'string' && id.length > 0 ? id : undefined;
95
+ }
96
+ /** Emit the existing fixed token rate-limit response without credential detail. */
97
+ function denyProtectedWork(ctx, retryAfter) {
98
+ ctx.status = 429;
99
+ ctx.set('Retry-After', String(retryAfter));
100
+ ctx.body = {
101
+ error: 'rate_limit_exceeded',
102
+ error_description: 'Too many token requests. Please try again later.',
103
+ retry_after: retryAfter,
104
+ };
105
+ }
106
+ /** Rewrite a proven credential and continue to oidc-provider. */
107
+ async function handOffValidatedCredential(credential, canonicalValue, options, next) {
108
+ credential.replace(canonicalValue);
109
+ await options.afterCredentialValidation?.();
110
+ await next();
29
111
  }
30
112
  /**
31
- * Create middleware that pre-hashes client secrets with SHA-256.
113
+ * Create the bounded active-secret bridge mounted immediately before oidc-provider.
32
114
  *
33
- * Must be mounted BEFORE oidc-provider's callback handler on the
34
- * OIDC router. Transparently transforms secrets so oidc-provider
35
- * can compare them against stored SHA-256 hashes.
36
- *
37
- * @returns Koa middleware function
115
+ * @param options - Optional credential-free validation/handoff test hook
116
+ * @returns Koa middleware
38
117
  */
39
- export function clientSecretHash() {
118
+ export function clientSecretHash(options = {}) {
40
119
  return async (ctx, next) => {
41
- // Handle client_secret_basic: Authorization: Basic base64(client_id:secret)
42
- const authHeader = ctx.headers.authorization;
43
- if (authHeader && authHeader.startsWith('Basic ')) {
120
+ const credential = presentedCredential(ctx);
121
+ const tenantId = organizationId(ctx);
122
+ if (credential === undefined || tenantId === undefined)
123
+ return next();
124
+ try {
125
+ const client = await getClientByClientId(credential.clientId);
126
+ if (client === null ||
127
+ client.organizationId !== tenantId ||
128
+ client.clientType !== 'confidential' ||
129
+ client.status !== 'active' ||
130
+ client.tokenEndpointAuthMethod !== credential.method) {
131
+ return next();
132
+ }
133
+ const canonicalValue = await getLatestActiveSha256(client.id);
134
+ if (canonicalValue === null)
135
+ return next();
136
+ const presentedSha256 = clientCrypto.sha256Secret(credential.plaintext);
137
+ const modernMatchId = await findActiveSecretIdBySha256(client.id, presentedSha256);
138
+ if (modernMatchId !== null) {
139
+ return handOffValidatedCredential(credential, canonicalValue, options, next);
140
+ }
141
+ const legacySecrets = await getActiveLegacySecretHashes(client.id);
142
+ if (legacySecrets.length === 0 || legacySecrets.length > 10)
143
+ return next();
144
+ if (legacyBatchActive) {
145
+ denyProtectedWork(ctx, BUSY_RETRY_SECONDS);
146
+ return;
147
+ }
148
+ legacyBatchActive = true;
149
+ let legacyMatched = false;
44
150
  try {
45
- const encoded = authHeader.slice(6); // strip 'Basic '
46
- const decoded = Buffer.from(encoded, 'base64').toString('utf8');
47
- const colonIndex = decoded.indexOf(':');
48
- if (colonIndex !== -1) {
49
- const clientId = decoded.slice(0, colonIndex);
50
- const secret = decoded.slice(colonIndex + 1);
51
- if (secret.length > 0) {
52
- // Hash the secret and re-encode the Basic auth header
53
- const hashedSecret = sha256(secret);
54
- const newEncoded = Buffer.from(`${clientId}:${hashedSecret}`).toString('base64');
55
- ctx.headers.authorization = `Basic ${newEncoded}`;
56
- // Also update the raw request header for oidc-provider
57
- ctx.req.headers.authorization = `Basic ${newEncoded}`;
151
+ const limit = await checkRateLimitStrict(`ratelimit:client-secret-legacy:${tenantId}:${client.id}`, TOKEN_RATE_LIMIT);
152
+ if (!limit.allowed) {
153
+ denyProtectedWork(ctx, limit.retryAfter);
154
+ return;
155
+ }
156
+ for (const legacy of legacySecrets) {
157
+ if (await clientCrypto.verifySecretHash(legacy.hash, credential.plaintext)) {
158
+ legacyMatched = true;
159
+ break;
58
160
  }
59
161
  }
60
162
  }
61
- catch {
62
- // Malformed Basic auth — let oidc-provider handle the error
163
+ finally {
164
+ legacyBatchActive = false;
165
+ }
166
+ if (legacyMatched) {
167
+ return handOffValidatedCredential(credential, canonicalValue, options, next);
63
168
  }
64
169
  }
65
- // Handle client_secret_post: client_secret in POST body
66
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
67
- const body = ctx.request.body;
68
- if (body && typeof body.client_secret === 'string' && body.client_secret.length > 0) {
69
- body.client_secret = sha256(body.client_secret);
170
+ catch {
171
+ // Fail closed by leaving the credential untouched for the provider's minimal rejection.
70
172
  }
71
- await next();
173
+ return next();
72
174
  };
73
175
  }
74
176
  //# sourceMappingURL=client-secret-hash.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client-secret-hash.js","sourceRoot":"","sources":["../../src/middleware/client-secret-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC;;GAEG;AACH,SAAS,MAAM,CAAC,KAAa;IAC3B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,4EAA4E;QAC5E,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;QAC7C,IAAI,UAAU,IAAI,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClD,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB;gBACtD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAChE,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAExC,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;oBACtB,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;oBAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;oBAE7C,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACtB,sDAAsD;wBACtD,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;wBACpC,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,IAAI,YAAY,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;wBACjF,GAAG,CAAC,OAAO,CAAC,aAAa,GAAG,SAAS,UAAU,EAAE,CAAC;wBAClD,uDAAuD;wBACvD,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,GAAG,SAAS,UAAU,EAAE,CAAC;oBACxD,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,4DAA4D;YAC9D,CAAC;QACH,CAAC;QAED,wDAAwD;QACxD,8DAA8D;QAC9D,MAAM,IAAI,GAAI,GAAG,CAAC,OAAe,CAAC,IAAI,CAAC;QACvC,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,aAAa,KAAK,QAAQ,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpF,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAClD,CAAC;QAED,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"client-secret-hash.js","sourceRoot":"","sources":["../../src/middleware/client-secret-hash.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,20 @@
1
+ /**
2
+ * Liveness probe handler — GET /health.
3
+ *
4
+ * Reports whether the server process can still reach its dependencies. Or
5
+ * container orchestration uses this as a liveness probe, so every dependency
6
+ * check is bounded: a hung database or cache must fail the probe quickly
7
+ * (503) instead of holding the response open.
8
+ *
9
+ * Returns 200 + { status: 'healthy', checks } when both backends respond, or
10
+ * 503 + { status: 'unhealthy', checks } when either is degraded or too slow.
11
+ */
1
12
  import type { Middleware } from 'koa';
13
+ /**
14
+ * Create the liveness probe middleware.
15
+ *
16
+ * Both checks run concurrently under an individual timeout, so one slow
17
+ * dependency cannot delay the other and a hang cannot keep the probe pending.
18
+ */
2
19
  export declare function healthCheck(): Middleware;
3
20
  //# sourceMappingURL=health.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/middleware/health.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAItC,wBAAgB,WAAW,IAAI,UAAU,CAgCxC"}
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/middleware/health.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAKtC;;;;;GAKG;AACH,wBAAgB,WAAW,IAAI,UAAU,CAqBxC"}
@@ -1,29 +1,33 @@
1
+ /**
2
+ * Liveness probe handler — GET /health.
3
+ *
4
+ * Reports whether the server process can still reach its dependencies. Or
5
+ * container orchestration uses this as a liveness probe, so every dependency
6
+ * check is bounded: a hung database or cache must fail the probe quickly
7
+ * (503) instead of holding the response open.
8
+ *
9
+ * Returns 200 + { status: 'healthy', checks } when both backends respond, or
10
+ * 503 + { status: 'unhealthy', checks } when either is degraded or too slow.
11
+ */
1
12
  import { getPool } from '../lib/database.js';
2
13
  import { getRedis } from '../lib/redis.js';
14
+ import { withTimeout } from '../lib/with-timeout.js';
15
+ /**
16
+ * Create the liveness probe middleware.
17
+ *
18
+ * Both checks run concurrently under an individual timeout, so one slow
19
+ * dependency cannot delay the other and a hang cannot keep the probe pending.
20
+ */
3
21
  export function healthCheck() {
4
22
  return async (ctx) => {
5
23
  const checks = { server: 'ok' };
6
- let healthy = true;
7
- // Check PostgreSQL
8
- try {
9
- const pool = getPool();
10
- await pool.query('SELECT 1');
11
- checks.database = 'ok';
12
- }
13
- catch {
14
- checks.database = 'error';
15
- healthy = false;
16
- }
17
- // Check Redis
18
- try {
19
- const redis = getRedis();
20
- await redis.ping();
21
- checks.redis = 'ok';
22
- }
23
- catch {
24
- checks.redis = 'error';
25
- healthy = false;
26
- }
24
+ const [dbResult, redisResult] = await Promise.allSettled([
25
+ withTimeout(getPool().query('SELECT 1'), 'database'),
26
+ withTimeout(getRedis().ping(), 'redis'),
27
+ ]);
28
+ checks.database = dbResult.status === 'fulfilled' ? 'ok' : 'error';
29
+ checks.redis = redisResult.status === 'fulfilled' ? 'ok' : 'error';
30
+ const healthy = dbResult.status === 'fulfilled' && redisResult.status === 'fulfilled';
27
31
  ctx.status = healthy ? 200 : 503;
28
32
  ctx.body = {
29
33
  status: healthy ? 'healthy' : 'unhealthy',
@@ -1 +1 @@
1
- {"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/middleware/health.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,MAAM,UAAU,WAAW;IACzB,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,MAAM,GAA2B,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACxD,IAAI,OAAO,GAAG,IAAI,CAAC;QAEnB,mBAAmB;QACnB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;YACvB,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAC7B,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,QAAQ,GAAG,OAAO,CAAC;YAC1B,OAAO,GAAG,KAAK,CAAC;QAClB,CAAC;QAED,cAAc;QACd,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;YACzB,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC;QACtB,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC;YACvB,OAAO,GAAG,KAAK,CAAC;QAClB,CAAC;QAED,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QACjC,GAAG,CAAC,IAAI,GAAG;YACT,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW;YACzC,MAAM;YACN,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/middleware/health.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAErD;;;;;GAKG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,MAAM,GAA2B,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAExD,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;YACvD,WAAW,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;YACpD,WAAW,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC;SACxC,CAAC,CAAC;QAEH,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;QACnE,MAAM,CAAC,KAAK,GAAG,WAAW,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;QAEnE,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,KAAK,WAAW,IAAI,WAAW,CAAC,MAAM,KAAK,WAAW,CAAC;QAEtF,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QACjC,GAAG,CAAC,IAAI,GAAG;YACT,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW;YACzC,MAAM;YACN,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ready.d.ts","sourceRoot":"","sources":["../../src/middleware/ready.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAwBtC;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,UAAU,CA6BzC"}
1
+ {"version":3,"file":"ready.d.ts","sourceRoot":"","sources":["../../src/middleware/ready.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAMtC;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,UAAU,CA6BzC"}
@@ -15,18 +15,7 @@
15
15
  import { getPool } from '../lib/database.js';
16
16
  import { getRedis } from '../lib/redis.js';
17
17
  import { logger } from '../lib/logger.js';
18
- /** Maximum time (ms) to wait for each backend check before failing. */
19
- const TIMEOUT_MS = 2000;
20
- /**
21
- * Race a promise against a timeout. Rejects with a descriptive error
22
- * if the promise doesn't resolve within `TIMEOUT_MS`.
23
- */
24
- function withTimeout(promise, label) {
25
- return Promise.race([
26
- promise,
27
- new Promise((_, reject) => setTimeout(() => reject(new Error(`${label} timeout after ${TIMEOUT_MS}ms`)), TIMEOUT_MS)),
28
- ]);
29
- }
18
+ import { withTimeout } from '../lib/with-timeout.js';
30
19
  /**
31
20
  * Create the readiness probe middleware.
32
21
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ready.js","sourceRoot":"","sources":["../../src/middleware/ready.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,uEAAuE;AACvE,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB;;;GAGG;AACH,SAAS,WAAW,CAAI,OAAmB,EAAE,KAAa;IACxD,OAAO,OAAO,CAAC,IAAI,CAAC;QAClB,OAAO;QACP,IAAI,OAAO,CAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC3B,UAAU,CACR,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,KAAK,kBAAkB,UAAU,IAAI,CAAC,CAAC,EACjE,UAAU,CACX,CACF;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,MAAM,GAAoD,EAAE,CAAC;QAEnE,qEAAqE;QACrE,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;YACvD,WAAW,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC;YAC9C,WAAW,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC;SACxC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE;YACP,QAAQ,CAAC,MAAM,KAAK,WAAW;gBAC7B,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE;gBACd,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAEpD,MAAM,CAAC,KAAK;YACV,WAAW,CAAC,MAAM,KAAK,WAAW;gBAChC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE;gBACd,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QAEvD,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAE9C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,EAAE,wBAAwB,CAAC,CAAC;QAC7E,CAAC;QAED,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QAC/B,GAAG,CAAC,IAAI,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;IAC/D,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"ready.js","sourceRoot":"","sources":["../../src/middleware/ready.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAErD;;;;;GAKG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,MAAM,GAAoD,EAAE,CAAC;QAEnE,qEAAqE;QACrE,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;YACvD,WAAW,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC;YAC9C,WAAW,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC;SACxC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE;YACP,QAAQ,CAAC,MAAM,KAAK,WAAW;gBAC7B,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE;gBACd,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAEpD,MAAM,CAAC,KAAK;YACV,WAAW,CAAC,MAAM,KAAK,WAAW;gBAChC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE;gBACd,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QAEvD,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAE9C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,EAAE,wBAAwB,CAAC,CAAC;QAC7E,CAAC;QAED,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QAC/B,GAAG,CAAC,IAAI,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;IAC/D,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Organization-existence guard for organization-prefixed administrative routes.
3
+ *
4
+ * A route value that is not a canonical UUID, or that names no organization, must produce one
5
+ * non-enumerating not-found response. Without this guard the identifier reaches the database and a
6
+ * malformed value surfaces as an internal error instead of a client-visible rejection.
7
+ *
8
+ * Place this middleware after `requirePermission(...)` so a caller without the operation's
9
+ * permission still receives the permission denial before any organization lookup.
10
+ */
11
+ import type { Middleware } from 'koa';
12
+ /**
13
+ * Require the organization named in the route path to exist.
14
+ *
15
+ * @returns Koa middleware that continues only for an existing organization.
16
+ */
17
+ export declare function requireExistingOrganization(): Middleware;
18
+ //# sourceMappingURL=require-existing-organization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-existing-organization.d.ts","sourceRoot":"","sources":["../../src/middleware/require-existing-organization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAQtC;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,UAAU,CAiBxD"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Organization-existence guard for organization-prefixed administrative routes.
3
+ *
4
+ * A route value that is not a canonical UUID, or that names no organization, must produce one
5
+ * non-enumerating not-found response. Without this guard the identifier reaches the database and a
6
+ * malformed value surfaces as an internal error instead of a client-visible rejection.
7
+ *
8
+ * Place this middleware after `requirePermission(...)` so a caller without the operation's
9
+ * permission still receives the permission denial before any organization lookup.
10
+ */
11
+ import { z } from 'zod';
12
+ import { getOrganizationById } from '../organizations/service.js';
13
+ import { recordSecurityDecision } from '../security/decision-context.js';
14
+ const organizationParamSchema = z.object({ orgId: z.string().uuid() });
15
+ /**
16
+ * Require the organization named in the route path to exist.
17
+ *
18
+ * @returns Koa middleware that continues only for an existing organization.
19
+ */
20
+ export function requireExistingOrganization() {
21
+ return async function requireExistingOrganizationMiddleware(ctx, next) {
22
+ const parsed = organizationParamSchema.safeParse(ctx.params);
23
+ const organization = parsed.success ? await getOrganizationById(parsed.data.orgId) : null;
24
+ if (organization === null) {
25
+ recordSecurityDecision(ctx, {
26
+ decisionPoint: 'resource',
27
+ reasonCode: 'resource-not-found',
28
+ outcome: 'deny',
29
+ detail: { resourceType: 'organization' },
30
+ });
31
+ ctx.status = 404;
32
+ ctx.body = { error: 'Organization not found' };
33
+ return;
34
+ }
35
+ await next();
36
+ };
37
+ }
38
+ //# sourceMappingURL=require-existing-organization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-existing-organization.js","sourceRoot":"","sources":["../../src/middleware/require-existing-organization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAEvE;;;;GAIG;AACH,MAAM,UAAU,2BAA2B;IACzC,OAAO,KAAK,UAAU,qCAAqC,CAAC,GAAG,EAAE,IAAI;QACnE,MAAM,MAAM,GAAG,uBAAuB,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC7D,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1F,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;YAC1B,sBAAsB,CAAC,GAAG,EAAE;gBAC1B,aAAa,EAAE,UAAU;gBACzB,UAAU,EAAE,oBAAoB;gBAChC,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE,EAAE,YAAY,EAAE,cAAc,EAAE;aACzC,CAAC,CAAC;YACH,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;YACjB,GAAG,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC;YAC/C,OAAO;QACT,CAAC;QACD,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"require-permission.d.ts","sourceRoot":"","sources":["../../src/middleware/require-permission.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAItC;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,mBAAmB,EAAE,MAAM,EAAE,GAAG,UAAU,CAqC9E"}
1
+ {"version":3,"file":"require-permission.d.ts","sourceRoot":"","sources":["../../src/middleware/require-permission.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAW,UAAU,EAAE,MAAM,KAAK,CAAC;AAmC/C;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,mBAAmB,EAAE,MAAM,EAAE,GAAG,UAAU,CAyC9E"}