@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,233 @@
1
+ /**
2
+ * SvelteKit form-action helpers for the common auth flows.
3
+ *
4
+ * Use these in `+page.server.ts` to wire `<form method="POST">` to
5
+ * fortress's auth methods. They handle:
6
+ *
7
+ * - parsing form data
8
+ * - calling `fortress.auth.*`
9
+ * - setting cookies via `event.cookies.set` (so SvelteKit's `Set-Cookie`
10
+ * handling sees them, not just the browser)
11
+ * - returning `fail(...)` on `FortressError`
12
+ * - throwing a `redirect(...)` on success when `redirectTo` is supplied
13
+ *
14
+ * Redirects and failures use SvelteKit's real `redirect()` / `fail()`
15
+ * primitives, so the action runtime recognizes them without casts or
16
+ * raw-Response 500s.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * // src/routes/login/+page.server.ts
21
+ * import { fortressActions } from '@bajustone/fortress/sveltekit';
22
+ * import { fortress } from '$lib/server/fortress';
23
+ *
24
+ * export const actions = {
25
+ * default: fortressActions.login(fortress, { redirectTo: '/dashboard' }),
26
+ * };
27
+ * ```
28
+ */
29
+
30
+ import type { ActionFailure } from '@sveltejs/kit';
31
+ import type { Fortress } from '../core/fortress';
32
+ import type { AuthChallenge, CreateUserInput, LoginIdentifierType } from '../core/types';
33
+ import type { SvelteKitAction, SvelteKitRequestEvent } from './types';
34
+ import { FortressError } from '../core/errors';
35
+ import { clearAuthCookies, setAuthCookies } from './cookies';
36
+
37
+ /** Shape returned by form-action helpers when something goes wrong. */
38
+ export type FortressActionFailure = ActionFailure<{ error: string; code?: string }>;
39
+
40
+ /** Discriminated result returned when no `redirectTo` is configured. */
41
+ export type FortressActionSuccess
42
+ = | { success: true; pending: false }
43
+ | { success: true; pending: true; challenge: AuthChallenge };
44
+
45
+ /** Common options for the auth-issuing helpers. */
46
+ export interface FortressActionOptions {
47
+ /**
48
+ * On success, throw a redirect to this path. SvelteKit's action runtime
49
+ * picks up the thrown 3xx and emits a normal HTTP redirect.
50
+ */
51
+ redirectTo?: string;
52
+ }
53
+
54
+ async function actionFailure(
55
+ status: number,
56
+ data: { error: string; code?: string },
57
+ ): Promise<FortressActionFailure> {
58
+ const { fail } = await import('@sveltejs/kit');
59
+ return fail(status, data);
60
+ }
61
+
62
+ async function actionRedirect(location: string): Promise<never> {
63
+ const { redirect } = await import('@sveltejs/kit');
64
+ return redirect(303, location);
65
+ }
66
+
67
+ /** Read a single string field from a form, defaulting to '' if missing. */
68
+ function field(form: FormData, name: string): string {
69
+ const v = form.get(name);
70
+ return typeof v === 'string' ? v : '';
71
+ }
72
+
73
+ function buildMeta(event: SvelteKitRequestEvent): { ipAddress?: string; userAgent?: string } {
74
+ return {
75
+ ipAddress:
76
+ event.request.headers.get('x-forwarded-for')
77
+ ?? event.request.headers.get('x-real-ip')
78
+ ?? undefined,
79
+ userAgent: event.request.headers.get('user-agent') ?? undefined,
80
+ };
81
+ }
82
+
83
+ /** Type of the {@link fortressActions} namespace — explicit so JSR's slow-type check is happy. */
84
+ export interface FortressActions {
85
+ login: (
86
+ fortress: Fortress<any, any>,
87
+ opts?: FortressActionOptions,
88
+ ) => SvelteKitAction<FortressActionFailure | FortressActionSuccess>;
89
+ register: (
90
+ fortress: Fortress<any, any>,
91
+ opts?: FortressActionOptions,
92
+ ) => SvelteKitAction<FortressActionFailure | FortressActionSuccess>;
93
+ logout: (
94
+ fortress: Fortress<any, any>,
95
+ opts?: FortressActionOptions,
96
+ ) => SvelteKitAction<FortressActionSuccess>;
97
+ refresh: (
98
+ fortress: Fortress<any, any>,
99
+ ) => SvelteKitAction<FortressActionFailure | FortressActionSuccess>;
100
+ }
101
+
102
+ /** Form-action helpers exported as a single namespace. */
103
+ export const fortressActions: FortressActions = {
104
+ /**
105
+ * Sign in with `identifier` + `password` form fields. Sets the access +
106
+ * refresh cookies on success. Optionally redirects.
107
+ */
108
+ login(
109
+ fortress: Fortress<any, any>,
110
+ opts: FortressActionOptions = {},
111
+ ): SvelteKitAction<FortressActionFailure | FortressActionSuccess> {
112
+ return async (event) => {
113
+ const form = await event.request.formData();
114
+ const identifier = field(form, 'identifier');
115
+ const password = field(form, 'password');
116
+ try {
117
+ const result = await fortress.auth.login(identifier, password, buildMeta(event));
118
+ if (result.status === 'pending')
119
+ return { success: true, pending: true, challenge: result.pending } as const;
120
+ if (result.status !== 'success')
121
+ return { success: true, pending: false } as const;
122
+ setAuthCookies(event, fortress, {
123
+ accessToken: result.accessToken,
124
+ refreshToken: result.refreshToken,
125
+ });
126
+ if (opts.redirectTo)
127
+ await actionRedirect(opts.redirectTo);
128
+ return { success: true, pending: false } as const;
129
+ }
130
+ catch (err) {
131
+ if (err instanceof FortressError) {
132
+ return actionFailure(err.statusCode, { error: err.message, code: err.code });
133
+ }
134
+ throw err;
135
+ }
136
+ };
137
+ },
138
+
139
+ /**
140
+ * Register a new user from `email`, `name`, `password` form fields. On
141
+ * success, immediately logs them in (sets cookies) and optionally
142
+ * redirects. Mirrors the typical sign-up UX.
143
+ */
144
+ register(
145
+ fortress: Fortress<any, any>,
146
+ opts: FortressActionOptions = {},
147
+ ): SvelteKitAction<FortressActionFailure | FortressActionSuccess> {
148
+ return async (event) => {
149
+ const form = await event.request.formData();
150
+ const data: CreateUserInput = {
151
+ email: field(form, 'email'),
152
+ name: field(form, 'name'),
153
+ password: field(form, 'password'),
154
+ };
155
+ try {
156
+ await fortress.auth.createUser(data);
157
+ const result = await fortress.auth.login(data.email, data.password ?? '', buildMeta(event));
158
+ if (result.status === 'pending')
159
+ return { success: true, pending: true, challenge: result.pending } as const;
160
+ if (result.status !== 'success')
161
+ return { success: true, pending: false } as const;
162
+ setAuthCookies(event, fortress, {
163
+ accessToken: result.accessToken,
164
+ refreshToken: result.refreshToken,
165
+ });
166
+ if (opts.redirectTo)
167
+ await actionRedirect(opts.redirectTo);
168
+ return { success: true, pending: false } as const;
169
+ }
170
+ catch (err) {
171
+ if (err instanceof FortressError) {
172
+ return actionFailure(err.statusCode, { error: err.message, code: err.code });
173
+ }
174
+ throw err;
175
+ }
176
+ };
177
+ },
178
+
179
+ /**
180
+ * Revoke the current refresh token (if present) and clear both auth
181
+ * cookies. Optionally redirects.
182
+ */
183
+ logout(
184
+ fortress: Fortress<any, any>,
185
+ opts: FortressActionOptions = {},
186
+ ): SvelteKitAction<FortressActionSuccess> {
187
+ return async (event) => {
188
+ const refreshToken = event.cookies.get(fortress.cookies.refreshName);
189
+ if (refreshToken) {
190
+ try {
191
+ await fortress.auth.logout(refreshToken);
192
+ }
193
+ catch {
194
+ // Ignore — clearing cookies is the important part.
195
+ }
196
+ }
197
+ clearAuthCookies(event, fortress);
198
+ if (opts.redirectTo)
199
+ await actionRedirect(opts.redirectTo);
200
+ return { success: true, pending: false } as const;
201
+ };
202
+ },
203
+
204
+ /**
205
+ * Manually rotate the access + refresh tokens using the cookie. Useful
206
+ * if you want a `?/refresh` form action for SPAs that need to extend a
207
+ * session without a full reload.
208
+ */
209
+ refresh(
210
+ fortress: Fortress<any, any>,
211
+ ): SvelteKitAction<FortressActionFailure | FortressActionSuccess> {
212
+ return async (event) => {
213
+ const refreshToken = event.cookies.get(fortress.cookies.refreshName);
214
+ if (!refreshToken) {
215
+ return actionFailure(401, { error: 'No refresh token', code: 'UNAUTHORIZED' });
216
+ }
217
+ try {
218
+ const result = await fortress.auth.refresh(refreshToken, buildMeta(event));
219
+ setAuthCookies(event, fortress, result);
220
+ return { success: true, pending: false } as const;
221
+ }
222
+ catch (err) {
223
+ if (err instanceof FortressError) {
224
+ return actionFailure(err.statusCode, { error: err.message, code: err.code });
225
+ }
226
+ throw err;
227
+ }
228
+ };
229
+ },
230
+ } as const;
231
+
232
+ // Re-export so consumers can pattern-match against the field type.
233
+ export type { LoginIdentifierType };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Optional catch-all `+server.ts` escape hatch.
3
+ *
4
+ * Lets users colocate Fortress routes inside their normal `src/routes/`
5
+ * tree instead of (or in addition to) the handle-hook approach.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * // src/routes/api/fortress/[...path]/+server.ts
10
+ * import { toSvelteKitHandler } from '@bajustone/fortress/sveltekit';
11
+ * import { fortress } from '$lib/server/fortress';
12
+ *
13
+ * export const { GET, POST, PUT, DELETE, PATCH } = toSvelteKitHandler(fortress);
14
+ * ```
15
+ */
16
+
17
+ import type { Fortress } from '../core/fortress';
18
+ import type { SvelteKitRequestEvent } from './types';
19
+
20
+ /** Per-method handler signature compatible with SvelteKit `+server.ts` exports. */
21
+ export type SvelteKitRouteHandler = (event: SvelteKitRequestEvent) => Promise<Response>;
22
+
23
+ /**
24
+ * Build a `{ GET, POST, PUT, DELETE, PATCH }` set of handlers that all
25
+ * delegate to `fortress.handleRequest`. Suitable for `export const { ... } =
26
+ * toSvelteKitHandler(fortress)` in a catch-all `+server.ts`.
27
+ */
28
+ export function toSvelteKitHandler(fortress: Fortress<any, any>): {
29
+ GET: SvelteKitRouteHandler;
30
+ POST: SvelteKitRouteHandler;
31
+ PUT: SvelteKitRouteHandler;
32
+ DELETE: SvelteKitRouteHandler;
33
+ PATCH: SvelteKitRouteHandler;
34
+ } {
35
+ const handler: SvelteKitRouteHandler = event => fortress.handleRequest(event.request);
36
+ return {
37
+ GET: handler,
38
+ POST: handler,
39
+ PUT: handler,
40
+ DELETE: handler,
41
+ PATCH: handler,
42
+ };
43
+ }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * SvelteKit cookie bridging.
3
+ *
4
+ * Translates between fortress's auth result objects and SvelteKit's
5
+ * `event.cookies` API. Cookie attributes (name, secure, sameSite, path,
6
+ * domain) come from the resolved {@link ResolvedCookieConfig} on the
7
+ * Fortress instance, so SvelteKit, Hono, and Express all emit cookies with
8
+ * the same shape.
9
+ */
10
+
11
+ import type { ResolvedCookieConfig } from '../core/config';
12
+ import type { Fortress } from '../core/fortress';
13
+ import type { AuthCookiePayload } from '../core/http/cookie-serialize';
14
+ import type { SvelteKitCookieOptions, SvelteKitRequestEvent } from './types';
15
+ import { parseCookieHeader } from '../core/http/cookie-serialize';
16
+
17
+ /**
18
+ * Set the access + refresh cookies on a SvelteKit `RequestEvent` from a
19
+ * fortress auth result. Used by the form-action helpers and the auto-refresh
20
+ * path inside `createSvelteKitHandle`.
21
+ */
22
+ export function setAuthCookies(
23
+ event: SvelteKitRequestEvent,
24
+ fortress: Fortress<any, any>,
25
+ payload: AuthCookiePayload,
26
+ ): void {
27
+ const cookies = fortress.cookies;
28
+ const accessExpiry = fortress.config.jwt.accessTokenExpirySeconds ?? 900;
29
+ const refreshExpiry = fortress.config.jwt.refreshTokenExpirySeconds ?? 604_800;
30
+
31
+ event.cookies.set(cookies.accessName, payload.accessToken, optsFor(cookies, accessExpiry));
32
+ if (payload.refreshToken) {
33
+ event.cookies.set(cookies.refreshName, payload.refreshToken, optsFor(cookies, refreshExpiry));
34
+ }
35
+ }
36
+
37
+ /** Clear both fortress auth cookies (logout). */
38
+ export function clearAuthCookies(event: SvelteKitRequestEvent, fortress: Fortress<any, any>): void {
39
+ const cookies = fortress.cookies;
40
+ event.cookies.delete(cookies.accessName, optsFor(cookies));
41
+ event.cookies.delete(cookies.refreshName, optsFor(cookies));
42
+ }
43
+
44
+ /**
45
+ * Replay any `Set-Cookie` headers from a `Response` (typically returned by
46
+ * `fortress.handleRequest`) through `event.cookies.set` so they're visible
47
+ * to subsequent same-request reads. Returns the original response unchanged
48
+ * — the cookies are already on it; this just mirrors them into SvelteKit's
49
+ * cookie jar for in-request use.
50
+ */
51
+ export function replayCookies(response: Response, event: SvelteKitRequestEvent): void {
52
+ const setCookies = response.headers.getSetCookie();
53
+ for (const raw of setCookies) {
54
+ const parsed = parseSetCookie(raw);
55
+ if (!parsed)
56
+ continue;
57
+ event.cookies.set(parsed.name, parsed.value, parsed.opts);
58
+ }
59
+ }
60
+
61
+ type RequiredPathCookieOptions = SvelteKitCookieOptions & { path: string };
62
+
63
+ function optsFor(cookies: ResolvedCookieConfig, maxAgeSeconds?: number): RequiredPathCookieOptions {
64
+ const opts: RequiredPathCookieOptions = {
65
+ path: cookies.path,
66
+ httpOnly: true,
67
+ secure: cookies.secure,
68
+ sameSite: cookies.sameSite,
69
+ };
70
+ if (cookies.domain)
71
+ opts.domain = cookies.domain;
72
+ if (maxAgeSeconds !== undefined)
73
+ opts.maxAge = maxAgeSeconds;
74
+ return opts;
75
+ }
76
+
77
+ interface ParsedSetCookie {
78
+ name: string;
79
+ value: string;
80
+ opts: RequiredPathCookieOptions;
81
+ }
82
+
83
+ /**
84
+ * Parse a single `Set-Cookie` header string into name/value/options.
85
+ *
86
+ * Handles the attributes fortress emits (Path, Domain, HttpOnly, Secure,
87
+ * SameSite, Max-Age, Expires). Unknown attributes are ignored.
88
+ */
89
+ function parseSetCookie(raw: string): ParsedSetCookie | null {
90
+ const parts = raw.split(';').map(p => p.trim());
91
+ if (parts.length === 0)
92
+ return null;
93
+ const first = parts.shift();
94
+ if (!first)
95
+ return null;
96
+ const eq = first.indexOf('=');
97
+ if (eq === -1)
98
+ return null;
99
+
100
+ const name = first.slice(0, eq).trim();
101
+ const rawValue = first.slice(eq + 1).trim();
102
+ let value: string;
103
+ try {
104
+ value = decodeURIComponent(rawValue);
105
+ }
106
+ catch {
107
+ value = rawValue;
108
+ }
109
+
110
+ // Reuse parseCookieHeader by formatting the attribute list as a single
111
+ // header string would not work — attributes have boolean variants like
112
+ // `Secure` and `HttpOnly`. Walk parts manually.
113
+ const opts: RequiredPathCookieOptions = { path: '/' };
114
+ for (const part of parts) {
115
+ const [k, v = ''] = part.split('=', 2);
116
+ const key = k.toLowerCase();
117
+ if (key === 'path')
118
+ opts.path = v;
119
+ else if (key === 'domain')
120
+ opts.domain = v;
121
+ else if (key === 'httponly')
122
+ opts.httpOnly = true;
123
+ else if (key === 'secure')
124
+ opts.secure = true;
125
+ else if (key === 'samesite')
126
+ opts.sameSite = v.toLowerCase() as 'lax' | 'strict' | 'none';
127
+ else if (key === 'max-age')
128
+ opts.maxAge = Number(v);
129
+ else if (key === 'expires')
130
+ opts.expires = new Date(v);
131
+ }
132
+ return { name, value, opts };
133
+ }
134
+
135
+ // Re-export so callers can use it from this module too
136
+ export { parseCookieHeader };