@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,58 @@
1
+ /**
2
+ * PKCE (Proof Key for Code Exchange) S256 implementation.
3
+ * Used by the OAuth plugin to secure authorization code flows.
4
+ */
5
+
6
+ import { timingSafeEqualHex } from '../../core/auth/timing-safe';
7
+
8
+ /**
9
+ * RFC 7636 §4.1: code_verifier = high-entropy cryptographic random
10
+ * STRING using the unreserved characters [A-Z]/[a-z]/[0-9]/"-"/"."/"_"/"~"
11
+ * with a minimum length of 43 characters and a maximum length of 128.
12
+ */
13
+ const CODE_VERIFIER_PATTERN = /^[\w.~-]{43,128}$/;
14
+
15
+ /** Generate a cryptographically random code verifier (43-128 chars, URL-safe) */
16
+ export function generateCodeVerifier(): string {
17
+ const bytes = new Uint8Array(32);
18
+ crypto.getRandomValues(bytes);
19
+ return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
20
+ }
21
+
22
+ /** Generate S256 code challenge from a code verifier */
23
+ export async function generateCodeChallenge(verifier: string): Promise<string> {
24
+ const data = new TextEncoder().encode(verifier);
25
+ const hash = await crypto.subtle.digest('SHA-256', data);
26
+ return btoa(String.fromCharCode(...new Uint8Array(hash)))
27
+ .replace(/\+/g, '-')
28
+ .replace(/\//g, '_')
29
+ .replace(/=/g, '');
30
+ }
31
+
32
+ /**
33
+ * Verify a code verifier against a stored S256 challenge.
34
+ *
35
+ * Validates the verifier against RFC 7636 §4.1 (43-128 chars, unreserved
36
+ * URL-safe character set) before computing the challenge — a verifier that
37
+ * fails this check is invalid by definition and must not unlock a code.
38
+ *
39
+ * Uses {@link timingSafeEqualHex} for the final comparison so a remote
40
+ * attacker can't differentiate "wrong verifier" from "wrong verifier with
41
+ * matching prefix" via response timing.
42
+ */
43
+ export async function verifyCodeChallenge(
44
+ verifier: string,
45
+ challenge: string,
46
+ method: string,
47
+ ): Promise<boolean> {
48
+ if (method !== 'S256')
49
+ return false;
50
+
51
+ if (!CODE_VERIFIER_PATTERN.test(verifier))
52
+ return false;
53
+
54
+ const computed = await generateCodeChallenge(verifier);
55
+ // base64url is a fixed alphabet; `timingSafeEqualHex` works for any
56
+ // single-byte-per-char ASCII string of equal length.
57
+ return timingSafeEqualHex(computed, challenge);
58
+ }
@@ -0,0 +1,298 @@
1
+ /**
2
+ * OpenAPI 3.1 generator plugin for fortress.
3
+ *
4
+ * Walks every registered endpoint definition (auth, IAM, and any plugin
5
+ * routes), folds in their JSON Schema component definitions, and emits a
6
+ * complete OpenAPI 3.1 specification. Pairs with Scalar UI for an
7
+ * interactive docs page mounted by the framework adapters.
8
+ *
9
+ * @module
10
+ */
11
+
12
+ import type { ComponentSchemas, EndpointDefinition } from '../../core/endpoint';
13
+ import type { ResourceFile } from '../../core/iam/resource-sync';
14
+ import type { JSONSchema } from '../../core/json-schema';
15
+ import type { FortressPlugin, PluginContext } from '../../core/plugin';
16
+ import type { OpenAPISpec, SpecBuilderOptions } from './spec-builder';
17
+ import { authComponentSchemas, authEndpoints } from '../../core/auth/auth-endpoints';
18
+ import { iamComponentSchemas, iamEndpoints } from '../../core/iam/iam-endpoints';
19
+ import { buildOpenAPISpec } from './spec-builder';
20
+
21
+ export interface OpenAPIConfig {
22
+ /** API title (default: 'Fortress Auth API') */
23
+ title?: string;
24
+ /** API version (default: '1.0.0') */
25
+ version?: string;
26
+ /** API description */
27
+ description?: string;
28
+ /** Server URL(s) for the spec */
29
+ servers?: Array<{ url: string; description?: string }>;
30
+ /** Optional top-level OpenAPI tags. */
31
+ tags?: Array<{ name: string; description?: string }>;
32
+ /** Operation ID strategy. Default: historical method+path IDs. */
33
+ operationId?: SpecBuilderOptions['operationId'];
34
+ /** Path to serve the JSON spec (default: '/openapi.json') */
35
+ specPath?: string;
36
+ /** Path to serve the Scalar UI (default: '/openapi') */
37
+ uiPath?: string;
38
+ /** Disable Scalar UI (default: false) */
39
+ disableUI?: boolean;
40
+ /** Include core auth endpoints in spec (default: true) */
41
+ includeCoreAuth?: boolean;
42
+ /** Include core IAM endpoints in spec (default: true) */
43
+ includeCoreIam?: boolean;
44
+ /** Additional component schemas to include */
45
+ additionalSchemas?: ComponentSchemas;
46
+ /** Additional endpoint definitions to include in the spec (for app-specific routes) */
47
+ additionalEndpoints?: EndpointDefinition[];
48
+ }
49
+
50
+ export interface OpenAPIMethods {
51
+ [key: string]: (...args: any[]) => any;
52
+ generateSpec: () => Promise<OpenAPISpec>;
53
+ getSpec: () => Promise<Record<string, unknown>>;
54
+ getUI: () => string;
55
+ }
56
+
57
+ function computeRelativeUrl(fromPath: string, toPath: string): string {
58
+ const fromParts = fromPath.split('/').slice(0, -1);
59
+ const toParts = toPath.split('/');
60
+ let common = 0;
61
+ while (common < fromParts.length && common < toParts.length && fromParts[common] === toParts[common]) {
62
+ common++;
63
+ }
64
+ const ups = fromParts.length - common;
65
+ const rest = toParts.slice(common).join('/');
66
+ if (ups === 0)
67
+ return `./${rest}`;
68
+ return '../'.repeat(ups) + rest;
69
+ }
70
+
71
+ function buildScalarHTML(specPath: string, title: string): string {
72
+ return `<!DOCTYPE html>
73
+ <html>
74
+ <head>
75
+ <title>${title} — API Reference</title>
76
+ <meta charset="utf-8" />
77
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
78
+ </head>
79
+ <body>
80
+ <script id="api-reference" data-url="${specPath}"></script>
81
+ <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
82
+ </body>
83
+ </html>`;
84
+ }
85
+
86
+ /**
87
+ * Build a single `oneOf` branch for a resource: `resource` is `const`,
88
+ * `action` is `enum` over the resource's valid actions, plus shared fields.
89
+ */
90
+ function buildResourceBranch(
91
+ resourceName: string,
92
+ actions: string[],
93
+ sharedProps: Record<string, JSONSchema>,
94
+ requiredFields: string[],
95
+ ): JSONSchema {
96
+ const actionSchema: JSONSchema = actions.length > 0
97
+ ? { type: 'string', enum: actions, description: 'Action name' }
98
+ : { type: 'string', description: 'Action name' };
99
+
100
+ return {
101
+ type: 'object',
102
+ properties: {
103
+ resource: { type: 'string', const: resourceName, description: 'Resource name' },
104
+ action: actionSchema,
105
+ ...sharedProps,
106
+ },
107
+ required: requiredFields,
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Enrich `Permission` and `PermissionInput` component schemas with
113
+ * per-resource `oneOf` discriminated branches, and patch the `/iam/check`
114
+ * endpoint's inline body with flat resource/action enums.
115
+ *
116
+ * Mutates `componentSchemas` and the matching endpoint in `allEndpoints`.
117
+ */
118
+ function enrichIamSchemas(
119
+ componentSchemas: ComponentSchemas,
120
+ allEndpoints: EndpointDefinition[],
121
+ resourceFile: ResourceFile,
122
+ ): void {
123
+ const entries = Object.entries(resourceFile.resources);
124
+ if (entries.length === 0)
125
+ return;
126
+
127
+ // Shared fields for Permission (response schema — has id, description, conditions)
128
+ const permissionShared: Record<string, JSONSchema> = {
129
+ id: { type: 'integer', description: 'Permission ID' },
130
+ effect: { type: 'string', enum: ['ALLOW', 'DENY'] },
131
+ conditions: componentSchemas.Permission?.properties?.conditions ?? { type: 'array' },
132
+ description: { type: 'string', description: 'Permission description', nullable: true },
133
+ };
134
+
135
+ // Shared fields for PermissionInput (request schema — no id)
136
+ const permissionInputShared: Record<string, JSONSchema> = {
137
+ effect: { type: 'string', enum: ['ALLOW', 'DENY'] },
138
+ conditions: componentSchemas.PermissionInput?.properties?.conditions ?? { type: 'array' },
139
+ };
140
+
141
+ // Build oneOf branches
142
+ const permissionBranches: JSONSchema[] = [];
143
+ const permissionInputBranches: JSONSchema[] = [];
144
+
145
+ for (const [name, def] of entries) {
146
+ permissionBranches.push(
147
+ buildResourceBranch(name, def.actions, permissionShared, ['id', 'resource', 'action', 'effect']),
148
+ );
149
+ permissionInputBranches.push(
150
+ buildResourceBranch(name, def.actions, permissionInputShared, ['resource', 'action']),
151
+ );
152
+ }
153
+
154
+ // Replace component schemas with oneOf
155
+ componentSchemas.Permission = { oneOf: permissionBranches };
156
+ componentSchemas.PermissionInput = { oneOf: permissionInputBranches };
157
+
158
+ // Patch /iam/check inline body with flat enums
159
+ const allResourceNames = entries.map(([name]) => name).sort();
160
+ const allActions = [...new Set(entries.flatMap(([, def]) => def.actions))].sort();
161
+
162
+ for (const ep of allEndpoints) {
163
+ if (ep.path === '/iam/check' && ep.input?.body?.properties) {
164
+ // Clone to avoid mutating the static endpoint definition
165
+ const bodyClone: JSONSchema = JSON.parse(JSON.stringify(ep.input.body));
166
+ if (bodyClone.properties!.resource) {
167
+ bodyClone.properties!.resource = { ...bodyClone.properties!.resource, enum: allResourceNames };
168
+ }
169
+ if (bodyClone.properties!.action && allActions.length > 0) {
170
+ bodyClone.properties!.action = { ...bodyClone.properties!.action, enum: allActions };
171
+ }
172
+ ep.input = { ...ep.input, body: bodyClone };
173
+ break;
174
+ }
175
+ }
176
+ }
177
+
178
+ /**
179
+ * OpenAPI plugin factory. Returns a {@link FortressPlugin} that walks every
180
+ * registered endpoint definition and emits a complete OpenAPI 3.1 spec,
181
+ * pairable with Scalar UI for interactive documentation.
182
+ */
183
+ export function openapi(config: OpenAPIConfig = {}): FortressPlugin & { readonly name: 'openapi' } {
184
+ const specPath = config.specPath ?? '/openapi.json';
185
+ const uiPath = config.uiPath ?? '/openapi';
186
+ const title = config.title ?? 'Fortress Auth API';
187
+ const version = config.version ?? '1.0.0';
188
+
189
+ let cachedSpec: OpenAPISpec | null = null;
190
+
191
+ const routes: FortressPlugin['routes'] = {
192
+ getSpec: {
193
+ method: 'GET',
194
+ path: specPath,
195
+ handler: 'getSpec',
196
+ meta: { summary: 'OpenAPI specification', tags: ['OpenAPI'], security: ['none'] },
197
+ responses: { 200: { description: 'OpenAPI 3.1 JSON spec' } },
198
+ },
199
+ };
200
+
201
+ if (!config.disableUI) {
202
+ routes.getUI = {
203
+ method: 'GET',
204
+ path: uiPath,
205
+ handler: 'getUI',
206
+ meta: { summary: 'API reference (Scalar)', tags: ['OpenAPI'], security: ['none'] },
207
+ responses: { 200: { description: 'Scalar API reference HTML' } },
208
+ };
209
+ }
210
+
211
+ return {
212
+ name: 'openapi',
213
+
214
+ routes,
215
+
216
+ methods: (ctx: PluginContext) => {
217
+ async function generateSpec(): Promise<OpenAPISpec> {
218
+ if (cachedSpec)
219
+ return cachedSpec;
220
+
221
+ // Collect all endpoints from the fortress config
222
+ const allEndpoints: EndpointDefinition[] = [];
223
+ const plugins = ctx.config.plugins ?? [];
224
+
225
+ // Add core auth endpoints
226
+ if (config.includeCoreAuth !== false) {
227
+ allEndpoints.push(...Object.values(authEndpoints) as EndpointDefinition[]);
228
+ }
229
+
230
+ // Add core IAM endpoints
231
+ if (config.includeCoreIam !== false) {
232
+ allEndpoints.push(...Object.values(iamEndpoints) as EndpointDefinition[]);
233
+ }
234
+
235
+ // Add top-level host routes. These are synthesized into the
236
+ // Fortress instance at runtime, but ctx.config keeps the caller's
237
+ // original object; include them explicitly so the OpenAPI plugin sees
238
+ // `createFortress({ routes })` just like `fortress.toOpenAPI()` does.
239
+ if (ctx.config.routes) {
240
+ allEndpoints.push(...Object.values(ctx.config.routes) as EndpointDefinition[]);
241
+ }
242
+
243
+ // Add plugin endpoints (excluding our own)
244
+ for (const plugin of plugins) {
245
+ if (plugin.name === 'openapi' || !plugin.routes)
246
+ continue;
247
+ allEndpoints.push(...Object.values(plugin.routes) as EndpointDefinition[]);
248
+ }
249
+
250
+ // Add consumer-provided endpoints
251
+ if (config.additionalEndpoints) {
252
+ allEndpoints.push(...config.additionalEndpoints);
253
+ }
254
+
255
+ // Merge component schemas
256
+ const componentSchemas: ComponentSchemas = {
257
+ ...(config.includeCoreAuth !== false ? authComponentSchemas : {}),
258
+ ...(config.includeCoreIam !== false ? iamComponentSchemas : {}),
259
+ ...(config.additionalSchemas ?? {}),
260
+ };
261
+
262
+ // Enrich IAM schemas with dynamic resource/action enums from the DB
263
+ if (config.includeCoreIam !== false && ctx.iam) {
264
+ try {
265
+ const resourceFile = await ctx.iam.getResources();
266
+ enrichIamSchemas(componentSchemas, allEndpoints, resourceFile);
267
+ }
268
+ catch {
269
+ // If resource fetch fails, fall back to plain string schemas
270
+ }
271
+ }
272
+
273
+ cachedSpec = buildOpenAPISpec(allEndpoints, componentSchemas, {
274
+ title,
275
+ version,
276
+ description: config.description,
277
+ servers: config.servers,
278
+ tags: config.tags,
279
+ operationId: config.operationId,
280
+ });
281
+
282
+ return cachedSpec;
283
+ }
284
+
285
+ return {
286
+ generateSpec,
287
+
288
+ async getSpec(): Promise<Record<string, unknown>> {
289
+ return await generateSpec() as unknown as Record<string, unknown>;
290
+ },
291
+
292
+ getUI(): string {
293
+ return buildScalarHTML(computeRelativeUrl(uiPath, specPath), title);
294
+ },
295
+ };
296
+ },
297
+ };
298
+ }