@bajustone/fortress 1.0.0-rc.1

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 (465) hide show
  1. package/CHANGELOG.md +1011 -0
  2. package/LICENSE +21 -0
  3. package/README.md +760 -0
  4. package/SECURITY.md +197 -0
  5. package/bin/fortress.ts +667 -0
  6. package/dist/auth-service-BcyQ2PuZ.d.cts +307 -0
  7. package/dist/auth-service-BkzZkWfH.d.ts +307 -0
  8. package/dist/config-B2366s_G.d.ts +665 -0
  9. package/dist/config-GtlVt6ZD.d.cts +665 -0
  10. package/dist/convert-routes-BLCQT57f.d.ts +72 -0
  11. package/dist/convert-routes-BdMvP2_X.d.cts +72 -0
  12. package/dist/crypto.cjs +61 -0
  13. package/dist/crypto.d.cts +36 -0
  14. package/dist/crypto.d.ts +36 -0
  15. package/dist/crypto.js +35 -0
  16. package/dist/drift-BT1wtMDJ.d.cts +22 -0
  17. package/dist/drift-k9LiYpRP.d.ts +22 -0
  18. package/dist/drizzle/pg.cjs +481 -0
  19. package/dist/drizzle/pg.d.cts +15 -0
  20. package/dist/drizzle/pg.d.ts +15 -0
  21. package/dist/drizzle/pg.js +454 -0
  22. package/dist/drizzle.cjs +1442 -0
  23. package/dist/drizzle.d.cts +85 -0
  24. package/dist/drizzle.d.ts +85 -0
  25. package/dist/drizzle.js +1411 -0
  26. package/dist/express.cjs +1357 -0
  27. package/dist/express.d.cts +333 -0
  28. package/dist/express.d.ts +333 -0
  29. package/dist/express.js +1314 -0
  30. package/dist/fetcher.cjs +77 -0
  31. package/dist/fetcher.d.cts +7 -0
  32. package/dist/fetcher.d.ts +7 -0
  33. package/dist/fetcher.js +41 -0
  34. package/dist/fortress-BmSvFfqK.d.cts +1089 -0
  35. package/dist/fortress-uA-_cheR.d.ts +1089 -0
  36. package/dist/hono.cjs +1530 -0
  37. package/dist/hono.d.cts +514 -0
  38. package/dist/hono.d.ts +514 -0
  39. package/dist/hono.js +1483 -0
  40. package/dist/index-CxEkfCA0.d.cts +77 -0
  41. package/dist/index-CxEkfCA0.d.ts +77 -0
  42. package/dist/index-D054pcb-.d.cts +146 -0
  43. package/dist/index-jAMbbUAY.d.ts +146 -0
  44. package/dist/index.cjs +9046 -0
  45. package/dist/index.d.cts +717 -0
  46. package/dist/index.d.ts +717 -0
  47. package/dist/index.js +8935 -0
  48. package/dist/jwt.cjs +255 -0
  49. package/dist/jwt.d.cts +90 -0
  50. package/dist/jwt.d.ts +90 -0
  51. package/dist/jwt.js +227 -0
  52. package/dist/otel.cjs +108 -0
  53. package/dist/otel.d.cts +62 -0
  54. package/dist/otel.d.ts +62 -0
  55. package/dist/otel.js +73 -0
  56. package/dist/plugins/account-lockout.cjs +322 -0
  57. package/dist/plugins/account-lockout.d.cts +42 -0
  58. package/dist/plugins/account-lockout.d.ts +42 -0
  59. package/dist/plugins/account-lockout.js +295 -0
  60. package/dist/plugins/admin.cjs +2378 -0
  61. package/dist/plugins/admin.d.cts +72 -0
  62. package/dist/plugins/admin.d.ts +72 -0
  63. package/dist/plugins/admin.js +2351 -0
  64. package/dist/plugins/api-key.cjs +792 -0
  65. package/dist/plugins/api-key.d.cts +121 -0
  66. package/dist/plugins/api-key.d.ts +121 -0
  67. package/dist/plugins/api-key.js +765 -0
  68. package/dist/plugins/audit-log.cjs +493 -0
  69. package/dist/plugins/audit-log.d.cts +86 -0
  70. package/dist/plugins/audit-log.d.ts +86 -0
  71. package/dist/plugins/audit-log.js +468 -0
  72. package/dist/plugins/data-isolation.cjs +211 -0
  73. package/dist/plugins/data-isolation.d.cts +49 -0
  74. package/dist/plugins/data-isolation.d.ts +49 -0
  75. package/dist/plugins/data-isolation.js +186 -0
  76. package/dist/plugins/email-verification.cjs +273 -0
  77. package/dist/plugins/email-verification.d.cts +44 -0
  78. package/dist/plugins/email-verification.d.ts +44 -0
  79. package/dist/plugins/email-verification.js +246 -0
  80. package/dist/plugins/magic-link.cjs +231 -0
  81. package/dist/plugins/magic-link.d.cts +39 -0
  82. package/dist/plugins/magic-link.d.ts +39 -0
  83. package/dist/plugins/magic-link.js +204 -0
  84. package/dist/plugins/oauth.cjs +1696 -0
  85. package/dist/plugins/oauth.d.cts +406 -0
  86. package/dist/plugins/oauth.d.ts +406 -0
  87. package/dist/plugins/oauth.js +1669 -0
  88. package/dist/plugins/openapi.cjs +1185 -0
  89. package/dist/plugins/openapi.d.cts +6 -0
  90. package/dist/plugins/openapi.d.ts +6 -0
  91. package/dist/plugins/openapi.js +1158 -0
  92. package/dist/plugins/rate-limit/express.cjs +55 -0
  93. package/dist/plugins/rate-limit/express.d.cts +64 -0
  94. package/dist/plugins/rate-limit/express.d.ts +64 -0
  95. package/dist/plugins/rate-limit/express.js +30 -0
  96. package/dist/plugins/rate-limit/hono.cjs +51 -0
  97. package/dist/plugins/rate-limit/hono.d.cts +59 -0
  98. package/dist/plugins/rate-limit/hono.d.ts +59 -0
  99. package/dist/plugins/rate-limit/hono.js +26 -0
  100. package/dist/plugins/rate-limit/sveltekit.cjs +49 -0
  101. package/dist/plugins/rate-limit/sveltekit.d.cts +59 -0
  102. package/dist/plugins/rate-limit/sveltekit.d.ts +59 -0
  103. package/dist/plugins/rate-limit/sveltekit.js +24 -0
  104. package/dist/plugins/rate-limit.cjs +354 -0
  105. package/dist/plugins/rate-limit.d.cts +140 -0
  106. package/dist/plugins/rate-limit.d.ts +140 -0
  107. package/dist/plugins/rate-limit.js +325 -0
  108. package/dist/plugins/social-login.cjs +905 -0
  109. package/dist/plugins/social-login.d.cts +114 -0
  110. package/dist/plugins/social-login.d.ts +114 -0
  111. package/dist/plugins/social-login.js +880 -0
  112. package/dist/plugins/tenancy.cjs +780 -0
  113. package/dist/plugins/tenancy.d.cts +108 -0
  114. package/dist/plugins/tenancy.d.ts +108 -0
  115. package/dist/plugins/tenancy.js +753 -0
  116. package/dist/plugins/two-factor.cjs +555 -0
  117. package/dist/plugins/two-factor.d.cts +67 -0
  118. package/dist/plugins/two-factor.d.ts +67 -0
  119. package/dist/plugins/two-factor.js +526 -0
  120. package/dist/plugins/webauthn.cjs +786 -0
  121. package/dist/plugins/webauthn.d.cts +72 -0
  122. package/dist/plugins/webauthn.d.ts +72 -0
  123. package/dist/plugins/webauthn.js +766 -0
  124. package/dist/plugins/webhook.cjs +819 -0
  125. package/dist/plugins/webhook.d.cts +254 -0
  126. package/dist/plugins/webhook.d.ts +254 -0
  127. package/dist/plugins/webhook.js +789 -0
  128. package/dist/protect-D1v_0upK.d.cts +123 -0
  129. package/dist/protect-DsxV_-dJ.d.ts +123 -0
  130. package/dist/sveltekit.cjs +1258 -0
  131. package/dist/sveltekit.d.cts +420 -0
  132. package/dist/sveltekit.d.ts +420 -0
  133. package/dist/sveltekit.js +1207 -0
  134. package/dist/testing.cjs +4294 -0
  135. package/dist/testing.d.cts +197 -0
  136. package/dist/testing.d.ts +197 -0
  137. package/dist/testing.js +4264 -0
  138. package/dist/types-et0R8tsC.d.cts +217 -0
  139. package/dist/types-et0R8tsC.d.ts +217 -0
  140. package/docs/adapter-typed-helpers.md +192 -0
  141. package/docs/admin-recipes.md +227 -0
  142. package/docs/architecture.md +434 -0
  143. package/docs/ci/github-actions.yml +59 -0
  144. package/docs/ci.md +109 -0
  145. package/docs/compatibility.md +115 -0
  146. package/docs/deployment.md +305 -0
  147. package/docs/hardening.md +118 -0
  148. package/docs/host-owned-routes.md +154 -0
  149. package/docs/migrations/0001-schema-version.md +54 -0
  150. package/docs/migrations/0002-initial-schema.md +84 -0
  151. package/docs/migrations/upgrade-guide.md +160 -0
  152. package/docs/observability.md +394 -0
  153. package/docs/plugins/account-lockout.md +131 -0
  154. package/docs/plugins/admin.md +402 -0
  155. package/docs/plugins/api-key.md +327 -0
  156. package/docs/plugins/audit-log.md +261 -0
  157. package/docs/plugins/data-isolation.md +186 -0
  158. package/docs/plugins/email-verification.md +121 -0
  159. package/docs/plugins/magic-link.md +123 -0
  160. package/docs/plugins/oauth.md +544 -0
  161. package/docs/plugins/openapi.md +250 -0
  162. package/docs/plugins/rate-limit.md +223 -0
  163. package/docs/plugins/social-login.md +337 -0
  164. package/docs/plugins/tenancy.md +122 -0
  165. package/docs/plugins/two-factor.md +191 -0
  166. package/docs/plugins/webauthn.md +188 -0
  167. package/docs/plugins/webhook.md +242 -0
  168. package/docs/policy-as-code.md +156 -0
  169. package/docs/route-manifest.md +46 -0
  170. package/docs/security.md +261 -0
  171. package/docs/threat-model.md +161 -0
  172. package/examples/README.md +119 -0
  173. package/examples/express-app/index.ts +747 -0
  174. package/examples/hono-app/docker-compose.yml +14 -0
  175. package/examples/hono-app/index.ts +1009 -0
  176. package/examples/policy/fortress.policy.json +47 -0
  177. package/examples/sveltekit-app/README.md +125 -0
  178. package/examples/sveltekit-app/src/app.d.ts +16 -0
  179. package/examples/sveltekit-app/src/hooks.server.ts +23 -0
  180. package/examples/sveltekit-app/src/lib/server/fortress.ts +81 -0
  181. package/examples/sveltekit-app/src/routes/api/echo/[id]/+server.ts +32 -0
  182. package/examples/sveltekit-app/src/routes/api/fortress/[...path]/+server.ts +15 -0
  183. package/examples/sveltekit-app/src/routes/dashboard/+page.server.ts +20 -0
  184. package/examples/sveltekit-app/src/routes/login/+page.server.ts +48 -0
  185. package/examples/sveltekit-app/src/routes/oauth/consent/+page.server.ts +69 -0
  186. package/examples/sveltekit-app/src/routes/oauth/consent/+page.svelte +83 -0
  187. package/migrations/pg/0001_schema_version.down.sql +2 -0
  188. package/migrations/pg/0001_schema_version.sql +8 -0
  189. package/migrations/pg/0002_initial_schema.down.sql +36 -0
  190. package/migrations/pg/0002_initial_schema.sql +376 -0
  191. package/migrations/pg/0003_auth_continuation.down.sql +6 -0
  192. package/migrations/pg/0003_auth_continuation.sql +30 -0
  193. package/migrations/pg/0004_tenant_default_unique.down.sql +1 -0
  194. package/migrations/pg/0004_tenant_default_unique.sql +14 -0
  195. package/migrations/pg/0005_hot_indexes_timestamptz.down.sql +71 -0
  196. package/migrations/pg/0005_hot_indexes_timestamptz.sql +71 -0
  197. package/migrations/pg/0006_canonical_email.down.sql +3 -0
  198. package/migrations/pg/0006_canonical_email.sql +8 -0
  199. package/migrations/pg/0007_audit_chain_anchor.down.sql +1 -0
  200. package/migrations/pg/0007_audit_chain_anchor.sql +8 -0
  201. package/migrations/pg/0008_two_factor_hardening.down.sql +8 -0
  202. package/migrations/pg/0008_two_factor_hardening.sql +8 -0
  203. package/migrations/pg/0009_encrypt_totp_secrets.down.sql +2 -0
  204. package/migrations/pg/0009_encrypt_totp_secrets.sql +5 -0
  205. package/migrations/pg/0010_bigint_append_only_ids.down.sql +2 -0
  206. package/migrations/pg/0010_bigint_append_only_ids.sql +23 -0
  207. package/migrations/sqlite/0001_schema_version.down.sql +2 -0
  208. package/migrations/sqlite/0001_schema_version.sql +8 -0
  209. package/migrations/sqlite/0002_initial_schema.down.sql +36 -0
  210. package/migrations/sqlite/0002_initial_schema.sql +376 -0
  211. package/migrations/sqlite/0003_auth_continuation.down.sql +27 -0
  212. package/migrations/sqlite/0003_auth_continuation.sql +45 -0
  213. package/migrations/sqlite/0004_tenant_default_unique.down.sql +1 -0
  214. package/migrations/sqlite/0004_tenant_default_unique.sql +13 -0
  215. package/migrations/sqlite/0005_hot_indexes_timestamptz.down.sql +10 -0
  216. package/migrations/sqlite/0005_hot_indexes_timestamptz.sql +10 -0
  217. package/migrations/sqlite/0006_canonical_email.down.sql +3 -0
  218. package/migrations/sqlite/0006_canonical_email.sql +8 -0
  219. package/migrations/sqlite/0007_audit_chain_anchor.down.sql +1 -0
  220. package/migrations/sqlite/0007_audit_chain_anchor.sql +8 -0
  221. package/migrations/sqlite/0008_two_factor_hardening.down.sql +14 -0
  222. package/migrations/sqlite/0008_two_factor_hardening.sql +7 -0
  223. package/migrations/sqlite/0009_encrypt_totp_secrets.down.sql +2 -0
  224. package/migrations/sqlite/0009_encrypt_totp_secrets.sql +5 -0
  225. package/migrations/sqlite/0010_bigint_append_only_ids.down.sql +1 -0
  226. package/migrations/sqlite/0010_bigint_append_only_ids.sql +2 -0
  227. package/package.json +398 -0
  228. package/src/adapters/database/index.ts +63 -0
  229. package/src/adapters/database/types.ts +22 -0
  230. package/src/changelog-script.test.ts +21 -0
  231. package/src/cli.test.ts +79 -0
  232. package/src/core/auth/auth-admin.test.ts +247 -0
  233. package/src/core/auth/auth-endpoints.ts +558 -0
  234. package/src/core/auth/auth-observer.test.ts +191 -0
  235. package/src/core/auth/auth-service.ts +1464 -0
  236. package/src/core/auth/email.test.ts +17 -0
  237. package/src/core/auth/email.ts +11 -0
  238. package/src/core/auth/hooks.test.ts +251 -0
  239. package/src/core/auth/impersonation.test.ts +179 -0
  240. package/src/core/auth/jwt.test.ts +184 -0
  241. package/src/core/auth/jwt.ts +239 -0
  242. package/src/core/auth/login-timing.test.ts +77 -0
  243. package/src/core/auth/password-policy.test.ts +253 -0
  244. package/src/core/auth/password-policy.ts +220 -0
  245. package/src/core/auth/password.test.ts +42 -0
  246. package/src/core/auth/password.ts +62 -0
  247. package/src/core/auth/post-auth-gate.test.ts +258 -0
  248. package/src/core/auth/post-auth-gate.ts +228 -0
  249. package/src/core/auth/refresh-token.test.ts +86 -0
  250. package/src/core/auth/refresh-token.ts +105 -0
  251. package/src/core/auth/timing-safe.ts +23 -0
  252. package/src/core/config.ts +212 -0
  253. package/src/core/endpoint.ts +149 -0
  254. package/src/core/errors.ts +199 -0
  255. package/src/core/fetcher-interop.test.ts +106 -0
  256. package/src/core/fortress.test.ts +354 -0
  257. package/src/core/fortress.ts +550 -0
  258. package/src/core/http/call.test.ts +148 -0
  259. package/src/core/http/call.ts +149 -0
  260. package/src/core/http/cookie-serialize.test.ts +198 -0
  261. package/src/core/http/cookie-serialize.ts +107 -0
  262. package/src/core/http/csrf.test.ts +140 -0
  263. package/src/core/http/csrf.ts +173 -0
  264. package/src/core/http/dispatch.ts +652 -0
  265. package/src/core/http/error-response.test.ts +69 -0
  266. package/src/core/http/error-response.ts +93 -0
  267. package/src/core/http/fortress-rbac.test.ts +43 -0
  268. package/src/core/http/fortress-rbac.ts +127 -0
  269. package/src/core/http/handle-request.test.ts +441 -0
  270. package/src/core/http/handle-request.ts +354 -0
  271. package/src/core/http/match.test.ts +122 -0
  272. package/src/core/http/match.ts +126 -0
  273. package/src/core/http/outbound.test.ts +34 -0
  274. package/src/core/http/outbound.ts +105 -0
  275. package/src/core/http/plugin-middleware.ts +67 -0
  276. package/src/core/http/principal.test.ts +345 -0
  277. package/src/core/http/principal.ts +86 -0
  278. package/src/core/http/protect.test.ts +220 -0
  279. package/src/core/http/protect.ts +394 -0
  280. package/src/core/http/token-extraction.test.ts +59 -0
  281. package/src/core/http/token-extraction.ts +53 -0
  282. package/src/core/iam/explain.test.ts +177 -0
  283. package/src/core/iam/explain.ts +302 -0
  284. package/src/core/iam/iam-endpoints.ts +779 -0
  285. package/src/core/iam/iam-service.test.ts +829 -0
  286. package/src/core/iam/iam-service.ts +1137 -0
  287. package/src/core/iam/permission-cache.test.ts +115 -0
  288. package/src/core/iam/permission-cache.ts +71 -0
  289. package/src/core/iam/permission-check-observer.test.ts +90 -0
  290. package/src/core/iam/permission-evaluator.test.ts +291 -0
  291. package/src/core/iam/permission-evaluator.ts +198 -0
  292. package/src/core/iam/permission-sync.test.ts +166 -0
  293. package/src/core/iam/permission-sync.ts +192 -0
  294. package/src/core/iam/resource-sync.test.ts +70 -0
  295. package/src/core/iam/resource-sync.ts +182 -0
  296. package/src/core/iam/service-account-http.test.ts +529 -0
  297. package/src/core/iam/service-account.test.ts +282 -0
  298. package/src/core/internal-adapter.test.ts +389 -0
  299. package/src/core/internal-adapter.ts +435 -0
  300. package/src/core/json-schema.ts +50 -0
  301. package/src/core/manifest/__snapshots__/route-manifest.test.ts.snap +192 -0
  302. package/src/core/manifest/drift.ts +103 -0
  303. package/src/core/manifest/route-manifest.test.ts +142 -0
  304. package/src/core/manifest/route-manifest.ts +154 -0
  305. package/src/core/migrate.test.ts +72 -0
  306. package/src/core/migrations/engine.test.ts +308 -0
  307. package/src/core/migrations/engine.ts +548 -0
  308. package/src/core/migrations/migrations.ts +1771 -0
  309. package/src/core/migrations/pg-migration.integration-test.ts +353 -0
  310. package/src/core/migrations/upgrade-fixture.test.ts +378 -0
  311. package/src/core/observability/db-instrumentation.ts +205 -0
  312. package/src/core/observability/listener-list.test.ts +124 -0
  313. package/src/core/observability/listener-list.ts +73 -0
  314. package/src/core/observability/logger.test.ts +43 -0
  315. package/src/core/observability/logger.ts +49 -0
  316. package/src/core/observability/spans.test.ts +193 -0
  317. package/src/core/observability/types.ts +80 -0
  318. package/src/core/openapi-drift.test.ts +41 -0
  319. package/src/core/openapi.ts +47 -0
  320. package/src/core/plugin-methods-map.ts +75 -0
  321. package/src/core/plugin-runner.test.ts +233 -0
  322. package/src/core/plugin-runner.ts +276 -0
  323. package/src/core/plugin.ts +210 -0
  324. package/src/core/policy/apply.ts +272 -0
  325. package/src/core/policy/diff.ts +288 -0
  326. package/src/core/policy/loader.test.ts +63 -0
  327. package/src/core/policy/loader.ts +214 -0
  328. package/src/core/policy/policy.test.ts +232 -0
  329. package/src/core/policy/types.ts +105 -0
  330. package/src/core/schema-builder.test.ts +660 -0
  331. package/src/core/schema-builder.ts +881 -0
  332. package/src/core/standard-schema.ts +56 -0
  333. package/src/core/types.ts +258 -0
  334. package/src/core/validation.test.ts +195 -0
  335. package/src/core/validation.ts +192 -0
  336. package/src/drizzle/adapter.test.ts +425 -0
  337. package/src/drizzle/adapter.ts +474 -0
  338. package/src/drizzle/index.ts +31 -0
  339. package/src/drizzle/pg/adapter.integration-test.ts +456 -0
  340. package/src/drizzle/pg/index.ts +13 -0
  341. package/src/drizzle/pg/pg.integration-test.ts +1539 -0
  342. package/src/drizzle/pg/schema.ts +539 -0
  343. package/src/drizzle/pg-error-map.test.ts +218 -0
  344. package/src/drizzle/pg-error-map.ts +151 -0
  345. package/src/drizzle/schema.ts +544 -0
  346. package/src/drizzle/sqlite-serialization.test.ts +106 -0
  347. package/src/express/express-api-key-user-routes.test.ts +241 -0
  348. package/src/express/express.test.ts +442 -0
  349. package/src/express/handle.ts +191 -0
  350. package/src/express/index.ts +62 -0
  351. package/src/express/middleware.ts +551 -0
  352. package/src/express/protect.ts +154 -0
  353. package/src/express/validated.test.ts +86 -0
  354. package/src/express/validated.ts +99 -0
  355. package/src/fetcher/index.ts +81 -0
  356. package/src/hono/convert-routes.test.ts +220 -0
  357. package/src/hono/convert-routes.ts +196 -0
  358. package/src/hono/converters.test.ts +50 -0
  359. package/src/hono/converters.ts +43 -0
  360. package/src/hono/handle.ts +79 -0
  361. package/src/hono/helpers.ts +2 -0
  362. package/src/hono/hono-api-key-user-routes.test.ts +225 -0
  363. package/src/hono/hono-handlers.test.ts +861 -0
  364. package/src/hono/hono.test.ts +454 -0
  365. package/src/hono/index.ts +101 -0
  366. package/src/hono/middleware/auth.ts +236 -0
  367. package/src/hono/middleware/auth.types.test.ts +94 -0
  368. package/src/hono/middleware/csrf.test.ts +127 -0
  369. package/src/hono/middleware/csrf.ts +68 -0
  370. package/src/hono/middleware/error-handler.ts +12 -0
  371. package/src/hono/middleware/plugin-middleware.ts +35 -0
  372. package/src/hono/middleware/rbac.ts +131 -0
  373. package/src/hono/middleware/security-headers.test.ts +88 -0
  374. package/src/hono/middleware/security-headers.ts +68 -0
  375. package/src/hono/openapi.ts +237 -0
  376. package/src/hono/protect.ts +87 -0
  377. package/src/hono/validated.test.ts +123 -0
  378. package/src/hono/validated.ts +62 -0
  379. package/src/index.ts +309 -0
  380. package/src/integration.test.ts +718 -0
  381. package/src/internal/changelog.ts +56 -0
  382. package/src/otel/index.ts +158 -0
  383. package/src/otel/otel-types.test.ts +35 -0
  384. package/src/otel/otel.test.ts +235 -0
  385. package/src/plugins/account-lockout/account-lockout.test.ts +367 -0
  386. package/src/plugins/account-lockout/index.ts +267 -0
  387. package/src/plugins/admin/admin-api-key.test.ts +438 -0
  388. package/src/plugins/admin/index.ts +1612 -0
  389. package/src/plugins/api-key/api-key.test.ts +684 -0
  390. package/src/plugins/api-key/core.ts +336 -0
  391. package/src/plugins/api-key/index.ts +315 -0
  392. package/src/plugins/audit-log/audit-log.test.ts +749 -0
  393. package/src/plugins/audit-log/index.ts +680 -0
  394. package/src/plugins/data-isolation/data-isolation.test.ts +197 -0
  395. package/src/plugins/data-isolation/index.ts +157 -0
  396. package/src/plugins/email-verification/email-verification.test.ts +199 -0
  397. package/src/plugins/email-verification/index.ts +190 -0
  398. package/src/plugins/magic-link/index.ts +129 -0
  399. package/src/plugins/magic-link/magic-link.test.ts +156 -0
  400. package/src/plugins/oauth/id-token.ts +86 -0
  401. package/src/plugins/oauth/index.ts +2145 -0
  402. package/src/plugins/oauth/jwks.test.ts +32 -0
  403. package/src/plugins/oauth/jwks.ts +182 -0
  404. package/src/plugins/oauth/oauth.test.ts +2220 -0
  405. package/src/plugins/oauth/pkce.ts +58 -0
  406. package/src/plugins/openapi/index.ts +298 -0
  407. package/src/plugins/openapi/openapi.test.ts +425 -0
  408. package/src/plugins/openapi/spec-builder.ts +287 -0
  409. package/src/plugins/rate-limit/express.test.ts +89 -0
  410. package/src/plugins/rate-limit/express.ts +86 -0
  411. package/src/plugins/rate-limit/hono.test.ts +60 -0
  412. package/src/plugins/rate-limit/hono.ts +74 -0
  413. package/src/plugins/rate-limit/index.ts +383 -0
  414. package/src/plugins/rate-limit/memory-store.ts +61 -0
  415. package/src/plugins/rate-limit/rate-limit.test.ts +756 -0
  416. package/src/plugins/rate-limit/sveltekit.test.ts +58 -0
  417. package/src/plugins/rate-limit/sveltekit.ts +66 -0
  418. package/src/plugins/social-login/index.ts +715 -0
  419. package/src/plugins/social-login/providers/apple.ts +34 -0
  420. package/src/plugins/social-login/providers/discord.ts +26 -0
  421. package/src/plugins/social-login/providers/github.ts +54 -0
  422. package/src/plugins/social-login/providers/google.ts +23 -0
  423. package/src/plugins/social-login/providers/index.ts +22 -0
  424. package/src/plugins/social-login/providers/microsoft.ts +35 -0
  425. package/src/plugins/social-login/providers/oidc.ts +37 -0
  426. package/src/plugins/social-login/providers/providers.test.ts +211 -0
  427. package/src/plugins/social-login/social-login.test.ts +675 -0
  428. package/src/plugins/social-login/types.ts +80 -0
  429. package/src/plugins/tenancy/index.ts +544 -0
  430. package/src/plugins/tenancy/tenancy.test.ts +323 -0
  431. package/src/plugins/two-factor/index.ts +569 -0
  432. package/src/plugins/two-factor/two-factor.test.ts +235 -0
  433. package/src/plugins/webauthn/index.ts +554 -0
  434. package/src/plugins/webauthn/webauthn.test.ts +472 -0
  435. package/src/plugins/webhook/builtin-events.ts +29 -0
  436. package/src/plugins/webhook/delivery.test.ts +51 -0
  437. package/src/plugins/webhook/delivery.ts +154 -0
  438. package/src/plugins/webhook/hooks.ts +89 -0
  439. package/src/plugins/webhook/index.ts +445 -0
  440. package/src/plugins/webhook/queue/database.ts +67 -0
  441. package/src/plugins/webhook/queue/in-memory.test.ts +48 -0
  442. package/src/plugins/webhook/queue/in-memory.ts +71 -0
  443. package/src/plugins/webhook/queue/types.ts +51 -0
  444. package/src/plugins/webhook/registry.test.ts +48 -0
  445. package/src/plugins/webhook/registry.ts +64 -0
  446. package/src/plugins/webhook/signing.ts +45 -0
  447. package/src/plugins/webhook/ssrf.ts +198 -0
  448. package/src/plugins/webhook/types.ts +138 -0
  449. package/src/plugins/webhook/webhook.test.ts +324 -0
  450. package/src/sveltekit/actions.ts +233 -0
  451. package/src/sveltekit/catch-all.ts +43 -0
  452. package/src/sveltekit/cookies.ts +136 -0
  453. package/src/sveltekit/handle.ts +356 -0
  454. package/src/sveltekit/helpers.ts +69 -0
  455. package/src/sveltekit/index.ts +74 -0
  456. package/src/sveltekit/protect.ts +80 -0
  457. package/src/sveltekit/sveltekit-types.test.ts +31 -0
  458. package/src/sveltekit/sveltekit.test.ts +799 -0
  459. package/src/sveltekit/types.ts +134 -0
  460. package/src/sveltekit/validated.test.ts +117 -0
  461. package/src/sveltekit/validated.ts +78 -0
  462. package/src/testing/adapter-conformance.test.ts +408 -0
  463. package/src/testing/checks.test.ts +124 -0
  464. package/src/testing/checks.ts +304 -0
  465. package/src/testing/index.ts +107 -0
@@ -0,0 +1,456 @@
1
+ import type { Sql } from 'postgres';
2
+ import type { StartedTestContainer } from 'testcontainers';
3
+ import type { DatabaseAdapter } from '../../adapters/database';
4
+ import { drizzle } from 'drizzle-orm/postgres-js';
5
+ import postgres from 'postgres';
6
+ import { GenericContainer, Wait } from 'testcontainers';
7
+
8
+ import { afterAll, beforeAll, beforeEach, describe } from 'vitest';
9
+ import { runAdapterTests } from '../../testing/adapter-conformance.test';
10
+ import { createDrizzleAdapter } from '../adapter';
11
+
12
+ const CREATE_TABLES_SQL = `
13
+ CREATE TABLE IF NOT EXISTS fortress_user (
14
+ id SERIAL PRIMARY KEY,
15
+ email VARCHAR(255) NOT NULL UNIQUE,
16
+ name VARCHAR(255) NOT NULL,
17
+ password_hash TEXT,
18
+ is_active BOOLEAN NOT NULL DEFAULT true,
19
+ email_verified BOOLEAN NOT NULL DEFAULT false,
20
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
21
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
22
+ );
23
+
24
+ CREATE TABLE IF NOT EXISTS fortress_login_identifier (
25
+ id SERIAL PRIMARY KEY,
26
+ user_id INTEGER NOT NULL REFERENCES fortress_user(id) ON DELETE CASCADE,
27
+ type VARCHAR(20) NOT NULL,
28
+ value VARCHAR(255) NOT NULL UNIQUE
29
+ );
30
+
31
+ CREATE TABLE IF NOT EXISTS fortress_refresh_token (
32
+ id SERIAL PRIMARY KEY,
33
+ user_id INTEGER NOT NULL REFERENCES fortress_user(id) ON DELETE CASCADE,
34
+ token_hash VARCHAR(64) NOT NULL UNIQUE,
35
+ token_family VARCHAR(64) NOT NULL,
36
+ family_created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
37
+ successor_token_hash VARCHAR(64),
38
+ rotated_at TIMESTAMPTZ,
39
+ is_revoked BOOLEAN NOT NULL DEFAULT false,
40
+ expires_at TIMESTAMPTZ NOT NULL,
41
+ ip_address VARCHAR(45),
42
+ user_agent TEXT,
43
+ device_name TEXT,
44
+ last_active_at TIMESTAMPTZ,
45
+ fingerprint_hash VARCHAR(64),
46
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
47
+ );
48
+
49
+ CREATE TABLE IF NOT EXISTS fortress_auth_continuation (
50
+ id SERIAL PRIMARY KEY,
51
+ user_id INTEGER NOT NULL REFERENCES fortress_user(id) ON DELETE CASCADE,
52
+ token_hash VARCHAR(64) NOT NULL UNIQUE,
53
+ reason VARCHAR(32) NOT NULL,
54
+ expires_at TIMESTAMPTZ NOT NULL,
55
+ consumed_at TIMESTAMPTZ,
56
+ failed_attempts INTEGER NOT NULL DEFAULT 0,
57
+ last_failed_at TIMESTAMPTZ,
58
+ invalidated_at TIMESTAMPTZ,
59
+ max_attempts INTEGER NOT NULL DEFAULT 5,
60
+ cooldown_seconds INTEGER NOT NULL DEFAULT 1,
61
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
62
+ );
63
+ CREATE INDEX IF NOT EXISTS auth_continuation_failure_idx
64
+ ON fortress_auth_continuation (user_id, reason, last_failed_at);
65
+
66
+ CREATE TABLE IF NOT EXISTS fortress_group (
67
+ id SERIAL PRIMARY KEY,
68
+ name VARCHAR(100) NOT NULL UNIQUE,
69
+ description TEXT
70
+ );
71
+
72
+ CREATE TABLE IF NOT EXISTS fortress_group_user (
73
+ group_id INTEGER NOT NULL REFERENCES fortress_group(id) ON DELETE CASCADE,
74
+ user_id INTEGER NOT NULL REFERENCES fortress_user(id) ON DELETE CASCADE,
75
+ PRIMARY KEY (group_id, user_id)
76
+ );
77
+
78
+ CREATE TABLE IF NOT EXISTS fortress_service_account (
79
+ id SERIAL PRIMARY KEY,
80
+ name VARCHAR(100) NOT NULL UNIQUE,
81
+ display_name VARCHAR(255),
82
+ description TEXT,
83
+ is_active BOOLEAN NOT NULL DEFAULT true,
84
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
85
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
86
+ );
87
+
88
+ CREATE TABLE IF NOT EXISTS fortress_resource (
89
+ name VARCHAR(100) PRIMARY KEY,
90
+ description TEXT
91
+ );
92
+
93
+ CREATE TABLE IF NOT EXISTS fortress_permission (
94
+ id SERIAL PRIMARY KEY,
95
+ resource VARCHAR(100) NOT NULL REFERENCES fortress_resource(name) ON DELETE CASCADE,
96
+ action VARCHAR(100) NOT NULL,
97
+ effect VARCHAR(10) NOT NULL DEFAULT 'ALLOW',
98
+ conditions JSONB,
99
+ description TEXT
100
+ );
101
+
102
+ CREATE TABLE IF NOT EXISTS fortress_role (
103
+ id SERIAL PRIMARY KEY,
104
+ name VARCHAR(100) NOT NULL UNIQUE,
105
+ description TEXT,
106
+ is_system BOOLEAN NOT NULL DEFAULT false
107
+ );
108
+
109
+ CREATE TABLE IF NOT EXISTS fortress_role_permission (
110
+ role_id INTEGER NOT NULL REFERENCES fortress_role(id) ON DELETE CASCADE,
111
+ permission_id INTEGER NOT NULL REFERENCES fortress_permission(id) ON DELETE CASCADE,
112
+ PRIMARY KEY (role_id, permission_id)
113
+ );
114
+
115
+ CREATE TABLE IF NOT EXISTS fortress_role_binding (
116
+ id SERIAL PRIMARY KEY,
117
+ role_id INTEGER NOT NULL REFERENCES fortress_role(id) ON DELETE CASCADE,
118
+ subject_type VARCHAR(20) NOT NULL,
119
+ subject_id INTEGER NOT NULL
120
+ );
121
+
122
+ CREATE TABLE IF NOT EXISTS fortress_email_verification_token (
123
+ id SERIAL PRIMARY KEY,
124
+ user_id INTEGER NOT NULL REFERENCES fortress_user(id) ON DELETE CASCADE,
125
+ token TEXT NOT NULL,
126
+ email VARCHAR(255) NOT NULL,
127
+ expires_at TIMESTAMPTZ NOT NULL,
128
+ used_at TIMESTAMPTZ,
129
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
130
+ );
131
+
132
+ CREATE TABLE IF NOT EXISTS fortress_magic_link_token (
133
+ id SERIAL PRIMARY KEY,
134
+ email VARCHAR(255) NOT NULL,
135
+ token VARCHAR(64) NOT NULL,
136
+ expires_at TIMESTAMPTZ NOT NULL,
137
+ used_at TIMESTAMPTZ,
138
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
139
+ );
140
+
141
+ CREATE TABLE IF NOT EXISTS fortress_api_key (
142
+ id SERIAL PRIMARY KEY,
143
+ subject_type VARCHAR(20) NOT NULL,
144
+ subject_id INTEGER NOT NULL,
145
+ name VARCHAR(255) NOT NULL,
146
+ key_hash VARCHAR(64) NOT NULL UNIQUE,
147
+ key_prefix VARCHAR(20) NOT NULL,
148
+ scopes TEXT,
149
+ expires_at TIMESTAMPTZ,
150
+ last_used_at TIMESTAMPTZ,
151
+ is_revoked BOOLEAN NOT NULL DEFAULT false,
152
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
153
+ );
154
+ CREATE INDEX IF NOT EXISTS api_key_subject_idx ON fortress_api_key (subject_type, subject_id);
155
+
156
+ CREATE TABLE IF NOT EXISTS fortress_two_factor_secret (
157
+ id SERIAL PRIMARY KEY,
158
+ user_id INTEGER NOT NULL UNIQUE REFERENCES fortress_user(id) ON DELETE CASCADE,
159
+ secret TEXT NOT NULL,
160
+ is_enabled BOOLEAN NOT NULL DEFAULT false,
161
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
162
+ );
163
+
164
+ CREATE TABLE IF NOT EXISTS fortress_backup_code (
165
+ id SERIAL PRIMARY KEY,
166
+ user_id INTEGER NOT NULL REFERENCES fortress_user(id) ON DELETE CASCADE,
167
+ code_hash TEXT NOT NULL,
168
+ is_used BOOLEAN NOT NULL DEFAULT false,
169
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
170
+ );
171
+
172
+ CREATE TABLE IF NOT EXISTS fortress_trusted_device (
173
+ id SERIAL PRIMARY KEY,
174
+ user_id INTEGER NOT NULL REFERENCES fortress_user(id) ON DELETE CASCADE,
175
+ device_hash VARCHAR(64) NOT NULL,
176
+ expires_at TIMESTAMPTZ NOT NULL,
177
+ last_used_at TIMESTAMPTZ NOT NULL,
178
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
179
+ );
180
+
181
+ CREATE TABLE IF NOT EXISTS fortress_social_account (
182
+ id SERIAL PRIMARY KEY,
183
+ user_id INTEGER NOT NULL REFERENCES fortress_user(id) ON DELETE CASCADE,
184
+ provider VARCHAR(50) NOT NULL,
185
+ provider_account_id VARCHAR(255) NOT NULL,
186
+ email VARCHAR(255),
187
+ access_token TEXT,
188
+ refresh_token TEXT,
189
+ token_expires_at TIMESTAMPTZ,
190
+ profile JSONB,
191
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
192
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
193
+ );
194
+
195
+ CREATE TABLE IF NOT EXISTS fortress_tenant (
196
+ id SERIAL PRIMARY KEY,
197
+ name VARCHAR(255) NOT NULL,
198
+ tax_id VARCHAR(100) NOT NULL UNIQUE,
199
+ description TEXT,
200
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
201
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
202
+ );
203
+
204
+ CREATE TABLE IF NOT EXISTS fortress_tenant_user (
205
+ tenant_id INTEGER NOT NULL REFERENCES fortress_tenant(id) ON DELETE CASCADE,
206
+ user_id INTEGER NOT NULL REFERENCES fortress_user(id) ON DELETE CASCADE,
207
+ is_default BOOLEAN NOT NULL DEFAULT false,
208
+ PRIMARY KEY (tenant_id, user_id)
209
+ );
210
+ CREATE UNIQUE INDEX IF NOT EXISTS fortress_tenant_user_one_default_idx
211
+ ON fortress_tenant_user (user_id) WHERE is_default = true;
212
+
213
+ CREATE TABLE IF NOT EXISTS fortress_oauth_client (
214
+ id SERIAL PRIMARY KEY,
215
+ client_id VARCHAR(255) NOT NULL UNIQUE,
216
+ client_secret_hash TEXT NOT NULL,
217
+ name VARCHAR(255) NOT NULL,
218
+ redirect_uris TEXT NOT NULL,
219
+ grant_types TEXT NOT NULL,
220
+ allowed_scopes TEXT,
221
+ token_endpoint_auth_method TEXT,
222
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
223
+ );
224
+
225
+ CREATE TABLE IF NOT EXISTS fortress_oauth_authorization_code (
226
+ id SERIAL PRIMARY KEY,
227
+ code VARCHAR(255) NOT NULL UNIQUE,
228
+ client_id VARCHAR(255) NOT NULL,
229
+ user_id INTEGER NOT NULL,
230
+ redirect_uri TEXT NOT NULL,
231
+ scope TEXT,
232
+ code_challenge TEXT,
233
+ code_challenge_method VARCHAR(10),
234
+ nonce TEXT,
235
+ auth_time INTEGER,
236
+ expires_at TIMESTAMPTZ NOT NULL,
237
+ used_at TIMESTAMPTZ,
238
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
239
+ );
240
+
241
+ CREATE TABLE IF NOT EXISTS fortress_oauth_access_token (
242
+ id SERIAL PRIMARY KEY,
243
+ token VARCHAR(255) NOT NULL UNIQUE,
244
+ client_id VARCHAR(255) NOT NULL,
245
+ user_id INTEGER,
246
+ scope TEXT,
247
+ expires_at TIMESTAMPTZ NOT NULL,
248
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
249
+ );
250
+
251
+ CREATE TABLE IF NOT EXISTS fortress_oauth_refresh_token (
252
+ id SERIAL PRIMARY KEY,
253
+ token VARCHAR(255) NOT NULL UNIQUE,
254
+ family_id VARCHAR(64) NOT NULL,
255
+ client_id VARCHAR(255) NOT NULL,
256
+ user_id INTEGER NOT NULL,
257
+ scope TEXT,
258
+ issued_at TIMESTAMPTZ NOT NULL,
259
+ expires_at TIMESTAMPTZ NOT NULL,
260
+ used_at TIMESTAMPTZ,
261
+ parent_id INTEGER,
262
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
263
+ );
264
+
265
+ CREATE TABLE IF NOT EXISTS fortress_oauth_pending_flow (
266
+ id SERIAL PRIMARY KEY,
267
+ client_id VARCHAR(255) NOT NULL,
268
+ redirect_uri TEXT NOT NULL,
269
+ scope TEXT,
270
+ state VARCHAR(255) NOT NULL,
271
+ code_challenge TEXT,
272
+ code_challenge_method VARCHAR(10),
273
+ nonce TEXT,
274
+ expires_at TIMESTAMPTZ NOT NULL,
275
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
276
+ );
277
+
278
+ CREATE TABLE IF NOT EXISTS fortress_oauth_signing_key (
279
+ id SERIAL PRIMARY KEY,
280
+ kid VARCHAR(64) NOT NULL UNIQUE,
281
+ alg VARCHAR(16) NOT NULL,
282
+ public_jwk TEXT NOT NULL,
283
+ private_jwk TEXT NOT NULL,
284
+ rotated_at TIMESTAMPTZ,
285
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
286
+ );
287
+
288
+ CREATE TABLE IF NOT EXISTS fortress_user_scope_assignment (
289
+ id SERIAL PRIMARY KEY,
290
+ user_id INTEGER NOT NULL REFERENCES fortress_user(id) ON DELETE CASCADE,
291
+ scope_name VARCHAR(100) NOT NULL,
292
+ scope_value VARCHAR(255) NOT NULL,
293
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
294
+ );
295
+
296
+ CREATE TABLE IF NOT EXISTS fortress_account_lockout (
297
+ id SERIAL PRIMARY KEY,
298
+ identifier VARCHAR(255) NOT NULL UNIQUE,
299
+ failed_attempts INTEGER NOT NULL DEFAULT 0,
300
+ last_failed_at TIMESTAMPTZ,
301
+ locked_until TIMESTAMPTZ,
302
+ lockout_count INTEGER NOT NULL DEFAULT 0,
303
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
304
+ );
305
+
306
+ CREATE TABLE IF NOT EXISTS fortress_audit_log (
307
+ id SERIAL PRIMARY KEY,
308
+ timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
309
+ event_type VARCHAR(100) NOT NULL,
310
+ actor_id INTEGER,
311
+ actor_type VARCHAR(20) NOT NULL DEFAULT 'USER',
312
+ target_id INTEGER,
313
+ target_type VARCHAR(50),
314
+ ip_address VARCHAR(45),
315
+ user_agent TEXT,
316
+ outcome VARCHAR(20) NOT NULL DEFAULT 'SUCCESS',
317
+ metadata JSONB,
318
+ previous_hash TEXT,
319
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
320
+ );
321
+
322
+ CREATE TABLE IF NOT EXISTS fortress_audit_chain_state (
323
+ id INTEGER PRIMARY KEY CHECK (id = 1),
324
+ last_hash VARCHAR(64),
325
+ entry_count INTEGER NOT NULL CHECK (entry_count >= 0),
326
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
327
+ CHECK ((entry_count = 0 AND last_hash IS NULL) OR (entry_count > 0 AND last_hash IS NOT NULL))
328
+ );
329
+ INSERT INTO fortress_audit_chain_state (id, last_hash, entry_count)
330
+ VALUES (1, NULL, 0) ON CONFLICT (id) DO NOTHING;
331
+
332
+ CREATE TABLE IF NOT EXISTS fortress_webhook_endpoint (
333
+ id SERIAL PRIMARY KEY,
334
+ url TEXT NOT NULL,
335
+ events TEXT NOT NULL,
336
+ secret TEXT NOT NULL,
337
+ is_active BOOLEAN NOT NULL DEFAULT true,
338
+ deactivated_reason TEXT,
339
+ consecutive_failures INTEGER NOT NULL DEFAULT 0,
340
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
341
+ );
342
+
343
+ CREATE TABLE IF NOT EXISTS fortress_webhook_delivery (
344
+ id SERIAL PRIMARY KEY,
345
+ endpoint_id INTEGER NOT NULL REFERENCES fortress_webhook_endpoint(id) ON DELETE CASCADE,
346
+ event_type VARCHAR(100) NOT NULL,
347
+ payload TEXT NOT NULL,
348
+ status VARCHAR(20) NOT NULL DEFAULT 'pending',
349
+ attempts INTEGER NOT NULL DEFAULT 0,
350
+ idempotency_key TEXT,
351
+ last_attempt_at TIMESTAMPTZ,
352
+ next_retry_at TIMESTAMPTZ,
353
+ response_status INTEGER,
354
+ response_body TEXT,
355
+ error_kind TEXT,
356
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
357
+ );
358
+
359
+ CREATE UNIQUE INDEX IF NOT EXISTS uniq_webhook_delivery_idempotency
360
+ ON fortress_webhook_delivery (endpoint_id, idempotency_key)
361
+ WHERE idempotency_key IS NOT NULL;
362
+
363
+ CREATE INDEX IF NOT EXISTS refresh_token_family_idx ON fortress_refresh_token (token_family);
364
+ CREATE INDEX IF NOT EXISTS refresh_token_user_idx ON fortress_refresh_token (user_id);
365
+ CREATE INDEX IF NOT EXISTS email_verification_token_token_idx ON fortress_email_verification_token (token);
366
+ CREATE INDEX IF NOT EXISTS magic_link_token_token_idx ON fortress_magic_link_token (token);
367
+ CREATE INDEX IF NOT EXISTS role_binding_subject_idx ON fortress_role_binding (subject_type, subject_id);
368
+ CREATE INDEX IF NOT EXISTS backup_code_user_idx ON fortress_backup_code (user_id);
369
+ CREATE INDEX IF NOT EXISTS trusted_device_user_idx ON fortress_trusted_device (user_id);
370
+ CREATE INDEX IF NOT EXISTS webhook_delivery_retry_idx ON fortress_webhook_delivery (status, next_retry_at);
371
+ CREATE INDEX IF NOT EXISTS audit_log_timestamp_idx ON fortress_audit_log (timestamp);
372
+ CREATE UNIQUE INDEX IF NOT EXISTS user_email_ci_unique ON fortress_user (lower(email));
373
+ CREATE UNIQUE INDEX IF NOT EXISTS login_identifier_email_ci_unique
374
+ ON fortress_login_identifier (lower(value)) WHERE type = 'email';
375
+ `;
376
+
377
+ const TRUNCATE_SQL = `
378
+ TRUNCATE
379
+ fortress_webhook_delivery,
380
+ fortress_webhook_endpoint,
381
+ fortress_audit_chain_state,
382
+ fortress_audit_log,
383
+ fortress_account_lockout,
384
+ fortress_user_scope_assignment,
385
+ fortress_oauth_signing_key,
386
+ fortress_oauth_pending_flow,
387
+ fortress_oauth_refresh_token,
388
+ fortress_oauth_access_token,
389
+ fortress_oauth_authorization_code,
390
+ fortress_oauth_client,
391
+ fortress_tenant_user,
392
+ fortress_tenant,
393
+ fortress_social_account,
394
+ fortress_trusted_device,
395
+ fortress_backup_code,
396
+ fortress_two_factor_secret,
397
+ fortress_api_key,
398
+ fortress_magic_link_token,
399
+ fortress_email_verification_token,
400
+ fortress_role_binding,
401
+ fortress_role_permission,
402
+ fortress_permission,
403
+ fortress_resource,
404
+ fortress_role,
405
+ fortress_service_account,
406
+ fortress_group_user,
407
+ fortress_group,
408
+ fortress_auth_continuation,
409
+ fortress_refresh_token,
410
+ fortress_login_identifier,
411
+ fortress_user
412
+ CASCADE;
413
+ INSERT INTO fortress_audit_chain_state (id, last_hash, entry_count)
414
+ VALUES (1, NULL, 0);
415
+ `;
416
+
417
+ let container: StartedTestContainer;
418
+ let pgClient: Sql;
419
+ let db: ReturnType<typeof drizzle>;
420
+
421
+ beforeAll(async () => {
422
+ container = await new GenericContainer('postgres:16-alpine')
423
+ .withEnvironment({
424
+ POSTGRES_USER: 'test',
425
+ POSTGRES_PASSWORD: 'test',
426
+ POSTGRES_DB: 'fortress_test',
427
+ })
428
+ .withExposedPorts(5432)
429
+ .withWaitStrategy(Wait.forListeningPorts())
430
+ .start();
431
+
432
+ const connectionString = `postgres://test:test@${container.getHost()}:${container.getMappedPort(5432)}/fortress_test`;
433
+ pgClient = postgres(connectionString);
434
+ db = drizzle(pgClient);
435
+
436
+ await pgClient.unsafe(CREATE_TABLES_SQL);
437
+ }, 60_000);
438
+
439
+ afterAll(async () => {
440
+ if (pgClient)
441
+ await pgClient.end();
442
+ if (container)
443
+ await container.stop();
444
+ });
445
+
446
+ beforeEach(async () => {
447
+ await pgClient.unsafe(TRUNCATE_SQL);
448
+ });
449
+
450
+ function createPgTestAdapter(): DatabaseAdapter {
451
+ return createDrizzleAdapter(db as any, { dialect: 'pg' });
452
+ }
453
+
454
+ describe('adapter conformance: PostgreSQL (drizzle)', () => {
455
+ runAdapterTests(createPgTestAdapter);
456
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * PostgreSQL-flavoured fortress Drizzle schema.
3
+ *
4
+ * Exposes {@link fortressPgSchema}, the aggregate of every fortress table
5
+ * defined with `pgTable`. Pass it (or your own override) to drizzle-kit for
6
+ * migration generation. The fortress drizzle adapter consumes tables
7
+ * generically, so you typically don't need to import this directly unless
8
+ * you're generating migrations.
9
+ *
10
+ * @module
11
+ */
12
+
13
+ export { fortressPgSchema } from './schema';