@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,307 @@
1
+ import { S as Subject, P as PermissionContext, b as Permission, c as PermissionInput, d as Role, a as SubjectType, G as Group, e as RoleBinding, F as FortressUser, C as CreateServiceAccountInput, f as ServiceAccount, R as RequestMeta, A as AuthResult, g as AuthMethod, h as AuthTokenPair, i as CreateUserInput, T as TokenClaims, j as SessionInfo, L as LoginIdentifierType, k as LoginIdentifier, l as PendingReason } from './types-et0R8tsC.cjs';
2
+
3
+ /**
4
+ * Structural logger contract. Shaped to be assignment-compatible with pino's
5
+ * `BaseLogger` and Fastify's `FastifyBaseLogger`, so consumers can drop a
6
+ * `pino()` instance or `fastify.log` in directly with zero adapter code.
7
+ *
8
+ * The default is {@link SILENT_LOGGER} — libraries that log to stderr by
9
+ * default are universally hated. Opt-in logging only.
10
+ */
11
+ type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
12
+ interface LogFn {
13
+ (msg: string): void;
14
+ (obj: Record<string, unknown>, msg?: string): void;
15
+ (msg: string, ...args: unknown[]): void;
16
+ }
17
+ interface FortressLogger {
18
+ level: LogLevel | 'silent' | string;
19
+ fatal: LogFn;
20
+ error: LogFn;
21
+ warn: LogFn;
22
+ info: LogFn;
23
+ debug: LogFn;
24
+ trace: LogFn;
25
+ silent: LogFn;
26
+ /** Optional on pino/Fastify; Fortress calls it defensively. */
27
+ child?: (bindings: Record<string, unknown>) => FortressLogger;
28
+ /** Optional guard for lazy evaluation of expensive log metadata. */
29
+ isLevelEnabled?: (level: LogLevel) => boolean;
30
+ }
31
+
32
+ /**
33
+ * Fortress-internal telemetry interfaces.
34
+ *
35
+ * Core code depends on these shapes, never on `@opentelemetry/api`. The OTel
36
+ * adapter in `src/otel/index.ts` is the only file in the repo that imports
37
+ * `@opentelemetry/api`; it returns a {@link TelemetryProvider} that satisfies
38
+ * these interfaces. This keeps `@opentelemetry/api` off the core critical
39
+ * path — runtimes like Cloudflare Workers and Deno never resolve it unless
40
+ * the user explicitly imports `@bajustone/fortress/otel`.
41
+ */
42
+ type AttributeValue = string | number | boolean;
43
+ type Attributes = Record<string, AttributeValue>;
44
+ interface Span {
45
+ setAttribute: (key: string, value: AttributeValue) => void;
46
+ recordException: (error: unknown) => void;
47
+ setStatus: (status: {
48
+ code: 'ok' | 'error';
49
+ message?: string;
50
+ }) => void;
51
+ end: () => void;
52
+ }
53
+ interface Tracer {
54
+ startSpan: (name: string, attributes?: Attributes) => Span;
55
+ /** Run a callback with this span as the active parent for nested spans. */
56
+ startActiveSpan?: <T>(name: string, attributes: Attributes | undefined, callback: (span: Span) => T | Promise<T>) => Promise<T>;
57
+ }
58
+ interface Counter {
59
+ add: (value: number, attributes?: Attributes) => void;
60
+ }
61
+ interface Histogram {
62
+ record: (value: number, attributes?: Attributes) => void;
63
+ }
64
+ interface Meter {
65
+ createCounter: (name: string, options?: {
66
+ description?: string;
67
+ unit?: string;
68
+ }) => Counter;
69
+ createHistogram: (name: string, options?: {
70
+ description?: string;
71
+ unit?: string;
72
+ boundaries?: number[];
73
+ }) => Histogram;
74
+ }
75
+ interface TelemetryProvider {
76
+ tracer: Tracer;
77
+ meter: Meter;
78
+ }
79
+
80
+ type Unsubscribe = () => void;
81
+
82
+ interface ResourceDefinition {
83
+ actions: string[];
84
+ description?: string;
85
+ }
86
+ interface ResourceFile {
87
+ resources: Record<string, ResourceDefinition>;
88
+ }
89
+ /** Validate and normalize the canonical map-shaped resource document. */
90
+ declare function parseResourceFile(value: unknown): ResourceFile;
91
+
92
+ interface IamEvent {
93
+ eventType: string;
94
+ actorId?: string | null;
95
+ targetId?: string | null;
96
+ targetType?: string | null;
97
+ metadata?: Record<string, unknown>;
98
+ }
99
+ /**
100
+ * Async IAM event listener. May return a Promise — if you `return` it, any
101
+ * rejection is routed to `config.logger.error`. Firing work via
102
+ * `void asyncWork()` inside a sync body is an explicit opt-out of that
103
+ * safety net; rejections will escape to the runtime's unhandled-rejection
104
+ * handler instead.
105
+ */
106
+ type IamEventListener = (event: IamEvent) => void | Promise<void>;
107
+ /**
108
+ * High-frequency event fired on every permission check. Emitted by a
109
+ * separate observer list from {@link IamEvent} so audit-log consumers
110
+ * (which subscribe to mutations) aren't spammed with per-check traffic.
111
+ *
112
+ * Listeners are invoked **synchronously**. The type signature intentionally
113
+ * returns `void` (not `Promise<void>`) to discourage awaiting expensive
114
+ * work on the hot path. If an observer needs async work it should fire
115
+ * and forget with `void asyncWork()`.
116
+ */
117
+ interface PermissionCheckEvent {
118
+ subjectType: 'USER' | 'SERVICE_ACCOUNT' | 'GROUP';
119
+ subjectId: string;
120
+ resource: string;
121
+ action: string;
122
+ allowed: boolean;
123
+ cached: boolean;
124
+ /** Pre-divided monotonic duration in seconds; ready to feed into a histogram. */
125
+ durationSeconds: number;
126
+ tenantId?: string;
127
+ }
128
+ type PermissionCheckListener = (event: PermissionCheckEvent) => void;
129
+ interface IamService {
130
+ checkPermission: (subject: Subject, resource: string, action: string, context?: PermissionContext) => Promise<boolean>;
131
+ getPermissionsForSubject: (subject: Subject, tenantId?: string) => Promise<Permission[]>;
132
+ createRole: (name: string, permissions: PermissionInput[], description?: string) => Promise<Role>;
133
+ deleteRole: (roleId: string) => Promise<void>;
134
+ bindRole: (subjectType: SubjectType, subjectId: string, roleId: string, tenantId?: string) => Promise<void>;
135
+ bindRoleToUser: (userId: string, roleId: string, tenantId?: string) => Promise<void>;
136
+ bindRoleToGroup: (groupId: string, roleId: string, tenantId?: string) => Promise<void>;
137
+ /** Omitted removes all scopes; null removes only the global binding; a string removes one tenant. */
138
+ unbindRole: (subjectType: SubjectType, subjectId: string, roleId: string, tenantId?: string | null) => Promise<void>;
139
+ bindPermissionToUser: (userId: string, permission: PermissionInput, tenantId?: string) => Promise<void>;
140
+ bindPermissionToGroup: (groupId: string, permission: PermissionInput, tenantId?: string) => Promise<void>;
141
+ unbindPermissionFromUser: (userId: string, permissionId: string, tenantId?: string | null) => Promise<void>;
142
+ unbindPermissionFromGroup: (groupId: string, permissionId: string, tenantId?: string | null) => Promise<void>;
143
+ createGroup: (name: string, description?: string) => Promise<Group>;
144
+ addUserToGroup: (groupId: string, userId: string) => Promise<void>;
145
+ removeUserFromGroup: (groupId: string, userId: string) => Promise<void>;
146
+ getResources: () => Promise<ResourceFile>;
147
+ getRoles: () => Promise<Role[]>;
148
+ /** Apply a resource map directly without filesystem access. */
149
+ pushResources: (resources: ResourceFile) => Promise<void>;
150
+ /** List role bindings: omitted scope means all, `null` means global only, string means one tenant. */
151
+ listRoleBindingsForSubject: (subject: Subject, tenantId?: string | null) => Promise<RoleBinding[]>;
152
+ syncResources: (direction: 'push' | 'pull', filePath?: string) => Promise<void>;
153
+ clearPermissionCache: () => void;
154
+ /**
155
+ * Register a listener for IAM mutation events. Multiple listeners are
156
+ * supported — each is invoked in registration order. Observer failures
157
+ * are routed to the configured logger at `error` level and never break
158
+ * IAM operations. Returns an unsubscribe function.
159
+ */
160
+ addIamObserver: (listener: IamEventListener) => Unsubscribe;
161
+ /**
162
+ * Register a listener for individual permission checks. Fires on every
163
+ * `checkPermission` call with latency and cache-hit information. The
164
+ * listener signature is synchronous — see {@link PermissionCheckEvent}
165
+ * for rationale. Returns an unsubscribe function.
166
+ */
167
+ addPermissionCheckObserver: (listener: PermissionCheckListener) => Unsubscribe;
168
+ getRole: (roleId: string) => Promise<Role & {
169
+ permissions: Permission[];
170
+ }>;
171
+ updateRole: (roleId: string, data: {
172
+ name?: string;
173
+ description?: string | null;
174
+ }) => Promise<Role>;
175
+ listGroups: (options?: {
176
+ limit?: number;
177
+ offset?: number;
178
+ }) => Promise<{
179
+ groups: Group[];
180
+ total: number;
181
+ }>;
182
+ getGroup: (groupId: string) => Promise<Group & {
183
+ users: FortressUser[];
184
+ }>;
185
+ updateGroup: (groupId: string, data: {
186
+ name?: string;
187
+ description?: string;
188
+ }) => Promise<Group>;
189
+ deleteGroup: (groupId: string) => Promise<void>;
190
+ getGroupUsers: (groupId: string) => Promise<FortressUser[]>;
191
+ listPermissions: (options?: {
192
+ resource?: string;
193
+ }) => Promise<Permission[]>;
194
+ createPermission: (permission: PermissionInput) => Promise<Permission>;
195
+ deletePermission: (permissionId: string) => Promise<void>;
196
+ addPermissionToRole: (roleId: string, permission: PermissionInput) => Promise<void>;
197
+ removePermissionFromRole: (roleId: string, permission: PermissionInput) => Promise<void>;
198
+ createServiceAccount: (input: CreateServiceAccountInput) => Promise<ServiceAccount>;
199
+ getServiceAccount: (id: string) => Promise<ServiceAccount>;
200
+ listServiceAccounts: (options?: {
201
+ limit?: number;
202
+ offset?: number;
203
+ }) => Promise<{
204
+ serviceAccounts: ServiceAccount[];
205
+ total: number;
206
+ }>;
207
+ updateServiceAccount: (id: string, data: {
208
+ displayName?: string | null;
209
+ description?: string | null;
210
+ isActive?: boolean;
211
+ }) => Promise<ServiceAccount>;
212
+ deleteServiceAccount: (id: string) => Promise<void>;
213
+ bindRoleToServiceAccount: (serviceAccountId: string, roleId: string, tenantId?: string) => Promise<void>;
214
+ /** Unbind across all scopes when omitted, global only for `null`, or one tenant for a string scope. */
215
+ unbindRoleFromServiceAccount: (serviceAccountId: string, roleId: string, tenantId?: string | null) => Promise<void>;
216
+ bindPermissionToServiceAccount: (serviceAccountId: string, permission: PermissionInput, tenantId?: string) => Promise<void>;
217
+ unbindPermissionFromServiceAccount: (serviceAccountId: string, permissionId: string, tenantId?: string | null) => Promise<void>;
218
+ }
219
+
220
+ /**
221
+ * Lifecycle event emitted by the auth service. Mirrors the IAM observer
222
+ * pattern (`addIamObserver`) so consumers — audit log, SIEM webhooks,
223
+ * telemetry plugins, Datadog adapters — can subscribe to auth events
224
+ * without reimplementing every hook individually.
225
+ */
226
+ interface AuthEvent {
227
+ eventType: 'LOGIN_SUCCESS' | 'LOGIN_FAILURE' | 'LOGIN_PENDING' | 'LOGOUT' | 'REGISTER' | 'TOKEN_REFRESH' | 'TOKEN_REUSE_DETECTED' | 'TOKEN_REUSE_GRACED' | 'TOKEN_FINGERPRINT_MISMATCH' | 'MFA_VERIFY_SUCCESS' | 'MFA_VERIFY_FAILURE' | 'SESSION_EXPIRED_IDLE' | 'SESSION_EXPIRED_ABSOLUTE' | 'PASSWORD_BREACH_CHECK_DEGRADED' | 'IMPERSONATE';
228
+ actorId?: string;
229
+ identifier?: string;
230
+ method?: 'password' | 'oauth' | 'magic_link' | 'webauthn' | '2fa' | 'api_key';
231
+ ipAddress?: string;
232
+ userAgent?: string;
233
+ outcome?: 'success' | 'failure' | 'pending';
234
+ /** Set on `LOGIN_PENDING` — which additional step the sign-in is waiting on. */
235
+ pendingReason?: PendingReason;
236
+ /** Free-form sub-action label (e.g. the specific fingerprint/session action that fired the event). */
237
+ action?: string;
238
+ error?: {
239
+ message: string;
240
+ code?: string;
241
+ };
242
+ metadata?: Record<string, unknown>;
243
+ }
244
+ /**
245
+ * Async auth event listener. May return a Promise — if you `return` it, any
246
+ * rejection is routed to `config.logger.error`. Firing work via
247
+ * `void asyncWork()` inside a sync body is an explicit opt-out of that
248
+ * safety net; rejections will escape to the runtime's unhandled-rejection
249
+ * handler instead.
250
+ */
251
+ type AuthEventListener = (event: AuthEvent) => void | Promise<void>;
252
+ interface AuthService {
253
+ login: (identifier: string, password: string, meta?: RequestMeta) => Promise<AuthResult>;
254
+ /** Consume a verified factor's continuation, rerun remaining gates, then issue tokens. */
255
+ completePendingAuth: (continuationToken: string, completion: unknown, meta?: RequestMeta) => Promise<AuthResult>;
256
+ /** Finish a trusted plugin-owned primary credential (for example a magic link). */
257
+ completePluginAuth: (userId: string, method: Exclude<AuthMethod, 'refresh' | 'impersonation'>, meta?: RequestMeta) => Promise<AuthResult>;
258
+ refresh: (refreshToken: string, meta?: RequestMeta) => Promise<AuthTokenPair>;
259
+ logout: (refreshToken: string) => Promise<void>;
260
+ me: (userId: string) => Promise<FortressUser>;
261
+ createUser: (data: CreateUserInput) => Promise<FortressUser>;
262
+ verifyToken: (token: string) => Promise<TokenClaims>;
263
+ signToken: (claims: Omit<TokenClaims, 'iat' | 'exp' | 'aud'>) => Promise<string>;
264
+ listSessions: (userId: string) => Promise<SessionInfo[]>;
265
+ revokeSession: (userId: string, tokenId: string) => Promise<void>;
266
+ revokeAllOtherSessions: (userId: string, currentTokenId: string) => Promise<void>;
267
+ addLoginIdentifier: (userId: string, type: LoginIdentifierType, value: string) => Promise<void>;
268
+ removeLoginIdentifier: (userId: string, type: LoginIdentifierType, value: string) => Promise<void>;
269
+ getLoginIdentifiers: (userId: string) => Promise<LoginIdentifier[]>;
270
+ /**
271
+ * Issue a short-lived, non-renewable access token that lets an admin act as another user.
272
+ * The token carries an RFC 8693 `act` claim identifying the real admin.
273
+ *
274
+ * Requires the admin user to hold `fortress:impersonate`. The built-in HTTP route also enforces this via endpoint metadata.
275
+ */
276
+ impersonate: (adminUserId: string, targetUserId: string, options?: {
277
+ reason?: string;
278
+ expirySeconds?: number;
279
+ }) => Promise<AuthResult>;
280
+ listUsers: (options: {
281
+ limit?: number;
282
+ offset?: number;
283
+ search?: string;
284
+ sortBy?: string;
285
+ sortDirection?: 'asc' | 'desc';
286
+ }) => Promise<{
287
+ users: FortressUser[];
288
+ total: number;
289
+ }>;
290
+ getUserById: (userId: string) => Promise<FortressUser>;
291
+ updateUser: (userId: string, data: {
292
+ name?: string;
293
+ email?: string;
294
+ isActive?: boolean;
295
+ password?: string;
296
+ }) => Promise<FortressUser>;
297
+ deleteUser: (userId: string) => Promise<void>;
298
+ /**
299
+ * Register a listener for auth lifecycle events. Multiple listeners are
300
+ * supported — each is invoked in registration order. Listener failures
301
+ * never break auth operations; they are routed to the configured logger
302
+ * at `error` level. Returns an unsubscribe function.
303
+ */
304
+ addAuthObserver: (listener: AuthEventListener) => Unsubscribe;
305
+ }
306
+
307
+ export { type AttributeValue as A, type Counter as C, type FortressLogger as F, type Histogram as H, type IamEvent as I, type Meter as M, type PermissionCheckEvent as P, type ResourceDefinition as R, type Span as S, type TelemetryProvider as T, type Unsubscribe as U, type Attributes as a, type AuthEvent as b, type AuthEventListener as c, type IamEventListener as d, type PermissionCheckListener as e, type Tracer as f, type IamService as g, type ResourceFile as h, type AuthService as i, parseResourceFile as p };
@@ -0,0 +1,307 @@
1
+ import { S as Subject, P as PermissionContext, b as Permission, c as PermissionInput, d as Role, a as SubjectType, G as Group, e as RoleBinding, F as FortressUser, C as CreateServiceAccountInput, f as ServiceAccount, R as RequestMeta, A as AuthResult, g as AuthMethod, h as AuthTokenPair, i as CreateUserInput, T as TokenClaims, j as SessionInfo, L as LoginIdentifierType, k as LoginIdentifier, l as PendingReason } from './types-et0R8tsC.js';
2
+
3
+ /**
4
+ * Structural logger contract. Shaped to be assignment-compatible with pino's
5
+ * `BaseLogger` and Fastify's `FastifyBaseLogger`, so consumers can drop a
6
+ * `pino()` instance or `fastify.log` in directly with zero adapter code.
7
+ *
8
+ * The default is {@link SILENT_LOGGER} — libraries that log to stderr by
9
+ * default are universally hated. Opt-in logging only.
10
+ */
11
+ type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
12
+ interface LogFn {
13
+ (msg: string): void;
14
+ (obj: Record<string, unknown>, msg?: string): void;
15
+ (msg: string, ...args: unknown[]): void;
16
+ }
17
+ interface FortressLogger {
18
+ level: LogLevel | 'silent' | string;
19
+ fatal: LogFn;
20
+ error: LogFn;
21
+ warn: LogFn;
22
+ info: LogFn;
23
+ debug: LogFn;
24
+ trace: LogFn;
25
+ silent: LogFn;
26
+ /** Optional on pino/Fastify; Fortress calls it defensively. */
27
+ child?: (bindings: Record<string, unknown>) => FortressLogger;
28
+ /** Optional guard for lazy evaluation of expensive log metadata. */
29
+ isLevelEnabled?: (level: LogLevel) => boolean;
30
+ }
31
+
32
+ /**
33
+ * Fortress-internal telemetry interfaces.
34
+ *
35
+ * Core code depends on these shapes, never on `@opentelemetry/api`. The OTel
36
+ * adapter in `src/otel/index.ts` is the only file in the repo that imports
37
+ * `@opentelemetry/api`; it returns a {@link TelemetryProvider} that satisfies
38
+ * these interfaces. This keeps `@opentelemetry/api` off the core critical
39
+ * path — runtimes like Cloudflare Workers and Deno never resolve it unless
40
+ * the user explicitly imports `@bajustone/fortress/otel`.
41
+ */
42
+ type AttributeValue = string | number | boolean;
43
+ type Attributes = Record<string, AttributeValue>;
44
+ interface Span {
45
+ setAttribute: (key: string, value: AttributeValue) => void;
46
+ recordException: (error: unknown) => void;
47
+ setStatus: (status: {
48
+ code: 'ok' | 'error';
49
+ message?: string;
50
+ }) => void;
51
+ end: () => void;
52
+ }
53
+ interface Tracer {
54
+ startSpan: (name: string, attributes?: Attributes) => Span;
55
+ /** Run a callback with this span as the active parent for nested spans. */
56
+ startActiveSpan?: <T>(name: string, attributes: Attributes | undefined, callback: (span: Span) => T | Promise<T>) => Promise<T>;
57
+ }
58
+ interface Counter {
59
+ add: (value: number, attributes?: Attributes) => void;
60
+ }
61
+ interface Histogram {
62
+ record: (value: number, attributes?: Attributes) => void;
63
+ }
64
+ interface Meter {
65
+ createCounter: (name: string, options?: {
66
+ description?: string;
67
+ unit?: string;
68
+ }) => Counter;
69
+ createHistogram: (name: string, options?: {
70
+ description?: string;
71
+ unit?: string;
72
+ boundaries?: number[];
73
+ }) => Histogram;
74
+ }
75
+ interface TelemetryProvider {
76
+ tracer: Tracer;
77
+ meter: Meter;
78
+ }
79
+
80
+ type Unsubscribe = () => void;
81
+
82
+ interface ResourceDefinition {
83
+ actions: string[];
84
+ description?: string;
85
+ }
86
+ interface ResourceFile {
87
+ resources: Record<string, ResourceDefinition>;
88
+ }
89
+ /** Validate and normalize the canonical map-shaped resource document. */
90
+ declare function parseResourceFile(value: unknown): ResourceFile;
91
+
92
+ interface IamEvent {
93
+ eventType: string;
94
+ actorId?: string | null;
95
+ targetId?: string | null;
96
+ targetType?: string | null;
97
+ metadata?: Record<string, unknown>;
98
+ }
99
+ /**
100
+ * Async IAM event listener. May return a Promise — if you `return` it, any
101
+ * rejection is routed to `config.logger.error`. Firing work via
102
+ * `void asyncWork()` inside a sync body is an explicit opt-out of that
103
+ * safety net; rejections will escape to the runtime's unhandled-rejection
104
+ * handler instead.
105
+ */
106
+ type IamEventListener = (event: IamEvent) => void | Promise<void>;
107
+ /**
108
+ * High-frequency event fired on every permission check. Emitted by a
109
+ * separate observer list from {@link IamEvent} so audit-log consumers
110
+ * (which subscribe to mutations) aren't spammed with per-check traffic.
111
+ *
112
+ * Listeners are invoked **synchronously**. The type signature intentionally
113
+ * returns `void` (not `Promise<void>`) to discourage awaiting expensive
114
+ * work on the hot path. If an observer needs async work it should fire
115
+ * and forget with `void asyncWork()`.
116
+ */
117
+ interface PermissionCheckEvent {
118
+ subjectType: 'USER' | 'SERVICE_ACCOUNT' | 'GROUP';
119
+ subjectId: string;
120
+ resource: string;
121
+ action: string;
122
+ allowed: boolean;
123
+ cached: boolean;
124
+ /** Pre-divided monotonic duration in seconds; ready to feed into a histogram. */
125
+ durationSeconds: number;
126
+ tenantId?: string;
127
+ }
128
+ type PermissionCheckListener = (event: PermissionCheckEvent) => void;
129
+ interface IamService {
130
+ checkPermission: (subject: Subject, resource: string, action: string, context?: PermissionContext) => Promise<boolean>;
131
+ getPermissionsForSubject: (subject: Subject, tenantId?: string) => Promise<Permission[]>;
132
+ createRole: (name: string, permissions: PermissionInput[], description?: string) => Promise<Role>;
133
+ deleteRole: (roleId: string) => Promise<void>;
134
+ bindRole: (subjectType: SubjectType, subjectId: string, roleId: string, tenantId?: string) => Promise<void>;
135
+ bindRoleToUser: (userId: string, roleId: string, tenantId?: string) => Promise<void>;
136
+ bindRoleToGroup: (groupId: string, roleId: string, tenantId?: string) => Promise<void>;
137
+ /** Omitted removes all scopes; null removes only the global binding; a string removes one tenant. */
138
+ unbindRole: (subjectType: SubjectType, subjectId: string, roleId: string, tenantId?: string | null) => Promise<void>;
139
+ bindPermissionToUser: (userId: string, permission: PermissionInput, tenantId?: string) => Promise<void>;
140
+ bindPermissionToGroup: (groupId: string, permission: PermissionInput, tenantId?: string) => Promise<void>;
141
+ unbindPermissionFromUser: (userId: string, permissionId: string, tenantId?: string | null) => Promise<void>;
142
+ unbindPermissionFromGroup: (groupId: string, permissionId: string, tenantId?: string | null) => Promise<void>;
143
+ createGroup: (name: string, description?: string) => Promise<Group>;
144
+ addUserToGroup: (groupId: string, userId: string) => Promise<void>;
145
+ removeUserFromGroup: (groupId: string, userId: string) => Promise<void>;
146
+ getResources: () => Promise<ResourceFile>;
147
+ getRoles: () => Promise<Role[]>;
148
+ /** Apply a resource map directly without filesystem access. */
149
+ pushResources: (resources: ResourceFile) => Promise<void>;
150
+ /** List role bindings: omitted scope means all, `null` means global only, string means one tenant. */
151
+ listRoleBindingsForSubject: (subject: Subject, tenantId?: string | null) => Promise<RoleBinding[]>;
152
+ syncResources: (direction: 'push' | 'pull', filePath?: string) => Promise<void>;
153
+ clearPermissionCache: () => void;
154
+ /**
155
+ * Register a listener for IAM mutation events. Multiple listeners are
156
+ * supported — each is invoked in registration order. Observer failures
157
+ * are routed to the configured logger at `error` level and never break
158
+ * IAM operations. Returns an unsubscribe function.
159
+ */
160
+ addIamObserver: (listener: IamEventListener) => Unsubscribe;
161
+ /**
162
+ * Register a listener for individual permission checks. Fires on every
163
+ * `checkPermission` call with latency and cache-hit information. The
164
+ * listener signature is synchronous — see {@link PermissionCheckEvent}
165
+ * for rationale. Returns an unsubscribe function.
166
+ */
167
+ addPermissionCheckObserver: (listener: PermissionCheckListener) => Unsubscribe;
168
+ getRole: (roleId: string) => Promise<Role & {
169
+ permissions: Permission[];
170
+ }>;
171
+ updateRole: (roleId: string, data: {
172
+ name?: string;
173
+ description?: string | null;
174
+ }) => Promise<Role>;
175
+ listGroups: (options?: {
176
+ limit?: number;
177
+ offset?: number;
178
+ }) => Promise<{
179
+ groups: Group[];
180
+ total: number;
181
+ }>;
182
+ getGroup: (groupId: string) => Promise<Group & {
183
+ users: FortressUser[];
184
+ }>;
185
+ updateGroup: (groupId: string, data: {
186
+ name?: string;
187
+ description?: string;
188
+ }) => Promise<Group>;
189
+ deleteGroup: (groupId: string) => Promise<void>;
190
+ getGroupUsers: (groupId: string) => Promise<FortressUser[]>;
191
+ listPermissions: (options?: {
192
+ resource?: string;
193
+ }) => Promise<Permission[]>;
194
+ createPermission: (permission: PermissionInput) => Promise<Permission>;
195
+ deletePermission: (permissionId: string) => Promise<void>;
196
+ addPermissionToRole: (roleId: string, permission: PermissionInput) => Promise<void>;
197
+ removePermissionFromRole: (roleId: string, permission: PermissionInput) => Promise<void>;
198
+ createServiceAccount: (input: CreateServiceAccountInput) => Promise<ServiceAccount>;
199
+ getServiceAccount: (id: string) => Promise<ServiceAccount>;
200
+ listServiceAccounts: (options?: {
201
+ limit?: number;
202
+ offset?: number;
203
+ }) => Promise<{
204
+ serviceAccounts: ServiceAccount[];
205
+ total: number;
206
+ }>;
207
+ updateServiceAccount: (id: string, data: {
208
+ displayName?: string | null;
209
+ description?: string | null;
210
+ isActive?: boolean;
211
+ }) => Promise<ServiceAccount>;
212
+ deleteServiceAccount: (id: string) => Promise<void>;
213
+ bindRoleToServiceAccount: (serviceAccountId: string, roleId: string, tenantId?: string) => Promise<void>;
214
+ /** Unbind across all scopes when omitted, global only for `null`, or one tenant for a string scope. */
215
+ unbindRoleFromServiceAccount: (serviceAccountId: string, roleId: string, tenantId?: string | null) => Promise<void>;
216
+ bindPermissionToServiceAccount: (serviceAccountId: string, permission: PermissionInput, tenantId?: string) => Promise<void>;
217
+ unbindPermissionFromServiceAccount: (serviceAccountId: string, permissionId: string, tenantId?: string | null) => Promise<void>;
218
+ }
219
+
220
+ /**
221
+ * Lifecycle event emitted by the auth service. Mirrors the IAM observer
222
+ * pattern (`addIamObserver`) so consumers — audit log, SIEM webhooks,
223
+ * telemetry plugins, Datadog adapters — can subscribe to auth events
224
+ * without reimplementing every hook individually.
225
+ */
226
+ interface AuthEvent {
227
+ eventType: 'LOGIN_SUCCESS' | 'LOGIN_FAILURE' | 'LOGIN_PENDING' | 'LOGOUT' | 'REGISTER' | 'TOKEN_REFRESH' | 'TOKEN_REUSE_DETECTED' | 'TOKEN_REUSE_GRACED' | 'TOKEN_FINGERPRINT_MISMATCH' | 'MFA_VERIFY_SUCCESS' | 'MFA_VERIFY_FAILURE' | 'SESSION_EXPIRED_IDLE' | 'SESSION_EXPIRED_ABSOLUTE' | 'PASSWORD_BREACH_CHECK_DEGRADED' | 'IMPERSONATE';
228
+ actorId?: string;
229
+ identifier?: string;
230
+ method?: 'password' | 'oauth' | 'magic_link' | 'webauthn' | '2fa' | 'api_key';
231
+ ipAddress?: string;
232
+ userAgent?: string;
233
+ outcome?: 'success' | 'failure' | 'pending';
234
+ /** Set on `LOGIN_PENDING` — which additional step the sign-in is waiting on. */
235
+ pendingReason?: PendingReason;
236
+ /** Free-form sub-action label (e.g. the specific fingerprint/session action that fired the event). */
237
+ action?: string;
238
+ error?: {
239
+ message: string;
240
+ code?: string;
241
+ };
242
+ metadata?: Record<string, unknown>;
243
+ }
244
+ /**
245
+ * Async auth event listener. May return a Promise — if you `return` it, any
246
+ * rejection is routed to `config.logger.error`. Firing work via
247
+ * `void asyncWork()` inside a sync body is an explicit opt-out of that
248
+ * safety net; rejections will escape to the runtime's unhandled-rejection
249
+ * handler instead.
250
+ */
251
+ type AuthEventListener = (event: AuthEvent) => void | Promise<void>;
252
+ interface AuthService {
253
+ login: (identifier: string, password: string, meta?: RequestMeta) => Promise<AuthResult>;
254
+ /** Consume a verified factor's continuation, rerun remaining gates, then issue tokens. */
255
+ completePendingAuth: (continuationToken: string, completion: unknown, meta?: RequestMeta) => Promise<AuthResult>;
256
+ /** Finish a trusted plugin-owned primary credential (for example a magic link). */
257
+ completePluginAuth: (userId: string, method: Exclude<AuthMethod, 'refresh' | 'impersonation'>, meta?: RequestMeta) => Promise<AuthResult>;
258
+ refresh: (refreshToken: string, meta?: RequestMeta) => Promise<AuthTokenPair>;
259
+ logout: (refreshToken: string) => Promise<void>;
260
+ me: (userId: string) => Promise<FortressUser>;
261
+ createUser: (data: CreateUserInput) => Promise<FortressUser>;
262
+ verifyToken: (token: string) => Promise<TokenClaims>;
263
+ signToken: (claims: Omit<TokenClaims, 'iat' | 'exp' | 'aud'>) => Promise<string>;
264
+ listSessions: (userId: string) => Promise<SessionInfo[]>;
265
+ revokeSession: (userId: string, tokenId: string) => Promise<void>;
266
+ revokeAllOtherSessions: (userId: string, currentTokenId: string) => Promise<void>;
267
+ addLoginIdentifier: (userId: string, type: LoginIdentifierType, value: string) => Promise<void>;
268
+ removeLoginIdentifier: (userId: string, type: LoginIdentifierType, value: string) => Promise<void>;
269
+ getLoginIdentifiers: (userId: string) => Promise<LoginIdentifier[]>;
270
+ /**
271
+ * Issue a short-lived, non-renewable access token that lets an admin act as another user.
272
+ * The token carries an RFC 8693 `act` claim identifying the real admin.
273
+ *
274
+ * Requires the admin user to hold `fortress:impersonate`. The built-in HTTP route also enforces this via endpoint metadata.
275
+ */
276
+ impersonate: (adminUserId: string, targetUserId: string, options?: {
277
+ reason?: string;
278
+ expirySeconds?: number;
279
+ }) => Promise<AuthResult>;
280
+ listUsers: (options: {
281
+ limit?: number;
282
+ offset?: number;
283
+ search?: string;
284
+ sortBy?: string;
285
+ sortDirection?: 'asc' | 'desc';
286
+ }) => Promise<{
287
+ users: FortressUser[];
288
+ total: number;
289
+ }>;
290
+ getUserById: (userId: string) => Promise<FortressUser>;
291
+ updateUser: (userId: string, data: {
292
+ name?: string;
293
+ email?: string;
294
+ isActive?: boolean;
295
+ password?: string;
296
+ }) => Promise<FortressUser>;
297
+ deleteUser: (userId: string) => Promise<void>;
298
+ /**
299
+ * Register a listener for auth lifecycle events. Multiple listeners are
300
+ * supported — each is invoked in registration order. Listener failures
301
+ * never break auth operations; they are routed to the configured logger
302
+ * at `error` level. Returns an unsubscribe function.
303
+ */
304
+ addAuthObserver: (listener: AuthEventListener) => Unsubscribe;
305
+ }
306
+
307
+ export { type AttributeValue as A, type Counter as C, type FortressLogger as F, type Histogram as H, type IamEvent as I, type Meter as M, type PermissionCheckEvent as P, type ResourceDefinition as R, type Span as S, type TelemetryProvider as T, type Unsubscribe as U, type Attributes as a, type AuthEvent as b, type AuthEventListener as c, type IamEventListener as d, type PermissionCheckListener as e, type Tracer as f, type IamService as g, type ResourceFile as h, type AuthService as i, parseResourceFile as p };