@portaidentity/server 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (742) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +33 -0
  4. package/dist/applications/cache.d.ts +64 -0
  5. package/dist/applications/cache.d.ts.map +1 -0
  6. package/dist/applications/cache.js +161 -0
  7. package/dist/applications/cache.js.map +1 -0
  8. package/dist/applications/errors.d.ts +25 -0
  9. package/dist/applications/errors.d.ts.map +1 -0
  10. package/dist/applications/errors.js +31 -0
  11. package/dist/applications/errors.js.map +1 -0
  12. package/dist/applications/index.d.ts +17 -0
  13. package/dist/applications/index.d.ts.map +1 -0
  14. package/dist/applications/index.js +19 -0
  15. package/dist/applications/index.js.map +1 -0
  16. package/dist/applications/repository.d.ts +184 -0
  17. package/dist/applications/repository.d.ts.map +1 -0
  18. package/dist/applications/repository.js +367 -0
  19. package/dist/applications/repository.js.map +1 -0
  20. package/dist/applications/service.d.ts +160 -0
  21. package/dist/applications/service.d.ts.map +1 -0
  22. package/dist/applications/service.js +388 -0
  23. package/dist/applications/service.js.map +1 -0
  24. package/dist/applications/slugs.d.ts +61 -0
  25. package/dist/applications/slugs.d.ts.map +1 -0
  26. package/dist/applications/slugs.js +115 -0
  27. package/dist/applications/slugs.js.map +1 -0
  28. package/dist/applications/types.d.ts +129 -0
  29. package/dist/applications/types.d.ts.map +1 -0
  30. package/dist/applications/types.js +62 -0
  31. package/dist/applications/types.js.map +1 -0
  32. package/dist/auth/csrf.d.ts +71 -0
  33. package/dist/auth/csrf.d.ts.map +1 -0
  34. package/dist/auth/csrf.js +112 -0
  35. package/dist/auth/csrf.js.map +1 -0
  36. package/dist/auth/email-renderer.d.ts +37 -0
  37. package/dist/auth/email-renderer.d.ts.map +1 -0
  38. package/dist/auth/email-renderer.js +116 -0
  39. package/dist/auth/email-renderer.js.map +1 -0
  40. package/dist/auth/email-service.d.ts +154 -0
  41. package/dist/auth/email-service.d.ts.map +1 -0
  42. package/dist/auth/email-service.js +415 -0
  43. package/dist/auth/email-service.js.map +1 -0
  44. package/dist/auth/email-transport.d.ts +65 -0
  45. package/dist/auth/email-transport.d.ts.map +1 -0
  46. package/dist/auth/email-transport.js +82 -0
  47. package/dist/auth/email-transport.js.map +1 -0
  48. package/dist/auth/i18n.d.ts +81 -0
  49. package/dist/auth/i18n.d.ts.map +1 -0
  50. package/dist/auth/i18n.js +395 -0
  51. package/dist/auth/i18n.js.map +1 -0
  52. package/dist/auth/index.d.ts +28 -0
  53. package/dist/auth/index.d.ts.map +1 -0
  54. package/dist/auth/index.js +31 -0
  55. package/dist/auth/index.js.map +1 -0
  56. package/dist/auth/interaction-authority.d.ts +39 -0
  57. package/dist/auth/interaction-authority.d.ts.map +1 -0
  58. package/dist/auth/interaction-authority.js +30 -0
  59. package/dist/auth/interaction-authority.js.map +1 -0
  60. package/dist/auth/magic-link-session.d.ts +81 -0
  61. package/dist/auth/magic-link-session.d.ts.map +1 -0
  62. package/dist/auth/magic-link-session.js +183 -0
  63. package/dist/auth/magic-link-session.js.map +1 -0
  64. package/dist/auth/rate-limiter.d.ts +143 -0
  65. package/dist/auth/rate-limiter.d.ts.map +1 -0
  66. package/dist/auth/rate-limiter.js +220 -0
  67. package/dist/auth/rate-limiter.js.map +1 -0
  68. package/dist/auth/recovery-crypto.d.ts +34 -0
  69. package/dist/auth/recovery-crypto.d.ts.map +1 -0
  70. package/dist/auth/recovery-crypto.js +103 -0
  71. package/dist/auth/recovery-crypto.js.map +1 -0
  72. package/dist/auth/recovery-job-processor.d.ts +24 -0
  73. package/dist/auth/recovery-job-processor.d.ts.map +1 -0
  74. package/dist/auth/recovery-job-processor.js +123 -0
  75. package/dist/auth/recovery-job-processor.js.map +1 -0
  76. package/dist/auth/recovery-job-repository.d.ts +219 -0
  77. package/dist/auth/recovery-job-repository.d.ts.map +1 -0
  78. package/dist/auth/recovery-job-repository.js +340 -0
  79. package/dist/auth/recovery-job-repository.js.map +1 -0
  80. package/dist/auth/recovery-service.d.ts +27 -0
  81. package/dist/auth/recovery-service.d.ts.map +1 -0
  82. package/dist/auth/recovery-service.js +53 -0
  83. package/dist/auth/recovery-service.js.map +1 -0
  84. package/dist/auth/recovery-worker.d.ts +158 -0
  85. package/dist/auth/recovery-worker.d.ts.map +1 -0
  86. package/dist/auth/recovery-worker.js +306 -0
  87. package/dist/auth/recovery-worker.js.map +1 -0
  88. package/dist/auth/template-engine.d.ts +134 -0
  89. package/dist/auth/template-engine.d.ts.map +1 -0
  90. package/dist/auth/template-engine.js +225 -0
  91. package/dist/auth/template-engine.js.map +1 -0
  92. package/dist/auth/token-repository.d.ts +238 -0
  93. package/dist/auth/token-repository.d.ts.map +1 -0
  94. package/dist/auth/token-repository.js +475 -0
  95. package/dist/auth/token-repository.js.map +1 -0
  96. package/dist/auth/tokens.d.ts +45 -0
  97. package/dist/auth/tokens.d.ts.map +1 -0
  98. package/dist/auth/tokens.js +51 -0
  99. package/dist/auth/tokens.js.map +1 -0
  100. package/dist/cli/bootstrap.d.ts +55 -0
  101. package/dist/cli/bootstrap.d.ts.map +1 -0
  102. package/dist/cli/bootstrap.js +90 -0
  103. package/dist/cli/bootstrap.js.map +1 -0
  104. package/dist/cli/commands/health.d.ts +20 -0
  105. package/dist/cli/commands/health.d.ts.map +1 -0
  106. package/dist/cli/commands/health.js +80 -0
  107. package/dist/cli/commands/health.js.map +1 -0
  108. package/dist/cli/commands/init.d.ts +48 -0
  109. package/dist/cli/commands/init.d.ts.map +1 -0
  110. package/dist/cli/commands/init.js +349 -0
  111. package/dist/cli/commands/init.js.map +1 -0
  112. package/dist/cli/commands/migrate.d.ts +19 -0
  113. package/dist/cli/commands/migrate.d.ts.map +1 -0
  114. package/dist/cli/commands/migrate.js +88 -0
  115. package/dist/cli/commands/migrate.js.map +1 -0
  116. package/dist/cli/commands/seed.d.ts +19 -0
  117. package/dist/cli/commands/seed.d.ts.map +1 -0
  118. package/dist/cli/commands/seed.js +71 -0
  119. package/dist/cli/commands/seed.js.map +1 -0
  120. package/dist/cli/commands/user-2fa.d.ts +29 -0
  121. package/dist/cli/commands/user-2fa.d.ts.map +1 -0
  122. package/dist/cli/commands/user-2fa.js +113 -0
  123. package/dist/cli/commands/user-2fa.js.map +1 -0
  124. package/dist/cli/error-handler.d.ts +31 -0
  125. package/dist/cli/error-handler.d.ts.map +1 -0
  126. package/dist/cli/error-handler.js +65 -0
  127. package/dist/cli/error-handler.js.map +1 -0
  128. package/dist/cli/index.d.ts +48 -0
  129. package/dist/cli/index.d.ts.map +1 -0
  130. package/dist/cli/index.js +99 -0
  131. package/dist/cli/index.js.map +1 -0
  132. package/dist/cli/output.d.ts +94 -0
  133. package/dist/cli/output.d.ts.map +1 -0
  134. package/dist/cli/output.js +124 -0
  135. package/dist/cli/output.js.map +1 -0
  136. package/dist/cli/prompt.d.ts +48 -0
  137. package/dist/cli/prompt.d.ts.map +1 -0
  138. package/dist/cli/prompt.js +96 -0
  139. package/dist/cli/prompt.js.map +1 -0
  140. package/dist/clients/cache.d.ts +63 -0
  141. package/dist/clients/cache.d.ts.map +1 -0
  142. package/dist/clients/cache.js +160 -0
  143. package/dist/clients/cache.js.map +1 -0
  144. package/dist/clients/crypto.d.ts +69 -0
  145. package/dist/clients/crypto.d.ts.map +1 -0
  146. package/dist/clients/crypto.js +83 -0
  147. package/dist/clients/crypto.js.map +1 -0
  148. package/dist/clients/errors.d.ts +27 -0
  149. package/dist/clients/errors.d.ts.map +1 -0
  150. package/dist/clients/errors.js +33 -0
  151. package/dist/clients/errors.js.map +1 -0
  152. package/dist/clients/index.d.ts +19 -0
  153. package/dist/clients/index.d.ts.map +1 -0
  154. package/dist/clients/index.js +23 -0
  155. package/dist/clients/index.js.map +1 -0
  156. package/dist/clients/repository.d.ts +161 -0
  157. package/dist/clients/repository.d.ts.map +1 -0
  158. package/dist/clients/repository.js +318 -0
  159. package/dist/clients/repository.js.map +1 -0
  160. package/dist/clients/resolve-login-methods.d.ts +74 -0
  161. package/dist/clients/resolve-login-methods.d.ts.map +1 -0
  162. package/dist/clients/resolve-login-methods.js +68 -0
  163. package/dist/clients/resolve-login-methods.js.map +1 -0
  164. package/dist/clients/secret-repository.d.ts +109 -0
  165. package/dist/clients/secret-repository.d.ts.map +1 -0
  166. package/dist/clients/secret-repository.js +166 -0
  167. package/dist/clients/secret-repository.js.map +1 -0
  168. package/dist/clients/secret-service.d.ts +87 -0
  169. package/dist/clients/secret-service.d.ts.map +1 -0
  170. package/dist/clients/secret-service.js +204 -0
  171. package/dist/clients/secret-service.js.map +1 -0
  172. package/dist/clients/service.d.ts +158 -0
  173. package/dist/clients/service.d.ts.map +1 -0
  174. package/dist/clients/service.js +523 -0
  175. package/dist/clients/service.js.map +1 -0
  176. package/dist/clients/types.d.ts +214 -0
  177. package/dist/clients/types.d.ts.map +1 -0
  178. package/dist/clients/types.js +77 -0
  179. package/dist/clients/types.js.map +1 -0
  180. package/dist/clients/validators.d.ts +87 -0
  181. package/dist/clients/validators.d.ts.map +1 -0
  182. package/dist/clients/validators.js +164 -0
  183. package/dist/clients/validators.js.map +1 -0
  184. package/dist/config/index.d.ts +23 -0
  185. package/dist/config/index.d.ts.map +1 -0
  186. package/dist/config/index.js +52 -0
  187. package/dist/config/index.js.map +1 -0
  188. package/dist/config/schema.d.ts +35 -0
  189. package/dist/config/schema.d.ts.map +1 -0
  190. package/dist/config/schema.js +167 -0
  191. package/dist/config/schema.js.map +1 -0
  192. package/dist/custom-claims/cache.d.ts +48 -0
  193. package/dist/custom-claims/cache.d.ts.map +1 -0
  194. package/dist/custom-claims/cache.js +122 -0
  195. package/dist/custom-claims/cache.js.map +1 -0
  196. package/dist/custom-claims/errors.d.ts +25 -0
  197. package/dist/custom-claims/errors.d.ts.map +1 -0
  198. package/dist/custom-claims/errors.js +31 -0
  199. package/dist/custom-claims/errors.js.map +1 -0
  200. package/dist/custom-claims/index.d.ts +14 -0
  201. package/dist/custom-claims/index.d.ts.map +1 -0
  202. package/dist/custom-claims/index.js +17 -0
  203. package/dist/custom-claims/index.js.map +1 -0
  204. package/dist/custom-claims/repository.d.ts +147 -0
  205. package/dist/custom-claims/repository.d.ts.map +1 -0
  206. package/dist/custom-claims/repository.js +338 -0
  207. package/dist/custom-claims/repository.js.map +1 -0
  208. package/dist/custom-claims/service.d.ts +129 -0
  209. package/dist/custom-claims/service.d.ts.map +1 -0
  210. package/dist/custom-claims/service.js +296 -0
  211. package/dist/custom-claims/service.js.map +1 -0
  212. package/dist/custom-claims/types.d.ts +132 -0
  213. package/dist/custom-claims/types.d.ts.map +1 -0
  214. package/dist/custom-claims/types.js +61 -0
  215. package/dist/custom-claims/types.js.map +1 -0
  216. package/dist/custom-claims/validators.d.ts +61 -0
  217. package/dist/custom-claims/validators.d.ts.map +1 -0
  218. package/dist/custom-claims/validators.js +136 -0
  219. package/dist/custom-claims/validators.js.map +1 -0
  220. package/dist/index.d.ts +14 -0
  221. package/dist/index.d.ts.map +1 -0
  222. package/dist/index.js +91 -0
  223. package/dist/index.js.map +1 -0
  224. package/dist/lib/admin-permissions.d.ts +147 -0
  225. package/dist/lib/admin-permissions.d.ts.map +1 -0
  226. package/dist/lib/admin-permissions.js +261 -0
  227. package/dist/lib/admin-permissions.js.map +1 -0
  228. package/dist/lib/audit-log.d.ts +45 -0
  229. package/dist/lib/audit-log.d.ts.map +1 -0
  230. package/dist/lib/audit-log.js +79 -0
  231. package/dist/lib/audit-log.js.map +1 -0
  232. package/dist/lib/branding-assets.d.ts +47 -0
  233. package/dist/lib/branding-assets.d.ts.map +1 -0
  234. package/dist/lib/branding-assets.js +93 -0
  235. package/dist/lib/branding-assets.js.map +1 -0
  236. package/dist/lib/bulk-operations.d.ts +54 -0
  237. package/dist/lib/bulk-operations.d.ts.map +1 -0
  238. package/dist/lib/bulk-operations.js +201 -0
  239. package/dist/lib/bulk-operations.js.map +1 -0
  240. package/dist/lib/cursor.d.ts +110 -0
  241. package/dist/lib/cursor.d.ts.map +1 -0
  242. package/dist/lib/cursor.js +115 -0
  243. package/dist/lib/cursor.js.map +1 -0
  244. package/dist/lib/data-export.d.ts +67 -0
  245. package/dist/lib/data-export.d.ts.map +1 -0
  246. package/dist/lib/data-export.js +272 -0
  247. package/dist/lib/data-export.js.map +1 -0
  248. package/dist/lib/data-import-plan.d.ts +50 -0
  249. package/dist/lib/data-import-plan.d.ts.map +1 -0
  250. package/dist/lib/data-import-plan.js +235 -0
  251. package/dist/lib/data-import-plan.js.map +1 -0
  252. package/dist/lib/data-import.d.ts +276 -0
  253. package/dist/lib/data-import.d.ts.map +1 -0
  254. package/dist/lib/data-import.js +1694 -0
  255. package/dist/lib/data-import.js.map +1 -0
  256. package/dist/lib/database.d.ts +22 -0
  257. package/dist/lib/database.d.ts.map +1 -0
  258. package/dist/lib/database.js +103 -0
  259. package/dist/lib/database.js.map +1 -0
  260. package/dist/lib/entity-history.d.ts +52 -0
  261. package/dist/lib/entity-history.d.ts.map +1 -0
  262. package/dist/lib/entity-history.js +121 -0
  263. package/dist/lib/entity-history.js.map +1 -0
  264. package/dist/lib/etag.d.ts +92 -0
  265. package/dist/lib/etag.d.ts.map +1 -0
  266. package/dist/lib/etag.js +155 -0
  267. package/dist/lib/etag.js.map +1 -0
  268. package/dist/lib/image-validator.d.ts +44 -0
  269. package/dist/lib/image-validator.d.ts.map +1 -0
  270. package/dist/lib/image-validator.js +204 -0
  271. package/dist/lib/image-validator.js.map +1 -0
  272. package/dist/lib/logger.d.ts +15 -0
  273. package/dist/lib/logger.d.ts.map +1 -0
  274. package/dist/lib/logger.js +84 -0
  275. package/dist/lib/logger.js.map +1 -0
  276. package/dist/lib/migrator.d.ts +14 -0
  277. package/dist/lib/migrator.d.ts.map +1 -0
  278. package/dist/lib/migrator.js +34 -0
  279. package/dist/lib/migrator.js.map +1 -0
  280. package/dist/lib/redis.d.ts +5 -0
  281. package/dist/lib/redis.d.ts.map +1 -0
  282. package/dist/lib/redis.js +29 -0
  283. package/dist/lib/redis.js.map +1 -0
  284. package/dist/lib/runtime-paths.d.ts +38 -0
  285. package/dist/lib/runtime-paths.d.ts.map +1 -0
  286. package/dist/lib/runtime-paths.js +50 -0
  287. package/dist/lib/runtime-paths.js.map +1 -0
  288. package/dist/lib/session-tracking.d.ts +81 -0
  289. package/dist/lib/session-tracking.d.ts.map +1 -0
  290. package/dist/lib/session-tracking.js +145 -0
  291. package/dist/lib/session-tracking.js.map +1 -0
  292. package/dist/lib/signing-key-crypto.d.ts +53 -0
  293. package/dist/lib/signing-key-crypto.d.ts.map +1 -0
  294. package/dist/lib/signing-key-crypto.js +108 -0
  295. package/dist/lib/signing-key-crypto.js.map +1 -0
  296. package/dist/lib/signing-keys.d.ts +141 -0
  297. package/dist/lib/signing-keys.d.ts.map +1 -0
  298. package/dist/lib/signing-keys.js +206 -0
  299. package/dist/lib/signing-keys.js.map +1 -0
  300. package/dist/lib/stats.d.ts +83 -0
  301. package/dist/lib/stats.d.ts.map +1 -0
  302. package/dist/lib/stats.js +187 -0
  303. package/dist/lib/stats.js.map +1 -0
  304. package/dist/lib/super-admin-protection.d.ts +69 -0
  305. package/dist/lib/super-admin-protection.d.ts.map +1 -0
  306. package/dist/lib/super-admin-protection.js +102 -0
  307. package/dist/lib/super-admin-protection.js.map +1 -0
  308. package/dist/lib/system-config.d.ts +79 -0
  309. package/dist/lib/system-config.d.ts.map +1 -0
  310. package/dist/lib/system-config.js +159 -0
  311. package/dist/lib/system-config.js.map +1 -0
  312. package/dist/middleware/admin-auth.d.ts +100 -0
  313. package/dist/middleware/admin-auth.d.ts.map +1 -0
  314. package/dist/middleware/admin-auth.js +241 -0
  315. package/dist/middleware/admin-auth.js.map +1 -0
  316. package/dist/middleware/admin-cors.d.ts +41 -0
  317. package/dist/middleware/admin-cors.d.ts.map +1 -0
  318. package/dist/middleware/admin-cors.js +83 -0
  319. package/dist/middleware/admin-cors.js.map +1 -0
  320. package/dist/middleware/admin-mutation-audit.d.ts +9 -0
  321. package/dist/middleware/admin-mutation-audit.d.ts.map +1 -0
  322. package/dist/middleware/admin-mutation-audit.js +76 -0
  323. package/dist/middleware/admin-mutation-audit.js.map +1 -0
  324. package/dist/middleware/admin-rate-limiter.d.ts +56 -0
  325. package/dist/middleware/admin-rate-limiter.d.ts.map +1 -0
  326. package/dist/middleware/admin-rate-limiter.js +97 -0
  327. package/dist/middleware/admin-rate-limiter.js.map +1 -0
  328. package/dist/middleware/client-secret-hash.d.ts +34 -0
  329. package/dist/middleware/client-secret-hash.d.ts.map +1 -0
  330. package/dist/middleware/client-secret-hash.js +74 -0
  331. package/dist/middleware/client-secret-hash.js.map +1 -0
  332. package/dist/middleware/error-handler.d.ts +3 -0
  333. package/dist/middleware/error-handler.d.ts.map +1 -0
  334. package/dist/middleware/error-handler.js +93 -0
  335. package/dist/middleware/error-handler.js.map +1 -0
  336. package/dist/middleware/health.d.ts +3 -0
  337. package/dist/middleware/health.d.ts.map +1 -0
  338. package/dist/middleware/health.js +35 -0
  339. package/dist/middleware/health.js.map +1 -0
  340. package/dist/middleware/metrics.d.ts +40 -0
  341. package/dist/middleware/metrics.d.ts.map +1 -0
  342. package/dist/middleware/metrics.js +75 -0
  343. package/dist/middleware/metrics.js.map +1 -0
  344. package/dist/middleware/oidc-client-tenant.d.ts +37 -0
  345. package/dist/middleware/oidc-client-tenant.d.ts.map +1 -0
  346. package/dist/middleware/oidc-client-tenant.js +156 -0
  347. package/dist/middleware/oidc-client-tenant.js.map +1 -0
  348. package/dist/middleware/oidc-cors.d.ts +33 -0
  349. package/dist/middleware/oidc-cors.d.ts.map +1 -0
  350. package/dist/middleware/oidc-cors.js +68 -0
  351. package/dist/middleware/oidc-cors.js.map +1 -0
  352. package/dist/middleware/oidc-preflight-cors.d.ts +54 -0
  353. package/dist/middleware/oidc-preflight-cors.d.ts.map +1 -0
  354. package/dist/middleware/oidc-preflight-cors.js +267 -0
  355. package/dist/middleware/oidc-preflight-cors.js.map +1 -0
  356. package/dist/middleware/prompt-login-reset.d.ts +103 -0
  357. package/dist/middleware/prompt-login-reset.d.ts.map +1 -0
  358. package/dist/middleware/prompt-login-reset.js +204 -0
  359. package/dist/middleware/prompt-login-reset.js.map +1 -0
  360. package/dist/middleware/ready.d.ts +23 -0
  361. package/dist/middleware/ready.d.ts.map +1 -0
  362. package/dist/middleware/ready.js +60 -0
  363. package/dist/middleware/ready.js.map +1 -0
  364. package/dist/middleware/request-logger.d.ts +27 -0
  365. package/dist/middleware/request-logger.d.ts.map +1 -0
  366. package/dist/middleware/request-logger.js +46 -0
  367. package/dist/middleware/request-logger.js.map +1 -0
  368. package/dist/middleware/require-permission.d.ts +28 -0
  369. package/dist/middleware/require-permission.d.ts.map +1 -0
  370. package/dist/middleware/require-permission.js +63 -0
  371. package/dist/middleware/require-permission.js.map +1 -0
  372. package/dist/middleware/require-user-organization.d.ts +18 -0
  373. package/dist/middleware/require-user-organization.d.ts.map +1 -0
  374. package/dist/middleware/require-user-organization.js +53 -0
  375. package/dist/middleware/require-user-organization.js.map +1 -0
  376. package/dist/middleware/root-page.d.ts +62 -0
  377. package/dist/middleware/root-page.d.ts.map +1 -0
  378. package/dist/middleware/root-page.js +134 -0
  379. package/dist/middleware/root-page.js.map +1 -0
  380. package/dist/middleware/security-headers.d.ts +98 -0
  381. package/dist/middleware/security-headers.d.ts.map +1 -0
  382. package/dist/middleware/security-headers.js +159 -0
  383. package/dist/middleware/security-headers.js.map +1 -0
  384. package/dist/middleware/tenant-resolver.d.ts +37 -0
  385. package/dist/middleware/tenant-resolver.d.ts.map +1 -0
  386. package/dist/middleware/tenant-resolver.js +71 -0
  387. package/dist/middleware/tenant-resolver.js.map +1 -0
  388. package/dist/middleware/token-rate-limiter.d.ts +89 -0
  389. package/dist/middleware/token-rate-limiter.d.ts.map +1 -0
  390. package/dist/middleware/token-rate-limiter.js +189 -0
  391. package/dist/middleware/token-rate-limiter.js.map +1 -0
  392. package/dist/oidc/account-finder.d.ts +52 -0
  393. package/dist/oidc/account-finder.d.ts.map +1 -0
  394. package/dist/oidc/account-finder.js +134 -0
  395. package/dist/oidc/account-finder.js.map +1 -0
  396. package/dist/oidc/adapter-factory.d.ts +90 -0
  397. package/dist/oidc/adapter-factory.d.ts.map +1 -0
  398. package/dist/oidc/adapter-factory.js +165 -0
  399. package/dist/oidc/adapter-factory.js.map +1 -0
  400. package/dist/oidc/configuration.d.ts +155 -0
  401. package/dist/oidc/configuration.d.ts.map +1 -0
  402. package/dist/oidc/configuration.js +531 -0
  403. package/dist/oidc/configuration.js.map +1 -0
  404. package/dist/oidc/postgres-adapter.d.ts +159 -0
  405. package/dist/oidc/postgres-adapter.d.ts.map +1 -0
  406. package/dist/oidc/postgres-adapter.js +222 -0
  407. package/dist/oidc/postgres-adapter.js.map +1 -0
  408. package/dist/oidc/protocol-security-observer.d.ts +38 -0
  409. package/dist/oidc/protocol-security-observer.d.ts.map +1 -0
  410. package/dist/oidc/protocol-security-observer.js +93 -0
  411. package/dist/oidc/protocol-security-observer.js.map +1 -0
  412. package/dist/oidc/provider.d.ts +37 -0
  413. package/dist/oidc/provider.d.ts.map +1 -0
  414. package/dist/oidc/provider.js +71 -0
  415. package/dist/oidc/provider.js.map +1 -0
  416. package/dist/oidc/redis-adapter.d.ts +140 -0
  417. package/dist/oidc/redis-adapter.d.ts.map +1 -0
  418. package/dist/oidc/redis-adapter.js +344 -0
  419. package/dist/oidc/redis-adapter.js.map +1 -0
  420. package/dist/organizations/cache.d.ts +60 -0
  421. package/dist/organizations/cache.d.ts.map +1 -0
  422. package/dist/organizations/cache.js +157 -0
  423. package/dist/organizations/cache.js.map +1 -0
  424. package/dist/organizations/errors.d.ts +25 -0
  425. package/dist/organizations/errors.d.ts.map +1 -0
  426. package/dist/organizations/errors.js +31 -0
  427. package/dist/organizations/errors.js.map +1 -0
  428. package/dist/organizations/index.d.ts +15 -0
  429. package/dist/organizations/index.d.ts.map +1 -0
  430. package/dist/organizations/index.js +15 -0
  431. package/dist/organizations/index.js.map +1 -0
  432. package/dist/organizations/repository.d.ts +183 -0
  433. package/dist/organizations/repository.d.ts.map +1 -0
  434. package/dist/organizations/repository.js +354 -0
  435. package/dist/organizations/repository.js.map +1 -0
  436. package/dist/organizations/service.d.ts +168 -0
  437. package/dist/organizations/service.d.ts.map +1 -0
  438. package/dist/organizations/service.js +511 -0
  439. package/dist/organizations/service.js.map +1 -0
  440. package/dist/organizations/slugs.d.ts +57 -0
  441. package/dist/organizations/slugs.d.ts.map +1 -0
  442. package/dist/organizations/slugs.js +130 -0
  443. package/dist/organizations/slugs.js.map +1 -0
  444. package/dist/organizations/types.d.ts +146 -0
  445. package/dist/organizations/types.d.ts.map +1 -0
  446. package/dist/organizations/types.js +49 -0
  447. package/dist/organizations/types.js.map +1 -0
  448. package/dist/rbac/cache.d.ts +110 -0
  449. package/dist/rbac/cache.d.ts.map +1 -0
  450. package/dist/rbac/cache.js +271 -0
  451. package/dist/rbac/cache.js.map +1 -0
  452. package/dist/rbac/errors.d.ts +34 -0
  453. package/dist/rbac/errors.d.ts.map +1 -0
  454. package/dist/rbac/errors.js +43 -0
  455. package/dist/rbac/errors.js.map +1 -0
  456. package/dist/rbac/index.d.ts +17 -0
  457. package/dist/rbac/index.d.ts.map +1 -0
  458. package/dist/rbac/index.js +21 -0
  459. package/dist/rbac/index.js.map +1 -0
  460. package/dist/rbac/mapping-repository.d.ts +121 -0
  461. package/dist/rbac/mapping-repository.d.ts.map +1 -0
  462. package/dist/rbac/mapping-repository.js +224 -0
  463. package/dist/rbac/mapping-repository.js.map +1 -0
  464. package/dist/rbac/permission-repository.d.ts +100 -0
  465. package/dist/rbac/permission-repository.d.ts.map +1 -0
  466. package/dist/rbac/permission-repository.js +190 -0
  467. package/dist/rbac/permission-repository.js.map +1 -0
  468. package/dist/rbac/permission-service.d.ts +85 -0
  469. package/dist/rbac/permission-service.d.ts.map +1 -0
  470. package/dist/rbac/permission-service.js +179 -0
  471. package/dist/rbac/permission-service.js.map +1 -0
  472. package/dist/rbac/role-repository.d.ts +104 -0
  473. package/dist/rbac/role-repository.d.ts.map +1 -0
  474. package/dist/rbac/role-repository.js +202 -0
  475. package/dist/rbac/role-repository.js.map +1 -0
  476. package/dist/rbac/role-service.d.ts +111 -0
  477. package/dist/rbac/role-service.d.ts.map +1 -0
  478. package/dist/rbac/role-service.js +256 -0
  479. package/dist/rbac/role-service.js.map +1 -0
  480. package/dist/rbac/slugs.d.ts +116 -0
  481. package/dist/rbac/slugs.d.ts.map +1 -0
  482. package/dist/rbac/slugs.js +178 -0
  483. package/dist/rbac/slugs.js.map +1 -0
  484. package/dist/rbac/types.d.ts +187 -0
  485. package/dist/rbac/types.d.ts.map +1 -0
  486. package/dist/rbac/types.js +75 -0
  487. package/dist/rbac/types.js.map +1 -0
  488. package/dist/rbac/user-role-service.d.ts +101 -0
  489. package/dist/rbac/user-role-service.d.ts.map +1 -0
  490. package/dist/rbac/user-role-service.js +166 -0
  491. package/dist/rbac/user-role-service.js.map +1 -0
  492. package/dist/routes/applications.d.ts +40 -0
  493. package/dist/routes/applications.d.ts.map +1 -0
  494. package/dist/routes/applications.js +284 -0
  495. package/dist/routes/applications.js.map +1 -0
  496. package/dist/routes/audit.d.ts +31 -0
  497. package/dist/routes/audit.d.ts.map +1 -0
  498. package/dist/routes/audit.js +140 -0
  499. package/dist/routes/audit.js.map +1 -0
  500. package/dist/routes/branding.d.ts +17 -0
  501. package/dist/routes/branding.d.ts.map +1 -0
  502. package/dist/routes/branding.js +104 -0
  503. package/dist/routes/branding.js.map +1 -0
  504. package/dist/routes/bulk.d.ts +37 -0
  505. package/dist/routes/bulk.d.ts.map +1 -0
  506. package/dist/routes/bulk.js +94 -0
  507. package/dist/routes/bulk.js.map +1 -0
  508. package/dist/routes/clients.d.ts +44 -0
  509. package/dist/routes/clients.d.ts.map +1 -0
  510. package/dist/routes/clients.js +353 -0
  511. package/dist/routes/clients.js.map +1 -0
  512. package/dist/routes/config.d.ts +35 -0
  513. package/dist/routes/config.d.ts.map +1 -0
  514. package/dist/routes/config.js +115 -0
  515. package/dist/routes/config.js.map +1 -0
  516. package/dist/routes/custom-claims.d.ts +38 -0
  517. package/dist/routes/custom-claims.d.ts.map +1 -0
  518. package/dist/routes/custom-claims.js +208 -0
  519. package/dist/routes/custom-claims.js.map +1 -0
  520. package/dist/routes/exports.d.ts +21 -0
  521. package/dist/routes/exports.d.ts.map +1 -0
  522. package/dist/routes/exports.js +98 -0
  523. package/dist/routes/exports.js.map +1 -0
  524. package/dist/routes/imports.d.ts +18 -0
  525. package/dist/routes/imports.d.ts.map +1 -0
  526. package/dist/routes/imports.js +72 -0
  527. package/dist/routes/imports.js.map +1 -0
  528. package/dist/routes/interactions.d.ts +111 -0
  529. package/dist/routes/interactions.d.ts.map +1 -0
  530. package/dist/routes/interactions.js +1074 -0
  531. package/dist/routes/interactions.js.map +1 -0
  532. package/dist/routes/invitation.d.ts +31 -0
  533. package/dist/routes/invitation.d.ts.map +1 -0
  534. package/dist/routes/invitation.js +342 -0
  535. package/dist/routes/invitation.js.map +1 -0
  536. package/dist/routes/keys.d.ts +28 -0
  537. package/dist/routes/keys.d.ts.map +1 -0
  538. package/dist/routes/keys.js +91 -0
  539. package/dist/routes/keys.js.map +1 -0
  540. package/dist/routes/magic-link.d.ts +43 -0
  541. package/dist/routes/magic-link.d.ts.map +1 -0
  542. package/dist/routes/magic-link.js +264 -0
  543. package/dist/routes/magic-link.js.map +1 -0
  544. package/dist/routes/organizations.d.ts +38 -0
  545. package/dist/routes/organizations.d.ts.map +1 -0
  546. package/dist/routes/organizations.js +347 -0
  547. package/dist/routes/organizations.js.map +1 -0
  548. package/dist/routes/password-reset.d.ts +65 -0
  549. package/dist/routes/password-reset.d.ts.map +1 -0
  550. package/dist/routes/password-reset.js +493 -0
  551. package/dist/routes/password-reset.js.map +1 -0
  552. package/dist/routes/permissions.d.ts +33 -0
  553. package/dist/routes/permissions.d.ts.map +1 -0
  554. package/dist/routes/permissions.js +166 -0
  555. package/dist/routes/permissions.js.map +1 -0
  556. package/dist/routes/roles.d.ts +37 -0
  557. package/dist/routes/roles.d.ts.map +1 -0
  558. package/dist/routes/roles.js +222 -0
  559. package/dist/routes/roles.js.map +1 -0
  560. package/dist/routes/sessions.d.ts +37 -0
  561. package/dist/routes/sessions.d.ts.map +1 -0
  562. package/dist/routes/sessions.js +121 -0
  563. package/dist/routes/sessions.js.map +1 -0
  564. package/dist/routes/stats.d.ts +28 -0
  565. package/dist/routes/stats.d.ts.map +1 -0
  566. package/dist/routes/stats.js +65 -0
  567. package/dist/routes/stats.js.map +1 -0
  568. package/dist/routes/two-factor-admin.d.ts +54 -0
  569. package/dist/routes/two-factor-admin.d.ts.map +1 -0
  570. package/dist/routes/two-factor-admin.js +344 -0
  571. package/dist/routes/two-factor-admin.js.map +1 -0
  572. package/dist/routes/two-factor.d.ts +31 -0
  573. package/dist/routes/two-factor.d.ts.map +1 -0
  574. package/dist/routes/two-factor.js +595 -0
  575. package/dist/routes/two-factor.js.map +1 -0
  576. package/dist/routes/user-roles.d.ts +33 -0
  577. package/dist/routes/user-roles.d.ts.map +1 -0
  578. package/dist/routes/user-roles.js +129 -0
  579. package/dist/routes/user-roles.js.map +1 -0
  580. package/dist/routes/users.d.ts +61 -0
  581. package/dist/routes/users.d.ts.map +1 -0
  582. package/dist/routes/users.js +868 -0
  583. package/dist/routes/users.js.map +1 -0
  584. package/dist/security/decision-context.d.ts +52 -0
  585. package/dist/security/decision-context.d.ts.map +1 -0
  586. package/dist/security/decision-context.js +150 -0
  587. package/dist/security/decision-context.js.map +1 -0
  588. package/dist/security/decision-event.d.ts +128 -0
  589. package/dist/security/decision-event.d.ts.map +1 -0
  590. package/dist/security/decision-event.js +225 -0
  591. package/dist/security/decision-event.js.map +1 -0
  592. package/dist/security/transport-decision.d.ts +6 -0
  593. package/dist/security/transport-decision.d.ts.map +1 -0
  594. package/dist/security/transport-decision.js +37 -0
  595. package/dist/security/transport-decision.js.map +1 -0
  596. package/dist/server.d.ts +50 -0
  597. package/dist/server.d.ts.map +1 -0
  598. package/dist/server.js +491 -0
  599. package/dist/server.js.map +1 -0
  600. package/dist/two-factor/cache.d.ts +47 -0
  601. package/dist/two-factor/cache.d.ts.map +1 -0
  602. package/dist/two-factor/cache.js +122 -0
  603. package/dist/two-factor/cache.js.map +1 -0
  604. package/dist/two-factor/crypto.d.ts +41 -0
  605. package/dist/two-factor/crypto.d.ts.map +1 -0
  606. package/dist/two-factor/crypto.js +94 -0
  607. package/dist/two-factor/crypto.js.map +1 -0
  608. package/dist/two-factor/errors.d.ts +82 -0
  609. package/dist/two-factor/errors.d.ts.map +1 -0
  610. package/dist/two-factor/errors.js +112 -0
  611. package/dist/two-factor/errors.js.map +1 -0
  612. package/dist/two-factor/index.d.ts +18 -0
  613. package/dist/two-factor/index.d.ts.map +1 -0
  614. package/dist/two-factor/index.js +25 -0
  615. package/dist/two-factor/index.js.map +1 -0
  616. package/dist/two-factor/otp.d.ts +43 -0
  617. package/dist/two-factor/otp.d.ts.map +1 -0
  618. package/dist/two-factor/otp.js +57 -0
  619. package/dist/two-factor/otp.js.map +1 -0
  620. package/dist/two-factor/recovery.d.ts +45 -0
  621. package/dist/two-factor/recovery.d.ts.map +1 -0
  622. package/dist/two-factor/recovery.js +104 -0
  623. package/dist/two-factor/recovery.js.map +1 -0
  624. package/dist/two-factor/repository.d.ts +156 -0
  625. package/dist/two-factor/repository.d.ts.map +1 -0
  626. package/dist/two-factor/repository.js +251 -0
  627. package/dist/two-factor/repository.js.map +1 -0
  628. package/dist/two-factor/service.d.ts +232 -0
  629. package/dist/two-factor/service.d.ts.map +1 -0
  630. package/dist/two-factor/service.js +599 -0
  631. package/dist/two-factor/service.js.map +1 -0
  632. package/dist/two-factor/totp.d.ts +57 -0
  633. package/dist/two-factor/totp.d.ts.map +1 -0
  634. package/dist/two-factor/totp.js +96 -0
  635. package/dist/two-factor/totp.js.map +1 -0
  636. package/dist/two-factor/types.d.ts +152 -0
  637. package/dist/two-factor/types.d.ts.map +1 -0
  638. package/dist/two-factor/types.js +64 -0
  639. package/dist/two-factor/types.js.map +1 -0
  640. package/dist/users/cache.d.ts +49 -0
  641. package/dist/users/cache.d.ts.map +1 -0
  642. package/dist/users/cache.js +140 -0
  643. package/dist/users/cache.js.map +1 -0
  644. package/dist/users/claims.d.ts +67 -0
  645. package/dist/users/claims.d.ts.map +1 -0
  646. package/dist/users/claims.js +104 -0
  647. package/dist/users/claims.js.map +1 -0
  648. package/dist/users/errors.d.ts +25 -0
  649. package/dist/users/errors.d.ts.map +1 -0
  650. package/dist/users/errors.js +31 -0
  651. package/dist/users/errors.js.map +1 -0
  652. package/dist/users/gdpr.d.ts +110 -0
  653. package/dist/users/gdpr.d.ts.map +1 -0
  654. package/dist/users/gdpr.js +211 -0
  655. package/dist/users/gdpr.js.map +1 -0
  656. package/dist/users/index.d.ts +20 -0
  657. package/dist/users/index.d.ts.map +1 -0
  658. package/dist/users/index.js +19 -0
  659. package/dist/users/index.js.map +1 -0
  660. package/dist/users/password.d.ts +85 -0
  661. package/dist/users/password.d.ts.map +1 -0
  662. package/dist/users/password.js +138 -0
  663. package/dist/users/password.js.map +1 -0
  664. package/dist/users/repository.d.ts +263 -0
  665. package/dist/users/repository.d.ts.map +1 -0
  666. package/dist/users/repository.js +499 -0
  667. package/dist/users/repository.js.map +1 -0
  668. package/dist/users/service.d.ts +299 -0
  669. package/dist/users/service.d.ts.map +1 -0
  670. package/dist/users/service.js +698 -0
  671. package/dist/users/service.js.map +1 -0
  672. package/dist/users/types.d.ts +220 -0
  673. package/dist/users/types.d.ts.map +1 -0
  674. package/dist/users/types.js +77 -0
  675. package/dist/users/types.js.map +1 -0
  676. package/locales/default/en/common.json +10 -0
  677. package/locales/default/en/consent.json +13 -0
  678. package/locales/default/en/emails.json +17 -0
  679. package/locales/default/en/errors.json +21 -0
  680. package/locales/default/en/forgot-password.json +11 -0
  681. package/locales/default/en/invitation.json +18 -0
  682. package/locales/default/en/login.json +17 -0
  683. package/locales/default/en/logout.json +9 -0
  684. package/locales/default/en/magic-link.json +12 -0
  685. package/locales/default/en/reset-password.json +16 -0
  686. package/locales/default/en/two-factor.json +29 -0
  687. package/migrations/.gitkeep +0 -0
  688. package/migrations/001_extensions.sql +22 -0
  689. package/migrations/002_organizations.sql +43 -0
  690. package/migrations/003_applications.sql +50 -0
  691. package/migrations/004_clients.sql +74 -0
  692. package/migrations/005_users.sql +119 -0
  693. package/migrations/006_roles_permissions.sql +80 -0
  694. package/migrations/007_custom_claims.sql +55 -0
  695. package/migrations/008_config.sql +51 -0
  696. package/migrations/009_audit_log.sql +31 -0
  697. package/migrations/010_oidc_adapter.sql +34 -0
  698. package/migrations/011_seed.sql +53 -0
  699. package/migrations/012_two_factor.sql +88 -0
  700. package/migrations/013_client_secret_sha256.sql +22 -0
  701. package/migrations/014_login_methods.sql +34 -0
  702. package/migrations/015_signing_key_encryption.sql +20 -0
  703. package/migrations/016_failed_login_count.sql +27 -0
  704. package/migrations/017_audit_retention.sql +11 -0
  705. package/migrations/018_admin_api_enhancements.sql +78 -0
  706. package/migrations/019_invitation_details.sql +27 -0
  707. package/migrations/020_two_factor_admin_permission.sql +108 -0
  708. package/migrations/021_auth_recovery_jobs.sql +74 -0
  709. package/migrations/022_recovery_artifact_ownership.sql +25 -0
  710. package/migrations/023_magic_link_authority.sql +39 -0
  711. package/package.json +106 -0
  712. package/templates/default/emails/invitation.hbs +39 -0
  713. package/templates/default/emails/invitation.txt.hbs +18 -0
  714. package/templates/default/emails/magic-link.hbs +30 -0
  715. package/templates/default/emails/magic-link.txt.hbs +11 -0
  716. package/templates/default/emails/otp-code.hbs +31 -0
  717. package/templates/default/emails/otp-code.txt.hbs +15 -0
  718. package/templates/default/emails/password-changed.hbs +24 -0
  719. package/templates/default/emails/password-changed.txt.hbs +7 -0
  720. package/templates/default/emails/password-reset.hbs +30 -0
  721. package/templates/default/emails/password-reset.txt.hbs +11 -0
  722. package/templates/default/emails/welcome.hbs +25 -0
  723. package/templates/default/emails/welcome.txt.hbs +9 -0
  724. package/templates/default/layouts/main.hbs +49 -0
  725. package/templates/default/pages/accept-invite.hbs +25 -0
  726. package/templates/default/pages/consent.hbs +27 -0
  727. package/templates/default/pages/error.hbs +15 -0
  728. package/templates/default/pages/forgot-password.hbs +21 -0
  729. package/templates/default/pages/invite-expired.hbs +6 -0
  730. package/templates/default/pages/invite-success.hbs +9 -0
  731. package/templates/default/pages/login.hbs +108 -0
  732. package/templates/default/pages/logout-success.hbs +5 -0
  733. package/templates/default/pages/logout.hbs +24 -0
  734. package/templates/default/pages/magic-link-sent.hbs +10 -0
  735. package/templates/default/pages/magic-link-success.hbs +17 -0
  736. package/templates/default/pages/reset-password.hbs +20 -0
  737. package/templates/default/pages/reset-success.hbs +9 -0
  738. package/templates/default/pages/two-factor-setup.hbs +65 -0
  739. package/templates/default/pages/two-factor-verify.hbs +77 -0
  740. package/templates/default/partials/flash-messages.hbs +10 -0
  741. package/templates/default/partials/footer.hbs +4 -0
  742. package/templates/default/partials/header.hbs +8 -0
@@ -0,0 +1,50 @@
1
+ -- Up Migration
2
+
3
+ -- Applications represent SaaS products managed by Porta
4
+ CREATE TABLE applications (
5
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
6
+ name VARCHAR(255) NOT NULL, -- e.g., "BusinessSuite"
7
+ slug VARCHAR(100) NOT NULL UNIQUE, -- e.g., "business-suite"
8
+ description TEXT,
9
+ status VARCHAR(20) NOT NULL DEFAULT 'active'
10
+ CHECK (status IN ('active', 'inactive', 'archived')),
11
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
12
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
13
+ );
14
+
15
+ CREATE TRIGGER set_updated_at
16
+ BEFORE UPDATE ON applications
17
+ FOR EACH ROW
18
+ EXECUTE FUNCTION trigger_set_updated_at();
19
+
20
+ COMMENT ON TABLE applications IS 'SaaS application definitions — each org uses the same app but with their own users/roles';
21
+
22
+ -- Application modules are logical groupings (e.g., CRM, Invoicing, HR)
23
+ -- Permissions are namespaced by module
24
+ CREATE TABLE application_modules (
25
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
26
+ application_id UUID NOT NULL REFERENCES applications(id) ON DELETE CASCADE,
27
+ name VARCHAR(255) NOT NULL, -- e.g., "CRM"
28
+ slug VARCHAR(100) NOT NULL, -- e.g., "crm"
29
+ description TEXT,
30
+ status VARCHAR(20) NOT NULL DEFAULT 'active'
31
+ CHECK (status IN ('active', 'inactive')),
32
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
33
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
34
+
35
+ UNIQUE (application_id, slug)
36
+ );
37
+
38
+ CREATE INDEX idx_app_modules_application ON application_modules(application_id);
39
+
40
+ CREATE TRIGGER set_updated_at
41
+ BEFORE UPDATE ON application_modules
42
+ FOR EACH ROW
43
+ EXECUTE FUNCTION trigger_set_updated_at();
44
+
45
+ COMMENT ON TABLE application_modules IS 'Logical groupings within an application (e.g., CRM, Invoicing) — permissions are namespaced by module';
46
+
47
+ -- Down Migration
48
+
49
+ DROP TABLE IF EXISTS application_modules CASCADE;
50
+ DROP TABLE IF EXISTS applications CASCADE;
@@ -0,0 +1,74 @@
1
+ -- Up Migration
2
+
3
+ -- OIDC clients — each represents a deployment type (web, mobile, SPA) of an app for an org
4
+ CREATE TABLE clients (
5
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
6
+ organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
7
+ application_id UUID NOT NULL REFERENCES applications(id) ON DELETE CASCADE,
8
+ client_id VARCHAR(255) NOT NULL UNIQUE, -- OIDC client_id (public identifier)
9
+ client_name VARCHAR(255) NOT NULL,
10
+ client_type VARCHAR(20) NOT NULL
11
+ CHECK (client_type IN ('confidential', 'public')),
12
+ application_type VARCHAR(20) NOT NULL DEFAULT 'web'
13
+ CHECK (application_type IN ('web', 'native', 'spa')),
14
+
15
+ -- OIDC Configuration
16
+ redirect_uris TEXT[] NOT NULL DEFAULT '{}', -- Array of allowed redirect URIs
17
+ post_logout_redirect_uris TEXT[] DEFAULT '{}',
18
+ grant_types TEXT[] NOT NULL DEFAULT '{authorization_code}',
19
+ response_types TEXT[] NOT NULL DEFAULT '{code}',
20
+ scope TEXT NOT NULL DEFAULT 'openid profile email',
21
+ token_endpoint_auth_method VARCHAR(50) NOT NULL DEFAULT 'client_secret_basic',
22
+
23
+ -- CORS
24
+ allowed_origins TEXT[] DEFAULT '{}', -- Allowed CORS origins
25
+
26
+ -- PKCE
27
+ require_pkce BOOLEAN NOT NULL DEFAULT TRUE,
28
+
29
+ -- Status
30
+ status VARCHAR(20) NOT NULL DEFAULT 'active'
31
+ CHECK (status IN ('active', 'inactive', 'revoked')),
32
+
33
+ -- Metadata
34
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
35
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
36
+ );
37
+
38
+ CREATE INDEX idx_clients_org ON clients(organization_id);
39
+ CREATE INDEX idx_clients_app ON clients(application_id);
40
+ CREATE INDEX idx_clients_client_id ON clients(client_id);
41
+
42
+ CREATE TRIGGER set_updated_at
43
+ BEFORE UPDATE ON clients
44
+ FOR EACH ROW
45
+ EXECUTE FUNCTION trigger_set_updated_at();
46
+
47
+ COMMENT ON TABLE clients IS 'OIDC clients — each represents a deployment (web/mobile/SPA) of an application for an organization';
48
+ COMMENT ON COLUMN clients.client_id IS 'Public OIDC client_id — used in authorization requests';
49
+ COMMENT ON COLUMN clients.require_pkce IS 'Whether PKCE is required for this client (recommended: always true)';
50
+
51
+ -- Client secrets — stored as Argon2id hashes
52
+ -- Multiple active secrets support zero-downtime rotation
53
+ CREATE TABLE client_secrets (
54
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
55
+ client_id UUID NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
56
+ secret_hash TEXT NOT NULL, -- Argon2id hash of the secret
57
+ label VARCHAR(255), -- Human-readable label
58
+ expires_at TIMESTAMPTZ, -- NULL = never expires
59
+ status VARCHAR(20) NOT NULL DEFAULT 'active'
60
+ CHECK (status IN ('active', 'revoked')),
61
+ last_used_at TIMESTAMPTZ,
62
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
63
+ );
64
+
65
+ CREATE INDEX idx_client_secrets_client ON client_secrets(client_id);
66
+ CREATE INDEX idx_client_secrets_active ON client_secrets(client_id) WHERE status = 'active';
67
+
68
+ COMMENT ON TABLE client_secrets IS 'Hashed client secrets — multiple active secrets allow zero-downtime rotation';
69
+ COMMENT ON COLUMN client_secrets.secret_hash IS 'Argon2id hash of the client secret (plaintext is never stored)';
70
+
71
+ -- Down Migration
72
+
73
+ DROP TABLE IF EXISTS client_secrets CASCADE;
74
+ DROP TABLE IF EXISTS clients CASCADE;
@@ -0,0 +1,119 @@
1
+ -- Up Migration
2
+
3
+ -- Users belong to exactly one organization
4
+ -- Profile fields follow OIDC Standard Claims (OpenID Connect Core 1.0, §5.1)
5
+ CREATE TABLE users (
6
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
7
+ organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
8
+
9
+ -- Authentication
10
+ email CITEXT NOT NULL, -- Case-insensitive via citext extension
11
+ email_verified BOOLEAN NOT NULL DEFAULT FALSE,
12
+ password_hash TEXT, -- NULL for passwordless-only users
13
+ password_changed_at TIMESTAMPTZ,
14
+
15
+ -- OIDC Standard Claims (OpenID Connect Core 1.0, §5.1)
16
+ given_name VARCHAR(255),
17
+ family_name VARCHAR(255),
18
+ middle_name VARCHAR(255),
19
+ nickname VARCHAR(255),
20
+ preferred_username VARCHAR(255),
21
+ profile_url TEXT, -- "profile" claim
22
+ picture_url TEXT, -- "picture" claim
23
+ website_url TEXT, -- "website" claim
24
+ gender VARCHAR(50),
25
+ birthdate DATE, -- ISO 8601 (YYYY-MM-DD)
26
+ zoneinfo VARCHAR(50), -- e.g., "Europe/Amsterdam"
27
+ locale VARCHAR(10), -- e.g., "nl-NL"
28
+ phone_number VARCHAR(50),
29
+ phone_number_verified BOOLEAN NOT NULL DEFAULT FALSE,
30
+
31
+ -- Address (structured, OIDC §5.1.1)
32
+ address_street TEXT,
33
+ address_locality VARCHAR(255), -- City
34
+ address_region VARCHAR(255), -- State/Province
35
+ address_postal_code VARCHAR(20),
36
+ address_country VARCHAR(2), -- ISO 3166-1 alpha-2
37
+
38
+ -- Status & Lifecycle
39
+ status VARCHAR(20) NOT NULL DEFAULT 'active'
40
+ CHECK (status IN ('active', 'inactive', 'suspended', 'locked')),
41
+ locked_at TIMESTAMPTZ,
42
+ locked_reason TEXT,
43
+ last_login_at TIMESTAMPTZ,
44
+ login_count INTEGER NOT NULL DEFAULT 0,
45
+
46
+ -- Metadata
47
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
48
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
49
+
50
+ -- Constraints: email is unique per organization (case-insensitive via CITEXT)
51
+ UNIQUE (organization_id, email)
52
+ );
53
+
54
+ CREATE INDEX idx_users_org ON users(organization_id);
55
+ CREATE INDEX idx_users_email ON users(email);
56
+ CREATE INDEX idx_users_status ON users(organization_id, status);
57
+
58
+ CREATE TRIGGER set_updated_at
59
+ BEFORE UPDATE ON users
60
+ FOR EACH ROW
61
+ EXECUTE FUNCTION trigger_set_updated_at();
62
+
63
+ COMMENT ON TABLE users IS 'User accounts — each user belongs to exactly one organization';
64
+ COMMENT ON COLUMN users.email IS 'Case-insensitive email (CITEXT) — unique per organization';
65
+ COMMENT ON COLUMN users.password_hash IS 'Argon2id hash — NULL for passwordless-only users';
66
+ COMMENT ON COLUMN users.status IS 'User lifecycle: active → inactive/suspended/locked';
67
+
68
+ -- Magic link tokens for passwordless authentication
69
+ CREATE TABLE magic_link_tokens (
70
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
71
+ user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
72
+ token_hash TEXT NOT NULL UNIQUE, -- SHA-256 hash of the token
73
+ expires_at TIMESTAMPTZ NOT NULL,
74
+ used_at TIMESTAMPTZ, -- NULL = not yet used
75
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
76
+ );
77
+
78
+ CREATE INDEX idx_magic_link_user ON magic_link_tokens(user_id);
79
+ CREATE INDEX idx_magic_link_token ON magic_link_tokens(token_hash) WHERE used_at IS NULL;
80
+
81
+ COMMENT ON TABLE magic_link_tokens IS 'Passwordless login tokens — SHA-256 hashed, single-use with expiry';
82
+
83
+ -- Password reset tokens
84
+ CREATE TABLE password_reset_tokens (
85
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
86
+ user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
87
+ token_hash TEXT NOT NULL UNIQUE, -- SHA-256 hash of the token
88
+ expires_at TIMESTAMPTZ NOT NULL,
89
+ used_at TIMESTAMPTZ,
90
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
91
+ );
92
+
93
+ CREATE INDEX idx_pwd_reset_user ON password_reset_tokens(user_id);
94
+ CREATE INDEX idx_pwd_reset_token ON password_reset_tokens(token_hash) WHERE used_at IS NULL;
95
+
96
+ COMMENT ON TABLE password_reset_tokens IS 'Password reset tokens — SHA-256 hashed, single-use with expiry';
97
+
98
+ -- Invitation tokens — sent when admin creates a user account
99
+ -- User clicks link to set their initial password
100
+ CREATE TABLE invitation_tokens (
101
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
102
+ user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
103
+ token_hash TEXT NOT NULL UNIQUE, -- SHA-256 hash of the token
104
+ expires_at TIMESTAMPTZ NOT NULL, -- Default: 7 days from creation
105
+ used_at TIMESTAMPTZ, -- NULL = not yet accepted
106
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
107
+ );
108
+
109
+ CREATE INDEX idx_invitation_user ON invitation_tokens(user_id);
110
+ CREATE INDEX idx_invitation_token ON invitation_tokens(token_hash) WHERE used_at IS NULL;
111
+
112
+ COMMENT ON TABLE invitation_tokens IS 'User invitation tokens — sent by admin, user clicks to set initial password';
113
+
114
+ -- Down Migration
115
+
116
+ DROP TABLE IF EXISTS invitation_tokens CASCADE;
117
+ DROP TABLE IF EXISTS password_reset_tokens CASCADE;
118
+ DROP TABLE IF EXISTS magic_link_tokens CASCADE;
119
+ DROP TABLE IF EXISTS users CASCADE;
@@ -0,0 +1,80 @@
1
+ -- Up Migration
2
+
3
+ -- Roles are defined globally per application
4
+ -- Role assignments to users are per-organization (since users belong to one org)
5
+ CREATE TABLE roles (
6
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
7
+ application_id UUID NOT NULL REFERENCES applications(id) ON DELETE CASCADE,
8
+ name VARCHAR(255) NOT NULL, -- e.g., "CRM Editor"
9
+ slug VARCHAR(100) NOT NULL, -- e.g., "crm-editor"
10
+ description TEXT,
11
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
12
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
13
+
14
+ UNIQUE (application_id, slug)
15
+ );
16
+
17
+ CREATE INDEX idx_roles_application ON roles(application_id);
18
+
19
+ CREATE TRIGGER set_updated_at
20
+ BEFORE UPDATE ON roles
21
+ FOR EACH ROW
22
+ EXECUTE FUNCTION trigger_set_updated_at();
23
+
24
+ COMMENT ON TABLE roles IS 'Global role definitions per application — assigned to users via user_roles';
25
+
26
+ -- Permissions are defined globally per application, optionally scoped to a module
27
+ CREATE TABLE permissions (
28
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
29
+ application_id UUID NOT NULL REFERENCES applications(id) ON DELETE CASCADE,
30
+ module_id UUID REFERENCES application_modules(id) ON DELETE SET NULL,
31
+ name VARCHAR(255) NOT NULL, -- e.g., "Read CRM Contacts"
32
+ slug VARCHAR(150) NOT NULL, -- e.g., "crm:contacts:read"
33
+ description TEXT,
34
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
35
+
36
+ UNIQUE (application_id, slug)
37
+ );
38
+
39
+ CREATE INDEX idx_permissions_application ON permissions(application_id);
40
+ CREATE INDEX idx_permissions_module ON permissions(module_id);
41
+
42
+ COMMENT ON TABLE permissions IS 'Global permission definitions per application — optionally scoped to a module';
43
+ COMMENT ON COLUMN permissions.slug IS 'Namespaced permission slug (e.g., "crm:contacts:read")';
44
+
45
+ -- Many-to-many: which permissions does each role grant?
46
+ CREATE TABLE role_permissions (
47
+ role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
48
+ permission_id UUID NOT NULL REFERENCES permissions(id) ON DELETE CASCADE,
49
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
50
+
51
+ PRIMARY KEY (role_id, permission_id)
52
+ );
53
+
54
+ CREATE INDEX idx_role_permissions_role ON role_permissions(role_id);
55
+ CREATE INDEX idx_role_permissions_permission ON role_permissions(permission_id);
56
+
57
+ COMMENT ON TABLE role_permissions IS 'Maps roles to permissions — many-to-many join table';
58
+
59
+ -- Many-to-many: which roles are assigned to each user?
60
+ CREATE TABLE user_roles (
61
+ user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
62
+ role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
63
+ assigned_by UUID REFERENCES users(id) ON DELETE SET NULL,
64
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
65
+
66
+ PRIMARY KEY (user_id, role_id)
67
+ );
68
+
69
+ CREATE INDEX idx_user_roles_user ON user_roles(user_id);
70
+ CREATE INDEX idx_user_roles_role ON user_roles(role_id);
71
+
72
+ COMMENT ON TABLE user_roles IS 'Maps users to roles — includes who assigned the role';
73
+ COMMENT ON COLUMN user_roles.assigned_by IS 'The admin user who assigned this role (NULL if system-assigned)';
74
+
75
+ -- Down Migration
76
+
77
+ DROP TABLE IF EXISTS user_roles CASCADE;
78
+ DROP TABLE IF EXISTS role_permissions CASCADE;
79
+ DROP TABLE IF EXISTS permissions CASCADE;
80
+ DROP TABLE IF EXISTS roles CASCADE;
@@ -0,0 +1,55 @@
1
+ -- Up Migration
2
+
3
+ -- Custom claim definitions — per application
4
+ -- Controls which tokens include the claim (id_token, access_token, userinfo)
5
+ CREATE TABLE custom_claim_definitions (
6
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
7
+ application_id UUID NOT NULL REFERENCES applications(id) ON DELETE CASCADE,
8
+ claim_name VARCHAR(255) NOT NULL, -- e.g., "department"
9
+ claim_type VARCHAR(20) NOT NULL DEFAULT 'string'
10
+ CHECK (claim_type IN ('string', 'number', 'boolean', 'json')),
11
+ description TEXT,
12
+ include_in_id_token BOOLEAN NOT NULL DEFAULT FALSE,
13
+ include_in_access_token BOOLEAN NOT NULL DEFAULT TRUE,
14
+ include_in_userinfo BOOLEAN NOT NULL DEFAULT TRUE,
15
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
16
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
17
+
18
+ UNIQUE (application_id, claim_name)
19
+ );
20
+
21
+ CREATE INDEX idx_custom_claims_app ON custom_claim_definitions(application_id);
22
+
23
+ CREATE TRIGGER set_updated_at
24
+ BEFORE UPDATE ON custom_claim_definitions
25
+ FOR EACH ROW
26
+ EXECUTE FUNCTION trigger_set_updated_at();
27
+
28
+ COMMENT ON TABLE custom_claim_definitions IS 'Per-application custom claim definitions — controls token inclusion';
29
+
30
+ -- Custom claim values — per user
31
+ -- Stores the actual value as JSONB (supports any type: string, number, boolean, object)
32
+ CREATE TABLE custom_claim_values (
33
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
34
+ user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
35
+ claim_id UUID NOT NULL REFERENCES custom_claim_definitions(id) ON DELETE CASCADE,
36
+ value JSONB NOT NULL, -- Stores any type as JSONB
37
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
38
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
39
+
40
+ UNIQUE (user_id, claim_id)
41
+ );
42
+
43
+ CREATE INDEX idx_custom_claim_values_user ON custom_claim_values(user_id);
44
+
45
+ CREATE TRIGGER set_updated_at
46
+ BEFORE UPDATE ON custom_claim_values
47
+ FOR EACH ROW
48
+ EXECUTE FUNCTION trigger_set_updated_at();
49
+
50
+ COMMENT ON TABLE custom_claim_values IS 'Per-user custom claim values — JSONB supports any claim type';
51
+
52
+ -- Down Migration
53
+
54
+ DROP TABLE IF EXISTS custom_claim_values CASCADE;
55
+ DROP TABLE IF EXISTS custom_claim_definitions CASCADE;
@@ -0,0 +1,51 @@
1
+ -- Up Migration
2
+
3
+ -- System configuration — key-value store with typed JSONB values
4
+ -- All configurable settings (token lifetimes, rate limits, etc.) are stored here
5
+ CREATE TABLE system_config (
6
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
7
+ key VARCHAR(255) NOT NULL UNIQUE, -- e.g., "access_token_ttl"
8
+ value JSONB NOT NULL, -- Typed value as JSON
9
+ value_type VARCHAR(20) NOT NULL DEFAULT 'string'
10
+ CHECK (value_type IN ('string', 'number', 'boolean', 'duration', 'json')),
11
+ description TEXT,
12
+ is_sensitive BOOLEAN NOT NULL DEFAULT FALSE, -- Hide from non-super-admin
13
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
14
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
15
+ );
16
+
17
+ CREATE TRIGGER set_updated_at
18
+ BEFORE UPDATE ON system_config
19
+ FOR EACH ROW
20
+ EXECUTE FUNCTION trigger_set_updated_at();
21
+
22
+ COMMENT ON TABLE system_config IS 'System-wide configuration — key-value store with typed JSONB values';
23
+ COMMENT ON COLUMN system_config.is_sensitive IS 'If true, value is hidden from non-super-admin users';
24
+
25
+ -- Signing keys for JWKS (JSON Web Key Sets)
26
+ -- Stores PEM-encoded key pairs with lifecycle management
27
+ CREATE TABLE signing_keys (
28
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
29
+ kid VARCHAR(255) NOT NULL UNIQUE, -- Key ID (for JWKS)
30
+ algorithm VARCHAR(10) NOT NULL DEFAULT 'ES256',
31
+ public_key TEXT NOT NULL, -- PEM-encoded public key
32
+ private_key TEXT NOT NULL, -- PEM-encoded private key (encrypted at rest)
33
+ status VARCHAR(20) NOT NULL DEFAULT 'active'
34
+ CHECK (status IN ('active', 'retired', 'revoked')),
35
+ activated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
36
+ retired_at TIMESTAMPTZ, -- When marked as retired
37
+ expires_at TIMESTAMPTZ, -- Grace period expiry
38
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
39
+ );
40
+
41
+ CREATE INDEX idx_signing_keys_status ON signing_keys(status);
42
+ CREATE INDEX idx_signing_keys_active ON signing_keys(status) WHERE status = 'active';
43
+
44
+ COMMENT ON TABLE signing_keys IS 'JWKS signing key pairs — supports key rotation with active/retired/revoked lifecycle';
45
+ COMMENT ON COLUMN signing_keys.kid IS 'Key ID published in JWKS endpoint for token verification';
46
+ COMMENT ON COLUMN signing_keys.private_key IS 'PEM-encoded private key — should be encrypted at rest in production';
47
+
48
+ -- Down Migration
49
+
50
+ DROP TABLE IF EXISTS signing_keys CASCADE;
51
+ DROP TABLE IF EXISTS system_config CASCADE;
@@ -0,0 +1,31 @@
1
+ -- Up Migration
2
+
3
+ -- Audit log for security events
4
+ -- Uses SET NULL on delete to preserve audit entries when users/orgs are removed
5
+ CREATE TABLE audit_log (
6
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
7
+ organization_id UUID REFERENCES organizations(id) ON DELETE SET NULL,
8
+ user_id UUID REFERENCES users(id) ON DELETE SET NULL,
9
+ actor_id UUID REFERENCES users(id) ON DELETE SET NULL, -- Who performed the action
10
+ event_type VARCHAR(100) NOT NULL, -- e.g., "user.login.success"
11
+ event_category VARCHAR(50) NOT NULL, -- e.g., "authentication", "admin", "security"
12
+ description TEXT,
13
+ metadata JSONB DEFAULT '{}', -- Additional context (IP, user-agent, etc.)
14
+ ip_address INET,
15
+ user_agent TEXT,
16
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
17
+ );
18
+
19
+ -- Composite indexes for common query patterns (org+time, user+time, event+time)
20
+ CREATE INDEX idx_audit_log_org ON audit_log(organization_id, created_at DESC);
21
+ CREATE INDEX idx_audit_log_user ON audit_log(user_id, created_at DESC);
22
+ CREATE INDEX idx_audit_log_event ON audit_log(event_type, created_at DESC);
23
+ CREATE INDEX idx_audit_log_category ON audit_log(event_category, created_at DESC);
24
+
25
+ COMMENT ON TABLE audit_log IS 'Security event audit log — append-only, preserved on user/org deletion';
26
+ COMMENT ON COLUMN audit_log.actor_id IS 'The user who performed the action (may differ from user_id for admin actions)';
27
+ COMMENT ON COLUMN audit_log.metadata IS 'Structured additional context (e.g., IP address, user-agent, request details)';
28
+
29
+ -- Down Migration
30
+
31
+ DROP TABLE IF EXISTS audit_log CASCADE;
@@ -0,0 +1,34 @@
1
+ -- Up Migration
2
+
3
+ -- Generic OIDC adapter storage table for node-oidc-provider
4
+ -- Stores: AccessToken, AuthorizationCode, RefreshToken, DeviceCode,
5
+ -- ClientCredentials, InitialAccessToken, RegistrationAccessToken,
6
+ -- Interaction, ReplayDetection, PushedAuthorizationRequest, Grant,
7
+ -- Session, BackchannelAuthenticationRequest
8
+ CREATE TABLE oidc_payloads (
9
+ id VARCHAR(255) NOT NULL,
10
+ type VARCHAR(50) NOT NULL, -- Model name (e.g., "AccessToken")
11
+ payload JSONB NOT NULL,
12
+ grant_id VARCHAR(255),
13
+ user_code VARCHAR(255),
14
+ uid VARCHAR(255),
15
+ expires_at TIMESTAMPTZ,
16
+ consumed_at TIMESTAMPTZ,
17
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
18
+
19
+ PRIMARY KEY (id, type)
20
+ );
21
+
22
+ -- Partial indexes for efficient lookups on non-null columns
23
+ CREATE INDEX idx_oidc_payloads_grant ON oidc_payloads(grant_id) WHERE grant_id IS NOT NULL;
24
+ CREATE INDEX idx_oidc_payloads_user_code ON oidc_payloads(user_code) WHERE user_code IS NOT NULL;
25
+ CREATE INDEX idx_oidc_payloads_uid ON oidc_payloads(uid) WHERE uid IS NOT NULL;
26
+ CREATE INDEX idx_oidc_payloads_expires ON oidc_payloads(expires_at) WHERE expires_at IS NOT NULL;
27
+
28
+ COMMENT ON TABLE oidc_payloads IS 'Generic storage for node-oidc-provider — all OIDC artifact types share this table';
29
+ COMMENT ON COLUMN oidc_payloads.type IS 'OIDC model type (e.g., AccessToken, AuthorizationCode, Session)';
30
+ COMMENT ON COLUMN oidc_payloads.payload IS 'Full OIDC artifact payload as JSONB';
31
+
32
+ -- Down Migration
33
+
34
+ DROP TABLE IF EXISTS oidc_payloads CASCADE;
@@ -0,0 +1,53 @@
1
+ -- Up Migration
2
+
3
+ -- Seed data for development: super-admin organization and default system config values.
4
+ -- NOTE: Super-admin user is NOT created here (deferred to RD-06, requires password hashing).
5
+ -- NOTE: Signing keys are NOT created here (deferred to RD-03, requires crypto key generation).
6
+
7
+ -- Super-admin organization
8
+ -- The is_super_admin partial unique index ensures only one row can have is_super_admin = TRUE
9
+ INSERT INTO organizations (name, slug, status, is_super_admin, branding_company_name, default_locale)
10
+ VALUES ('Porta Admin', 'porta-admin', 'active', TRUE, 'Porta', 'en');
11
+
12
+ -- Default system configuration values
13
+ -- These control global OIDC/auth behavior and can be changed at runtime via admin UI
14
+ INSERT INTO system_config (key, value, value_type, description, is_sensitive) VALUES
15
+ -- Token lifetimes
16
+ ('access_token_ttl', '"3600"', 'duration', 'Access token TTL in seconds (default: 1 hour)', FALSE),
17
+ ('id_token_ttl', '"3600"', 'duration', 'ID token TTL in seconds (default: 1 hour)', FALSE),
18
+ ('refresh_token_ttl', '"2592000"', 'duration', 'Refresh token TTL in seconds (default: 30 days)', FALSE),
19
+ ('authorization_code_ttl', '"600"', 'duration', 'Authorization code TTL in seconds (default: 10 minutes)', FALSE),
20
+
21
+ -- Session settings
22
+ ('session_ttl', '"86400"', 'duration', 'Session TTL in seconds (default: 24 hours)', FALSE),
23
+ ('cookie_secure', 'true', 'boolean', 'Require HTTPS for cookies (disable for local dev only)', FALSE),
24
+
25
+ -- Magic link / passwordless
26
+ ('magic_link_ttl', '"900"', 'duration', 'Magic link token TTL in seconds (default: 15 minutes)', FALSE),
27
+ ('magic_link_length', '48', 'number', 'Magic link token length in bytes', FALSE),
28
+
29
+ -- Password reset
30
+ ('password_reset_ttl', '"3600"', 'duration', 'Password reset token TTL in seconds (default: 1 hour)', FALSE),
31
+
32
+ -- Invitation
33
+ ('invitation_ttl', '"604800"', 'duration', 'Invitation token TTL in seconds (default: 7 days)', FALSE),
34
+
35
+ -- Rate limiting
36
+ ('login_rate_limit', '10', 'number', 'Max login attempts per user per 15 minutes', FALSE),
37
+ ('api_rate_limit', '100', 'number', 'Max API requests per client per minute', FALSE),
38
+
39
+ -- Account lockout
40
+ ('max_failed_logins', '5', 'number', 'Max failed logins before account lockout', FALSE),
41
+ ('lockout_duration', '"900"', 'duration', 'Account lockout duration in seconds (default: 15 minutes)', FALSE),
42
+
43
+ -- PKCE
44
+ ('require_pkce', 'true', 'boolean', 'Require PKCE for all authorization code flows', FALSE),
45
+
46
+ -- CORS
47
+ ('cors_max_age', '86400', 'number', 'CORS preflight cache duration in seconds (default: 24h)', FALSE);
48
+
49
+ -- Down Migration
50
+
51
+ -- Remove seed data in reverse order
52
+ DELETE FROM system_config;
53
+ DELETE FROM organizations WHERE slug = 'porta-admin';
@@ -0,0 +1,88 @@
1
+ -- Up Migration
2
+
3
+ -- Two-Factor Authentication (RD-12)
4
+ -- Adds per-org 2FA policy, per-user 2FA state, and tables for TOTP config,
5
+ -- email OTP codes, and recovery codes.
6
+
7
+ -- Add 2FA policy to organizations — controls whether 2FA is required for org members.
8
+ -- Values: 'optional' (default), 'required_email', 'required_totp', 'required_any'
9
+ ALTER TABLE organizations
10
+ ADD COLUMN two_factor_policy VARCHAR(20) NOT NULL DEFAULT 'optional'
11
+ CHECK (two_factor_policy IN ('optional', 'required_email', 'required_totp', 'required_any'));
12
+
13
+ -- Add 2FA state columns to users — tracks whether 2FA is enabled and which method.
14
+ ALTER TABLE users
15
+ ADD COLUMN two_factor_enabled BOOLEAN NOT NULL DEFAULT false,
16
+ ADD COLUMN two_factor_method VARCHAR(10) DEFAULT NULL
17
+ CHECK (two_factor_method IN ('email', 'totp') OR two_factor_method IS NULL);
18
+
19
+ -- TOTP configuration per user (encrypted secrets)
20
+ -- Stores AES-256-GCM encrypted TOTP secret, one row per user.
21
+ CREATE TABLE user_totp (
22
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
23
+ user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
24
+ encrypted_secret TEXT NOT NULL, -- AES-256-GCM encrypted TOTP secret
25
+ encryption_iv TEXT NOT NULL, -- Initialization vector (hex)
26
+ encryption_tag TEXT NOT NULL, -- Authentication tag (hex)
27
+ algorithm VARCHAR(10) NOT NULL DEFAULT 'SHA1',
28
+ digits INTEGER NOT NULL DEFAULT 6,
29
+ period INTEGER NOT NULL DEFAULT 30,
30
+ verified BOOLEAN NOT NULL DEFAULT false, -- true after first successful TOTP verification
31
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
32
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
33
+ UNIQUE (user_id) -- one TOTP config per user
34
+ );
35
+
36
+ -- Email OTP codes (SHA-256 hashed, short-lived)
37
+ -- Stores hashed 6-digit codes sent via email for 2FA verification.
38
+ CREATE TABLE two_factor_otp_codes (
39
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
40
+ user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
41
+ code_hash VARCHAR(64) NOT NULL, -- SHA-256 hex hash of 6-digit code
42
+ expires_at TIMESTAMPTZ NOT NULL, -- 10 minute expiry
43
+ used_at TIMESTAMPTZ DEFAULT NULL, -- NULL = unused, set on successful verification
44
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
45
+ );
46
+
47
+ -- Index for fast lookup of active (unused, not expired) OTP codes per user
48
+ CREATE INDEX idx_otp_codes_user_active
49
+ ON two_factor_otp_codes(user_id, expires_at)
50
+ WHERE used_at IS NULL;
51
+
52
+ -- Recovery codes (Argon2id hashed, single-use)
53
+ -- 10 codes generated during 2FA setup, each usable once as a backup.
54
+ CREATE TABLE two_factor_recovery_codes (
55
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
56
+ user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
57
+ code_hash TEXT NOT NULL, -- Argon2id hash of recovery code
58
+ used_at TIMESTAMPTZ DEFAULT NULL, -- NULL = unused
59
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
60
+ );
61
+
62
+ -- Index for fast lookup of unused recovery codes per user
63
+ CREATE INDEX idx_recovery_codes_user
64
+ ON two_factor_recovery_codes(user_id)
65
+ WHERE used_at IS NULL;
66
+
67
+ -- Auto-update trigger for user_totp.updated_at
68
+ -- Uses the reusable trigger function defined in 001_extensions.sql
69
+ CREATE TRIGGER set_user_totp_updated_at
70
+ BEFORE UPDATE ON user_totp
71
+ FOR EACH ROW EXECUTE FUNCTION trigger_set_updated_at();
72
+
73
+ -- Down Migration
74
+
75
+ -- Drop tables and indexes (indexes are dropped automatically with tables)
76
+ DROP TRIGGER IF EXISTS set_user_totp_updated_at ON user_totp;
77
+ DROP TABLE IF EXISTS two_factor_recovery_codes;
78
+ DROP TABLE IF EXISTS two_factor_otp_codes;
79
+ DROP TABLE IF EXISTS user_totp;
80
+
81
+ -- Remove 2FA columns from users
82
+ ALTER TABLE users
83
+ DROP COLUMN IF EXISTS two_factor_method,
84
+ DROP COLUMN IF EXISTS two_factor_enabled;
85
+
86
+ -- Remove 2FA policy from organizations
87
+ ALTER TABLE organizations
88
+ DROP COLUMN IF EXISTS two_factor_policy;
@@ -0,0 +1,22 @@
1
+ -- Migration 013: Add SHA-256 hash column to client_secrets
2
+ --
3
+ -- Adds a SHA-256 hex hash column used for oidc-provider client authentication.
4
+ -- SHA-256 is appropriate for machine-generated, high-entropy client secrets
5
+ -- (48 bytes / 384 bits). Argon2id hashes are retained for admin API verification.
6
+ --
7
+ -- Existing secrets cannot be backfilled (we don't have plaintexts).
8
+ -- New secrets will have both Argon2id hash and SHA-256 hash.
9
+
10
+ -- Up Migration
11
+ ALTER TABLE client_secrets ADD COLUMN secret_sha256 VARCHAR(64);
12
+
13
+ CREATE INDEX idx_client_secrets_sha256
14
+ ON client_secrets(secret_sha256)
15
+ WHERE secret_sha256 IS NOT NULL;
16
+
17
+ COMMENT ON COLUMN client_secrets.secret_sha256
18
+ IS 'SHA-256 hex hash of the secret — used for oidc-provider client authentication';
19
+
20
+ -- Down Migration (in comments for reference)
21
+ -- DROP INDEX IF EXISTS idx_client_secrets_sha256;
22
+ -- ALTER TABLE client_secrets DROP COLUMN IF EXISTS secret_sha256;