@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.
- package/CHANGELOG.md +1011 -0
- package/LICENSE +21 -0
- package/README.md +760 -0
- package/SECURITY.md +197 -0
- package/bin/fortress.ts +667 -0
- package/dist/auth-service-BcyQ2PuZ.d.cts +307 -0
- package/dist/auth-service-BkzZkWfH.d.ts +307 -0
- package/dist/config-B2366s_G.d.ts +665 -0
- package/dist/config-GtlVt6ZD.d.cts +665 -0
- package/dist/convert-routes-BLCQT57f.d.ts +72 -0
- package/dist/convert-routes-BdMvP2_X.d.cts +72 -0
- package/dist/crypto.cjs +61 -0
- package/dist/crypto.d.cts +36 -0
- package/dist/crypto.d.ts +36 -0
- package/dist/crypto.js +35 -0
- package/dist/drift-BT1wtMDJ.d.cts +22 -0
- package/dist/drift-k9LiYpRP.d.ts +22 -0
- package/dist/drizzle/pg.cjs +481 -0
- package/dist/drizzle/pg.d.cts +15 -0
- package/dist/drizzle/pg.d.ts +15 -0
- package/dist/drizzle/pg.js +454 -0
- package/dist/drizzle.cjs +1442 -0
- package/dist/drizzle.d.cts +85 -0
- package/dist/drizzle.d.ts +85 -0
- package/dist/drizzle.js +1411 -0
- package/dist/express.cjs +1357 -0
- package/dist/express.d.cts +333 -0
- package/dist/express.d.ts +333 -0
- package/dist/express.js +1314 -0
- package/dist/fetcher.cjs +77 -0
- package/dist/fetcher.d.cts +7 -0
- package/dist/fetcher.d.ts +7 -0
- package/dist/fetcher.js +41 -0
- package/dist/fortress-BmSvFfqK.d.cts +1089 -0
- package/dist/fortress-uA-_cheR.d.ts +1089 -0
- package/dist/hono.cjs +1530 -0
- package/dist/hono.d.cts +514 -0
- package/dist/hono.d.ts +514 -0
- package/dist/hono.js +1483 -0
- package/dist/index-CxEkfCA0.d.cts +77 -0
- package/dist/index-CxEkfCA0.d.ts +77 -0
- package/dist/index-D054pcb-.d.cts +146 -0
- package/dist/index-jAMbbUAY.d.ts +146 -0
- package/dist/index.cjs +9046 -0
- package/dist/index.d.cts +717 -0
- package/dist/index.d.ts +717 -0
- package/dist/index.js +8935 -0
- package/dist/jwt.cjs +255 -0
- package/dist/jwt.d.cts +90 -0
- package/dist/jwt.d.ts +90 -0
- package/dist/jwt.js +227 -0
- package/dist/otel.cjs +108 -0
- package/dist/otel.d.cts +62 -0
- package/dist/otel.d.ts +62 -0
- package/dist/otel.js +73 -0
- package/dist/plugins/account-lockout.cjs +322 -0
- package/dist/plugins/account-lockout.d.cts +42 -0
- package/dist/plugins/account-lockout.d.ts +42 -0
- package/dist/plugins/account-lockout.js +295 -0
- package/dist/plugins/admin.cjs +2378 -0
- package/dist/plugins/admin.d.cts +72 -0
- package/dist/plugins/admin.d.ts +72 -0
- package/dist/plugins/admin.js +2351 -0
- package/dist/plugins/api-key.cjs +792 -0
- package/dist/plugins/api-key.d.cts +121 -0
- package/dist/plugins/api-key.d.ts +121 -0
- package/dist/plugins/api-key.js +765 -0
- package/dist/plugins/audit-log.cjs +493 -0
- package/dist/plugins/audit-log.d.cts +86 -0
- package/dist/plugins/audit-log.d.ts +86 -0
- package/dist/plugins/audit-log.js +468 -0
- package/dist/plugins/data-isolation.cjs +211 -0
- package/dist/plugins/data-isolation.d.cts +49 -0
- package/dist/plugins/data-isolation.d.ts +49 -0
- package/dist/plugins/data-isolation.js +186 -0
- package/dist/plugins/email-verification.cjs +273 -0
- package/dist/plugins/email-verification.d.cts +44 -0
- package/dist/plugins/email-verification.d.ts +44 -0
- package/dist/plugins/email-verification.js +246 -0
- package/dist/plugins/magic-link.cjs +231 -0
- package/dist/plugins/magic-link.d.cts +39 -0
- package/dist/plugins/magic-link.d.ts +39 -0
- package/dist/plugins/magic-link.js +204 -0
- package/dist/plugins/oauth.cjs +1696 -0
- package/dist/plugins/oauth.d.cts +406 -0
- package/dist/plugins/oauth.d.ts +406 -0
- package/dist/plugins/oauth.js +1669 -0
- package/dist/plugins/openapi.cjs +1185 -0
- package/dist/plugins/openapi.d.cts +6 -0
- package/dist/plugins/openapi.d.ts +6 -0
- package/dist/plugins/openapi.js +1158 -0
- package/dist/plugins/rate-limit/express.cjs +55 -0
- package/dist/plugins/rate-limit/express.d.cts +64 -0
- package/dist/plugins/rate-limit/express.d.ts +64 -0
- package/dist/plugins/rate-limit/express.js +30 -0
- package/dist/plugins/rate-limit/hono.cjs +51 -0
- package/dist/plugins/rate-limit/hono.d.cts +59 -0
- package/dist/plugins/rate-limit/hono.d.ts +59 -0
- package/dist/plugins/rate-limit/hono.js +26 -0
- package/dist/plugins/rate-limit/sveltekit.cjs +49 -0
- package/dist/plugins/rate-limit/sveltekit.d.cts +59 -0
- package/dist/plugins/rate-limit/sveltekit.d.ts +59 -0
- package/dist/plugins/rate-limit/sveltekit.js +24 -0
- package/dist/plugins/rate-limit.cjs +354 -0
- package/dist/plugins/rate-limit.d.cts +140 -0
- package/dist/plugins/rate-limit.d.ts +140 -0
- package/dist/plugins/rate-limit.js +325 -0
- package/dist/plugins/social-login.cjs +905 -0
- package/dist/plugins/social-login.d.cts +114 -0
- package/dist/plugins/social-login.d.ts +114 -0
- package/dist/plugins/social-login.js +880 -0
- package/dist/plugins/tenancy.cjs +780 -0
- package/dist/plugins/tenancy.d.cts +108 -0
- package/dist/plugins/tenancy.d.ts +108 -0
- package/dist/plugins/tenancy.js +753 -0
- package/dist/plugins/two-factor.cjs +555 -0
- package/dist/plugins/two-factor.d.cts +67 -0
- package/dist/plugins/two-factor.d.ts +67 -0
- package/dist/plugins/two-factor.js +526 -0
- package/dist/plugins/webauthn.cjs +786 -0
- package/dist/plugins/webauthn.d.cts +72 -0
- package/dist/plugins/webauthn.d.ts +72 -0
- package/dist/plugins/webauthn.js +766 -0
- package/dist/plugins/webhook.cjs +819 -0
- package/dist/plugins/webhook.d.cts +254 -0
- package/dist/plugins/webhook.d.ts +254 -0
- package/dist/plugins/webhook.js +789 -0
- package/dist/protect-D1v_0upK.d.cts +123 -0
- package/dist/protect-DsxV_-dJ.d.ts +123 -0
- package/dist/sveltekit.cjs +1258 -0
- package/dist/sveltekit.d.cts +420 -0
- package/dist/sveltekit.d.ts +420 -0
- package/dist/sveltekit.js +1207 -0
- package/dist/testing.cjs +4294 -0
- package/dist/testing.d.cts +197 -0
- package/dist/testing.d.ts +197 -0
- package/dist/testing.js +4264 -0
- package/dist/types-et0R8tsC.d.cts +217 -0
- package/dist/types-et0R8tsC.d.ts +217 -0
- package/docs/adapter-typed-helpers.md +192 -0
- package/docs/admin-recipes.md +227 -0
- package/docs/architecture.md +434 -0
- package/docs/ci/github-actions.yml +59 -0
- package/docs/ci.md +109 -0
- package/docs/compatibility.md +115 -0
- package/docs/deployment.md +305 -0
- package/docs/hardening.md +118 -0
- package/docs/host-owned-routes.md +154 -0
- package/docs/migrations/0001-schema-version.md +54 -0
- package/docs/migrations/0002-initial-schema.md +84 -0
- package/docs/migrations/upgrade-guide.md +160 -0
- package/docs/observability.md +394 -0
- package/docs/plugins/account-lockout.md +131 -0
- package/docs/plugins/admin.md +402 -0
- package/docs/plugins/api-key.md +327 -0
- package/docs/plugins/audit-log.md +261 -0
- package/docs/plugins/data-isolation.md +186 -0
- package/docs/plugins/email-verification.md +121 -0
- package/docs/plugins/magic-link.md +123 -0
- package/docs/plugins/oauth.md +544 -0
- package/docs/plugins/openapi.md +250 -0
- package/docs/plugins/rate-limit.md +223 -0
- package/docs/plugins/social-login.md +337 -0
- package/docs/plugins/tenancy.md +122 -0
- package/docs/plugins/two-factor.md +191 -0
- package/docs/plugins/webauthn.md +188 -0
- package/docs/plugins/webhook.md +242 -0
- package/docs/policy-as-code.md +156 -0
- package/docs/route-manifest.md +46 -0
- package/docs/security.md +261 -0
- package/docs/threat-model.md +161 -0
- package/examples/README.md +119 -0
- package/examples/express-app/index.ts +747 -0
- package/examples/hono-app/docker-compose.yml +14 -0
- package/examples/hono-app/index.ts +1009 -0
- package/examples/policy/fortress.policy.json +47 -0
- package/examples/sveltekit-app/README.md +125 -0
- package/examples/sveltekit-app/src/app.d.ts +16 -0
- package/examples/sveltekit-app/src/hooks.server.ts +23 -0
- package/examples/sveltekit-app/src/lib/server/fortress.ts +81 -0
- package/examples/sveltekit-app/src/routes/api/echo/[id]/+server.ts +32 -0
- package/examples/sveltekit-app/src/routes/api/fortress/[...path]/+server.ts +15 -0
- package/examples/sveltekit-app/src/routes/dashboard/+page.server.ts +20 -0
- package/examples/sveltekit-app/src/routes/login/+page.server.ts +48 -0
- package/examples/sveltekit-app/src/routes/oauth/consent/+page.server.ts +69 -0
- package/examples/sveltekit-app/src/routes/oauth/consent/+page.svelte +83 -0
- package/migrations/pg/0001_schema_version.down.sql +2 -0
- package/migrations/pg/0001_schema_version.sql +8 -0
- package/migrations/pg/0002_initial_schema.down.sql +36 -0
- package/migrations/pg/0002_initial_schema.sql +376 -0
- package/migrations/pg/0003_auth_continuation.down.sql +6 -0
- package/migrations/pg/0003_auth_continuation.sql +30 -0
- package/migrations/pg/0004_tenant_default_unique.down.sql +1 -0
- package/migrations/pg/0004_tenant_default_unique.sql +14 -0
- package/migrations/pg/0005_hot_indexes_timestamptz.down.sql +71 -0
- package/migrations/pg/0005_hot_indexes_timestamptz.sql +71 -0
- package/migrations/pg/0006_canonical_email.down.sql +3 -0
- package/migrations/pg/0006_canonical_email.sql +8 -0
- package/migrations/pg/0007_audit_chain_anchor.down.sql +1 -0
- package/migrations/pg/0007_audit_chain_anchor.sql +8 -0
- package/migrations/pg/0008_two_factor_hardening.down.sql +8 -0
- package/migrations/pg/0008_two_factor_hardening.sql +8 -0
- package/migrations/pg/0009_encrypt_totp_secrets.down.sql +2 -0
- package/migrations/pg/0009_encrypt_totp_secrets.sql +5 -0
- package/migrations/pg/0010_bigint_append_only_ids.down.sql +2 -0
- package/migrations/pg/0010_bigint_append_only_ids.sql +23 -0
- package/migrations/sqlite/0001_schema_version.down.sql +2 -0
- package/migrations/sqlite/0001_schema_version.sql +8 -0
- package/migrations/sqlite/0002_initial_schema.down.sql +36 -0
- package/migrations/sqlite/0002_initial_schema.sql +376 -0
- package/migrations/sqlite/0003_auth_continuation.down.sql +27 -0
- package/migrations/sqlite/0003_auth_continuation.sql +45 -0
- package/migrations/sqlite/0004_tenant_default_unique.down.sql +1 -0
- package/migrations/sqlite/0004_tenant_default_unique.sql +13 -0
- package/migrations/sqlite/0005_hot_indexes_timestamptz.down.sql +10 -0
- package/migrations/sqlite/0005_hot_indexes_timestamptz.sql +10 -0
- package/migrations/sqlite/0006_canonical_email.down.sql +3 -0
- package/migrations/sqlite/0006_canonical_email.sql +8 -0
- package/migrations/sqlite/0007_audit_chain_anchor.down.sql +1 -0
- package/migrations/sqlite/0007_audit_chain_anchor.sql +8 -0
- package/migrations/sqlite/0008_two_factor_hardening.down.sql +14 -0
- package/migrations/sqlite/0008_two_factor_hardening.sql +7 -0
- package/migrations/sqlite/0009_encrypt_totp_secrets.down.sql +2 -0
- package/migrations/sqlite/0009_encrypt_totp_secrets.sql +5 -0
- package/migrations/sqlite/0010_bigint_append_only_ids.down.sql +1 -0
- package/migrations/sqlite/0010_bigint_append_only_ids.sql +2 -0
- package/package.json +398 -0
- package/src/adapters/database/index.ts +63 -0
- package/src/adapters/database/types.ts +22 -0
- package/src/changelog-script.test.ts +21 -0
- package/src/cli.test.ts +79 -0
- package/src/core/auth/auth-admin.test.ts +247 -0
- package/src/core/auth/auth-endpoints.ts +558 -0
- package/src/core/auth/auth-observer.test.ts +191 -0
- package/src/core/auth/auth-service.ts +1464 -0
- package/src/core/auth/email.test.ts +17 -0
- package/src/core/auth/email.ts +11 -0
- package/src/core/auth/hooks.test.ts +251 -0
- package/src/core/auth/impersonation.test.ts +179 -0
- package/src/core/auth/jwt.test.ts +184 -0
- package/src/core/auth/jwt.ts +239 -0
- package/src/core/auth/login-timing.test.ts +77 -0
- package/src/core/auth/password-policy.test.ts +253 -0
- package/src/core/auth/password-policy.ts +220 -0
- package/src/core/auth/password.test.ts +42 -0
- package/src/core/auth/password.ts +62 -0
- package/src/core/auth/post-auth-gate.test.ts +258 -0
- package/src/core/auth/post-auth-gate.ts +228 -0
- package/src/core/auth/refresh-token.test.ts +86 -0
- package/src/core/auth/refresh-token.ts +105 -0
- package/src/core/auth/timing-safe.ts +23 -0
- package/src/core/config.ts +212 -0
- package/src/core/endpoint.ts +149 -0
- package/src/core/errors.ts +199 -0
- package/src/core/fetcher-interop.test.ts +106 -0
- package/src/core/fortress.test.ts +354 -0
- package/src/core/fortress.ts +550 -0
- package/src/core/http/call.test.ts +148 -0
- package/src/core/http/call.ts +149 -0
- package/src/core/http/cookie-serialize.test.ts +198 -0
- package/src/core/http/cookie-serialize.ts +107 -0
- package/src/core/http/csrf.test.ts +140 -0
- package/src/core/http/csrf.ts +173 -0
- package/src/core/http/dispatch.ts +652 -0
- package/src/core/http/error-response.test.ts +69 -0
- package/src/core/http/error-response.ts +93 -0
- package/src/core/http/fortress-rbac.test.ts +43 -0
- package/src/core/http/fortress-rbac.ts +127 -0
- package/src/core/http/handle-request.test.ts +441 -0
- package/src/core/http/handle-request.ts +354 -0
- package/src/core/http/match.test.ts +122 -0
- package/src/core/http/match.ts +126 -0
- package/src/core/http/outbound.test.ts +34 -0
- package/src/core/http/outbound.ts +105 -0
- package/src/core/http/plugin-middleware.ts +67 -0
- package/src/core/http/principal.test.ts +345 -0
- package/src/core/http/principal.ts +86 -0
- package/src/core/http/protect.test.ts +220 -0
- package/src/core/http/protect.ts +394 -0
- package/src/core/http/token-extraction.test.ts +59 -0
- package/src/core/http/token-extraction.ts +53 -0
- package/src/core/iam/explain.test.ts +177 -0
- package/src/core/iam/explain.ts +302 -0
- package/src/core/iam/iam-endpoints.ts +779 -0
- package/src/core/iam/iam-service.test.ts +829 -0
- package/src/core/iam/iam-service.ts +1137 -0
- package/src/core/iam/permission-cache.test.ts +115 -0
- package/src/core/iam/permission-cache.ts +71 -0
- package/src/core/iam/permission-check-observer.test.ts +90 -0
- package/src/core/iam/permission-evaluator.test.ts +291 -0
- package/src/core/iam/permission-evaluator.ts +198 -0
- package/src/core/iam/permission-sync.test.ts +166 -0
- package/src/core/iam/permission-sync.ts +192 -0
- package/src/core/iam/resource-sync.test.ts +70 -0
- package/src/core/iam/resource-sync.ts +182 -0
- package/src/core/iam/service-account-http.test.ts +529 -0
- package/src/core/iam/service-account.test.ts +282 -0
- package/src/core/internal-adapter.test.ts +389 -0
- package/src/core/internal-adapter.ts +435 -0
- package/src/core/json-schema.ts +50 -0
- package/src/core/manifest/__snapshots__/route-manifest.test.ts.snap +192 -0
- package/src/core/manifest/drift.ts +103 -0
- package/src/core/manifest/route-manifest.test.ts +142 -0
- package/src/core/manifest/route-manifest.ts +154 -0
- package/src/core/migrate.test.ts +72 -0
- package/src/core/migrations/engine.test.ts +308 -0
- package/src/core/migrations/engine.ts +548 -0
- package/src/core/migrations/migrations.ts +1771 -0
- package/src/core/migrations/pg-migration.integration-test.ts +353 -0
- package/src/core/migrations/upgrade-fixture.test.ts +378 -0
- package/src/core/observability/db-instrumentation.ts +205 -0
- package/src/core/observability/listener-list.test.ts +124 -0
- package/src/core/observability/listener-list.ts +73 -0
- package/src/core/observability/logger.test.ts +43 -0
- package/src/core/observability/logger.ts +49 -0
- package/src/core/observability/spans.test.ts +193 -0
- package/src/core/observability/types.ts +80 -0
- package/src/core/openapi-drift.test.ts +41 -0
- package/src/core/openapi.ts +47 -0
- package/src/core/plugin-methods-map.ts +75 -0
- package/src/core/plugin-runner.test.ts +233 -0
- package/src/core/plugin-runner.ts +276 -0
- package/src/core/plugin.ts +210 -0
- package/src/core/policy/apply.ts +272 -0
- package/src/core/policy/diff.ts +288 -0
- package/src/core/policy/loader.test.ts +63 -0
- package/src/core/policy/loader.ts +214 -0
- package/src/core/policy/policy.test.ts +232 -0
- package/src/core/policy/types.ts +105 -0
- package/src/core/schema-builder.test.ts +660 -0
- package/src/core/schema-builder.ts +881 -0
- package/src/core/standard-schema.ts +56 -0
- package/src/core/types.ts +258 -0
- package/src/core/validation.test.ts +195 -0
- package/src/core/validation.ts +192 -0
- package/src/drizzle/adapter.test.ts +425 -0
- package/src/drizzle/adapter.ts +474 -0
- package/src/drizzle/index.ts +31 -0
- package/src/drizzle/pg/adapter.integration-test.ts +456 -0
- package/src/drizzle/pg/index.ts +13 -0
- package/src/drizzle/pg/pg.integration-test.ts +1539 -0
- package/src/drizzle/pg/schema.ts +539 -0
- package/src/drizzle/pg-error-map.test.ts +218 -0
- package/src/drizzle/pg-error-map.ts +151 -0
- package/src/drizzle/schema.ts +544 -0
- package/src/drizzle/sqlite-serialization.test.ts +106 -0
- package/src/express/express-api-key-user-routes.test.ts +241 -0
- package/src/express/express.test.ts +442 -0
- package/src/express/handle.ts +191 -0
- package/src/express/index.ts +62 -0
- package/src/express/middleware.ts +551 -0
- package/src/express/protect.ts +154 -0
- package/src/express/validated.test.ts +86 -0
- package/src/express/validated.ts +99 -0
- package/src/fetcher/index.ts +81 -0
- package/src/hono/convert-routes.test.ts +220 -0
- package/src/hono/convert-routes.ts +196 -0
- package/src/hono/converters.test.ts +50 -0
- package/src/hono/converters.ts +43 -0
- package/src/hono/handle.ts +79 -0
- package/src/hono/helpers.ts +2 -0
- package/src/hono/hono-api-key-user-routes.test.ts +225 -0
- package/src/hono/hono-handlers.test.ts +861 -0
- package/src/hono/hono.test.ts +454 -0
- package/src/hono/index.ts +101 -0
- package/src/hono/middleware/auth.ts +236 -0
- package/src/hono/middleware/auth.types.test.ts +94 -0
- package/src/hono/middleware/csrf.test.ts +127 -0
- package/src/hono/middleware/csrf.ts +68 -0
- package/src/hono/middleware/error-handler.ts +12 -0
- package/src/hono/middleware/plugin-middleware.ts +35 -0
- package/src/hono/middleware/rbac.ts +131 -0
- package/src/hono/middleware/security-headers.test.ts +88 -0
- package/src/hono/middleware/security-headers.ts +68 -0
- package/src/hono/openapi.ts +237 -0
- package/src/hono/protect.ts +87 -0
- package/src/hono/validated.test.ts +123 -0
- package/src/hono/validated.ts +62 -0
- package/src/index.ts +309 -0
- package/src/integration.test.ts +718 -0
- package/src/internal/changelog.ts +56 -0
- package/src/otel/index.ts +158 -0
- package/src/otel/otel-types.test.ts +35 -0
- package/src/otel/otel.test.ts +235 -0
- package/src/plugins/account-lockout/account-lockout.test.ts +367 -0
- package/src/plugins/account-lockout/index.ts +267 -0
- package/src/plugins/admin/admin-api-key.test.ts +438 -0
- package/src/plugins/admin/index.ts +1612 -0
- package/src/plugins/api-key/api-key.test.ts +684 -0
- package/src/plugins/api-key/core.ts +336 -0
- package/src/plugins/api-key/index.ts +315 -0
- package/src/plugins/audit-log/audit-log.test.ts +749 -0
- package/src/plugins/audit-log/index.ts +680 -0
- package/src/plugins/data-isolation/data-isolation.test.ts +197 -0
- package/src/plugins/data-isolation/index.ts +157 -0
- package/src/plugins/email-verification/email-verification.test.ts +199 -0
- package/src/plugins/email-verification/index.ts +190 -0
- package/src/plugins/magic-link/index.ts +129 -0
- package/src/plugins/magic-link/magic-link.test.ts +156 -0
- package/src/plugins/oauth/id-token.ts +86 -0
- package/src/plugins/oauth/index.ts +2145 -0
- package/src/plugins/oauth/jwks.test.ts +32 -0
- package/src/plugins/oauth/jwks.ts +182 -0
- package/src/plugins/oauth/oauth.test.ts +2220 -0
- package/src/plugins/oauth/pkce.ts +58 -0
- package/src/plugins/openapi/index.ts +298 -0
- package/src/plugins/openapi/openapi.test.ts +425 -0
- package/src/plugins/openapi/spec-builder.ts +287 -0
- package/src/plugins/rate-limit/express.test.ts +89 -0
- package/src/plugins/rate-limit/express.ts +86 -0
- package/src/plugins/rate-limit/hono.test.ts +60 -0
- package/src/plugins/rate-limit/hono.ts +74 -0
- package/src/plugins/rate-limit/index.ts +383 -0
- package/src/plugins/rate-limit/memory-store.ts +61 -0
- package/src/plugins/rate-limit/rate-limit.test.ts +756 -0
- package/src/plugins/rate-limit/sveltekit.test.ts +58 -0
- package/src/plugins/rate-limit/sveltekit.ts +66 -0
- package/src/plugins/social-login/index.ts +715 -0
- package/src/plugins/social-login/providers/apple.ts +34 -0
- package/src/plugins/social-login/providers/discord.ts +26 -0
- package/src/plugins/social-login/providers/github.ts +54 -0
- package/src/plugins/social-login/providers/google.ts +23 -0
- package/src/plugins/social-login/providers/index.ts +22 -0
- package/src/plugins/social-login/providers/microsoft.ts +35 -0
- package/src/plugins/social-login/providers/oidc.ts +37 -0
- package/src/plugins/social-login/providers/providers.test.ts +211 -0
- package/src/plugins/social-login/social-login.test.ts +675 -0
- package/src/plugins/social-login/types.ts +80 -0
- package/src/plugins/tenancy/index.ts +544 -0
- package/src/plugins/tenancy/tenancy.test.ts +323 -0
- package/src/plugins/two-factor/index.ts +569 -0
- package/src/plugins/two-factor/two-factor.test.ts +235 -0
- package/src/plugins/webauthn/index.ts +554 -0
- package/src/plugins/webauthn/webauthn.test.ts +472 -0
- package/src/plugins/webhook/builtin-events.ts +29 -0
- package/src/plugins/webhook/delivery.test.ts +51 -0
- package/src/plugins/webhook/delivery.ts +154 -0
- package/src/plugins/webhook/hooks.ts +89 -0
- package/src/plugins/webhook/index.ts +445 -0
- package/src/plugins/webhook/queue/database.ts +67 -0
- package/src/plugins/webhook/queue/in-memory.test.ts +48 -0
- package/src/plugins/webhook/queue/in-memory.ts +71 -0
- package/src/plugins/webhook/queue/types.ts +51 -0
- package/src/plugins/webhook/registry.test.ts +48 -0
- package/src/plugins/webhook/registry.ts +64 -0
- package/src/plugins/webhook/signing.ts +45 -0
- package/src/plugins/webhook/ssrf.ts +198 -0
- package/src/plugins/webhook/types.ts +138 -0
- package/src/plugins/webhook/webhook.test.ts +324 -0
- package/src/sveltekit/actions.ts +233 -0
- package/src/sveltekit/catch-all.ts +43 -0
- package/src/sveltekit/cookies.ts +136 -0
- package/src/sveltekit/handle.ts +356 -0
- package/src/sveltekit/helpers.ts +69 -0
- package/src/sveltekit/index.ts +74 -0
- package/src/sveltekit/protect.ts +80 -0
- package/src/sveltekit/sveltekit-types.test.ts +31 -0
- package/src/sveltekit/sveltekit.test.ts +799 -0
- package/src/sveltekit/types.ts +134 -0
- package/src/sveltekit/validated.test.ts +117 -0
- package/src/sveltekit/validated.ts +78 -0
- package/src/testing/adapter-conformance.test.ts +408 -0
- package/src/testing/checks.test.ts +124 -0
- package/src/testing/checks.ts +304 -0
- package/src/testing/index.ts +107 -0
|
@@ -0,0 +1,1089 @@
|
|
|
1
|
+
import { S as SpecBuilderOptions, O as OpenAPISpec, a as OpenAPIMethods } from './index-jAMbbUAY.js';
|
|
2
|
+
import { g as IamService, i as AuthService, F as FortressLogger, T as TelemetryProvider } from './auth-service-BkzZkWfH.js';
|
|
3
|
+
import { E as EndpointDefinition, h as FortressSchema, H as HttpMethod, O as EndpointPermission, g as SecurityRequirement, C as ComponentSchemas, c as InferEndpointCallInput, r as InferEndpointSuccessResponse, F as FortressPlugin, o as FortressConfig, R as ResolvedCookieConfig, M as MiddlewareDefinition, B as PluginRequestContext } from './config-B2366s_G.js';
|
|
4
|
+
import { l as PendingReason, g as AuthMethod, S as Subject, T as TokenClaims } from './types-et0R8tsC.js';
|
|
5
|
+
import { D as DatabaseAdapter } from './index-CxEkfCA0.js';
|
|
6
|
+
import { ApiKeyMethods } from './plugins/api-key.js';
|
|
7
|
+
import { AuditLogMethods } from './plugins/audit-log.js';
|
|
8
|
+
import { DataIsolationMethods } from './plugins/data-isolation.js';
|
|
9
|
+
import { EmailVerificationMethods } from './plugins/email-verification.js';
|
|
10
|
+
import { MagicLinkMethods } from './plugins/magic-link.js';
|
|
11
|
+
import { OAuthMethods } from './plugins/oauth.js';
|
|
12
|
+
import { SocialLoginMethods } from './plugins/social-login.js';
|
|
13
|
+
import { TenancyMethods } from './plugins/tenancy.js';
|
|
14
|
+
import { TwoFactorMethods } from './plugins/two-factor.js';
|
|
15
|
+
import { WebAuthnMethods } from './plugins/webauthn.js';
|
|
16
|
+
|
|
17
|
+
interface EmptyInput$1 {
|
|
18
|
+
}
|
|
19
|
+
/** Wire shape of a fortress user — domain `Date` fields are ISO strings on the wire. */
|
|
20
|
+
interface UserWire {
|
|
21
|
+
id: string;
|
|
22
|
+
email: string;
|
|
23
|
+
name: string;
|
|
24
|
+
isActive: boolean;
|
|
25
|
+
emailVerified?: boolean;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
updatedAt: string;
|
|
28
|
+
}
|
|
29
|
+
/** Wire shape of a pending post-auth challenge. */
|
|
30
|
+
interface AuthChallengeWire {
|
|
31
|
+
reason: PendingReason;
|
|
32
|
+
continuationToken: string;
|
|
33
|
+
}
|
|
34
|
+
/** Wire shape of a successful sign-in. */
|
|
35
|
+
interface AuthSuccessWire {
|
|
36
|
+
status: 'success';
|
|
37
|
+
user: UserWire;
|
|
38
|
+
method: AuthMethod;
|
|
39
|
+
accessToken: string;
|
|
40
|
+
refreshToken: string;
|
|
41
|
+
pluginData?: Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
/** Wire shape of a pending sign-in (2FA, email verification, etc.). */
|
|
44
|
+
interface AuthPendingWire {
|
|
45
|
+
status: 'pending';
|
|
46
|
+
user: UserWire;
|
|
47
|
+
pending: AuthChallengeWire;
|
|
48
|
+
pluginData?: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
/** Wire shape of an impersonation sign-in (no refresh token). */
|
|
51
|
+
interface AuthImpersonationWire {
|
|
52
|
+
status: 'impersonation';
|
|
53
|
+
user: UserWire;
|
|
54
|
+
accessToken: string;
|
|
55
|
+
refreshToken: null;
|
|
56
|
+
pluginData?: Record<string, unknown>;
|
|
57
|
+
}
|
|
58
|
+
/** Discriminated union of every auth-flow wire outcome. */
|
|
59
|
+
type AuthResultWire = AuthSuccessWire | AuthPendingWire | AuthImpersonationWire;
|
|
60
|
+
/** Wire shape of a refreshed access/refresh token pair. */
|
|
61
|
+
interface AuthTokenPairWire {
|
|
62
|
+
accessToken: string;
|
|
63
|
+
refreshToken: string;
|
|
64
|
+
}
|
|
65
|
+
/** Wire shape of a persisted refresh-token session. */
|
|
66
|
+
interface SessionInfoWire {
|
|
67
|
+
id: string;
|
|
68
|
+
ipAddress?: string | null;
|
|
69
|
+
userAgent?: string | null;
|
|
70
|
+
deviceName?: string | null;
|
|
71
|
+
createdAt: string;
|
|
72
|
+
lastActiveAt?: string | null;
|
|
73
|
+
}
|
|
74
|
+
/** Wire shape of the create-user input body. */
|
|
75
|
+
interface CreateUserInputWire {
|
|
76
|
+
email: string;
|
|
77
|
+
name: string;
|
|
78
|
+
password?: string;
|
|
79
|
+
isActive?: boolean;
|
|
80
|
+
}
|
|
81
|
+
/** Wire shape of a structured error response. */
|
|
82
|
+
interface ErrorResponseWire {
|
|
83
|
+
code: string;
|
|
84
|
+
message: string;
|
|
85
|
+
statusCode: number;
|
|
86
|
+
}
|
|
87
|
+
/** Wire shape of a login identifier. */
|
|
88
|
+
interface LoginIdentifierWire {
|
|
89
|
+
id: string;
|
|
90
|
+
userId: string;
|
|
91
|
+
type: 'email' | 'phone' | 'username';
|
|
92
|
+
value: string;
|
|
93
|
+
}
|
|
94
|
+
/** Empty `{ ok: boolean }` ack. */
|
|
95
|
+
interface OkResponseWire {
|
|
96
|
+
ok: boolean;
|
|
97
|
+
}
|
|
98
|
+
/** Explicit registry type so JSR's fast-check doesn't walk into the deeply-inferred `defineComponents` return. */
|
|
99
|
+
interface AuthComponents {
|
|
100
|
+
readonly components: {
|
|
101
|
+
readonly User: FortressSchema<UserWire>;
|
|
102
|
+
readonly AuthChallenge: FortressSchema<AuthChallengeWire>;
|
|
103
|
+
readonly AuthResult: FortressSchema<AuthResultWire>;
|
|
104
|
+
readonly AuthTokenPair: FortressSchema<AuthTokenPairWire>;
|
|
105
|
+
readonly SessionInfo: FortressSchema<SessionInfoWire>;
|
|
106
|
+
readonly CreateUserInput: FortressSchema<CreateUserInputWire>;
|
|
107
|
+
readonly ErrorResponse: FortressSchema<ErrorResponseWire>;
|
|
108
|
+
readonly LoginIdentifier: FortressSchema<LoginIdentifierWire>;
|
|
109
|
+
};
|
|
110
|
+
readonly ref: <K extends keyof AuthComponents['components']>(name: K) => FortressSchema<AuthComponents['components'][K] extends FortressSchema<infer U> ? U : never>;
|
|
111
|
+
}
|
|
112
|
+
/** Reusable OpenAPI component schemas referenced by the core auth endpoints. */
|
|
113
|
+
declare const authComponentSchemas: AuthComponents['components'];
|
|
114
|
+
/**
|
|
115
|
+
* Typed record of every core auth endpoint. Declared explicitly (not
|
|
116
|
+
* inferred from the builder) so JSR's fast-check passes without
|
|
117
|
+
* `--allow-slow-types`, while `InferEndpointCallInput<typeof authEndpoints.login>`
|
|
118
|
+
* and friends still resolve to the precise per-endpoint shapes.
|
|
119
|
+
*/
|
|
120
|
+
interface AuthEndpointsMap {
|
|
121
|
+
login: EndpointDefinition<{
|
|
122
|
+
identifier: string;
|
|
123
|
+
password: string;
|
|
124
|
+
trustedDeviceToken?: string;
|
|
125
|
+
}, EmptyInput$1, EmptyInput$1, {
|
|
126
|
+
200: AuthResultWire;
|
|
127
|
+
401: ErrorResponseWire;
|
|
128
|
+
}>;
|
|
129
|
+
verifyTwoFactor: EndpointDefinition<{
|
|
130
|
+
continuationToken: string;
|
|
131
|
+
code: string;
|
|
132
|
+
rememberDevice?: boolean;
|
|
133
|
+
}, EmptyInput$1, EmptyInput$1, {
|
|
134
|
+
200: AuthResultWire;
|
|
135
|
+
400: ErrorResponseWire;
|
|
136
|
+
401: ErrorResponseWire;
|
|
137
|
+
}>;
|
|
138
|
+
verifyMagicLink: EndpointDefinition<{
|
|
139
|
+
token: string;
|
|
140
|
+
}, EmptyInput$1, EmptyInput$1, {
|
|
141
|
+
200: AuthResultWire;
|
|
142
|
+
400: ErrorResponseWire;
|
|
143
|
+
404: ErrorResponseWire;
|
|
144
|
+
}>;
|
|
145
|
+
createUser: EndpointDefinition<CreateUserInputWire, EmptyInput$1, EmptyInput$1, {
|
|
146
|
+
201: UserWire;
|
|
147
|
+
409: ErrorResponseWire;
|
|
148
|
+
}>;
|
|
149
|
+
refresh: EndpointDefinition<{
|
|
150
|
+
refreshToken: string;
|
|
151
|
+
}, EmptyInput$1, EmptyInput$1, {
|
|
152
|
+
200: AuthTokenPairWire;
|
|
153
|
+
401: ErrorResponseWire;
|
|
154
|
+
}>;
|
|
155
|
+
logout: EndpointDefinition<{
|
|
156
|
+
refreshToken: string;
|
|
157
|
+
}, EmptyInput$1, EmptyInput$1, {
|
|
158
|
+
200: OkResponseWire;
|
|
159
|
+
}>;
|
|
160
|
+
me: EndpointDefinition<EmptyInput$1, EmptyInput$1, EmptyInput$1, {
|
|
161
|
+
200: UserWire;
|
|
162
|
+
401: ErrorResponseWire;
|
|
163
|
+
}>;
|
|
164
|
+
listSessions: EndpointDefinition<EmptyInput$1, EmptyInput$1, EmptyInput$1, {
|
|
165
|
+
200: SessionInfoWire[];
|
|
166
|
+
401: ErrorResponseWire;
|
|
167
|
+
}>;
|
|
168
|
+
revokeSession: EndpointDefinition<EmptyInput$1, EmptyInput$1, {
|
|
169
|
+
id: string;
|
|
170
|
+
}, {
|
|
171
|
+
200: OkResponseWire;
|
|
172
|
+
401: ErrorResponseWire;
|
|
173
|
+
}>;
|
|
174
|
+
revokeAllOtherSessions: EndpointDefinition<{
|
|
175
|
+
currentTokenId: string;
|
|
176
|
+
}, EmptyInput$1, EmptyInput$1, {
|
|
177
|
+
200: OkResponseWire;
|
|
178
|
+
401: ErrorResponseWire;
|
|
179
|
+
}>;
|
|
180
|
+
addLoginIdentifier: EndpointDefinition<{
|
|
181
|
+
type: 'email' | 'phone' | 'username';
|
|
182
|
+
value: string;
|
|
183
|
+
}, EmptyInput$1, EmptyInput$1, {
|
|
184
|
+
200: OkResponseWire;
|
|
185
|
+
401: ErrorResponseWire;
|
|
186
|
+
}>;
|
|
187
|
+
removeLoginIdentifier: EndpointDefinition<{
|
|
188
|
+
type: 'email' | 'phone' | 'username';
|
|
189
|
+
value: string;
|
|
190
|
+
}, EmptyInput$1, EmptyInput$1, {
|
|
191
|
+
200: OkResponseWire;
|
|
192
|
+
401: ErrorResponseWire;
|
|
193
|
+
}>;
|
|
194
|
+
getLoginIdentifiers: EndpointDefinition<EmptyInput$1, EmptyInput$1, EmptyInput$1, {
|
|
195
|
+
200: LoginIdentifierWire[];
|
|
196
|
+
401: ErrorResponseWire;
|
|
197
|
+
}>;
|
|
198
|
+
impersonate: EndpointDefinition<{
|
|
199
|
+
targetUserId: string;
|
|
200
|
+
reason?: string;
|
|
201
|
+
expirySeconds?: number;
|
|
202
|
+
}, EmptyInput$1, EmptyInput$1, {
|
|
203
|
+
200: AuthResultWire;
|
|
204
|
+
401: ErrorResponseWire;
|
|
205
|
+
404: ErrorResponseWire;
|
|
206
|
+
}>;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Declarative endpoint definitions for fortress's built-in auth routes
|
|
210
|
+
* (sign in, refresh, sessions, impersonation).
|
|
211
|
+
*
|
|
212
|
+
* The explicit `AuthEndpointsMap` annotation is what keeps JSR fast-check
|
|
213
|
+
* happy — each entry's `EndpointDefinition<TBody, TQuery, TParams, TResponses>`
|
|
214
|
+
* generics are stated declaratively, not inferred from the builder chain.
|
|
215
|
+
* `fortress.call.*` type inference still resolves because `typeof
|
|
216
|
+
* authEndpoints.login` picks up the exact generics declared on the map.
|
|
217
|
+
*/
|
|
218
|
+
declare const authEndpoints: AuthEndpointsMap;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Cookie serialization for fortress auth tokens.
|
|
222
|
+
*
|
|
223
|
+
* Produces and parses cookie strings for the access/refresh token pair using
|
|
224
|
+
* the resolved {@link ResolvedCookieConfig} (which already accounts for
|
|
225
|
+
* `__Host-` prefix rules and dev-vs-prod relaxation).
|
|
226
|
+
*
|
|
227
|
+
* Returns raw `Set-Cookie` header values so any framework adapter can append
|
|
228
|
+
* them to a `Response` directly without depending on a framework cookie API.
|
|
229
|
+
*/
|
|
230
|
+
|
|
231
|
+
/** Result of an auth flow that may set cookies on a Response. */
|
|
232
|
+
interface AuthCookiePayload {
|
|
233
|
+
accessToken: string;
|
|
234
|
+
refreshToken?: string | null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Shared principal resolution for `fortress.handleRequest` and user-route
|
|
239
|
+
* adapters (Hono / Express / SvelteKit).
|
|
240
|
+
*
|
|
241
|
+
* Plugins that register a `resolvePrincipal` capability (e.g. `api-key`) are
|
|
242
|
+
* tried in registration order; the first non-null result wins. If no plugin
|
|
243
|
+
* resolves the request, a JWT bearer token is attempted as a fallback.
|
|
244
|
+
*
|
|
245
|
+
* Two variants are exported:
|
|
246
|
+
*
|
|
247
|
+
* - {@link tryPluginPrincipal} — only walks the plugin chain. Used by
|
|
248
|
+
* `handle-request.ts`, which needs to decide whether to enforce the JWT
|
|
249
|
+
* fallback based on the endpoint's `security` metadata.
|
|
250
|
+
* - {@link resolveRequestPrincipal} — plugin chain + non-throwing JWT
|
|
251
|
+
* fallback. Used by adapter user-route middleware so that API keys and
|
|
252
|
+
* cookies/bearer tokens authenticate uniformly on *any* route, not just
|
|
253
|
+
* Fortress-owned routes.
|
|
254
|
+
*/
|
|
255
|
+
|
|
256
|
+
/** Result of a successful principal resolution. */
|
|
257
|
+
interface ResolvedPrincipal {
|
|
258
|
+
subject: Subject;
|
|
259
|
+
claims?: TokenClaims;
|
|
260
|
+
/** Credential-level narrowing scopes (e.g. API-key scopes). null/undefined = unscoped. */
|
|
261
|
+
scopes?: string[] | null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Manifest-driven permission seeding.
|
|
266
|
+
*
|
|
267
|
+
* `endpoint().permission(resource, action)` declares the permission an
|
|
268
|
+
* endpoint needs, but a freshly migrated database has no `Permission` row
|
|
269
|
+
* for it. Every consumer used to write the same seed script: walk
|
|
270
|
+
* `fortress.endpoints`, dedupe `(resource, action)` pairs, call
|
|
271
|
+
* `iam.createPermission(...)` for each, optionally bind defaults onto a
|
|
272
|
+
* couple of well-known roles.
|
|
273
|
+
*
|
|
274
|
+
* This module replaces that script. It's idempotent (re-runs safely on a
|
|
275
|
+
* partially seeded DB) and exposed on the {@link Fortress} instance as
|
|
276
|
+
* `fortress.syncPermissionsFromManifest()`.
|
|
277
|
+
*/
|
|
278
|
+
|
|
279
|
+
/** Options accepted by {@link runPermissionSync}. */
|
|
280
|
+
interface PermissionSyncOptions {
|
|
281
|
+
/**
|
|
282
|
+
* Endpoints to scan for `meta.permission` declarations. Defaults to the
|
|
283
|
+
* full set registered on the Fortress instance when called via
|
|
284
|
+
* {@link import('../fortress').Fortress.syncPermissionsFromManifest}.
|
|
285
|
+
*/
|
|
286
|
+
endpoints?: EndpointDefinition[];
|
|
287
|
+
/**
|
|
288
|
+
* Optional role-name → permissions map. For each role:
|
|
289
|
+
*
|
|
290
|
+
* - `'*'` binds every permission discovered in `endpoints` to the role.
|
|
291
|
+
* - `['resource:action', ...]` binds only the named permissions (one
|
|
292
|
+
* string per `(resource, action)` pair, colon-separated).
|
|
293
|
+
*
|
|
294
|
+
* Roles are created on demand; existing roles get any missing
|
|
295
|
+
* permissions added. Permissions on a role that aren't requested here
|
|
296
|
+
* are left in place \u2014 this only grants, never revokes.
|
|
297
|
+
*
|
|
298
|
+
* ```ts
|
|
299
|
+
* await fortress.syncPermissionsFromManifest({\n * defaultRoles: {\n * admin: '*',\n * member: ['school:read', 'school:list'],\n * },\n * });\n * ```
|
|
300
|
+
*/
|
|
301
|
+
defaultRoles?: Record<string, '*' | readonly string[]>;
|
|
302
|
+
}
|
|
303
|
+
/** Result returned by {@link runPermissionSync}. */
|
|
304
|
+
interface PermissionSyncResult {
|
|
305
|
+
/** Number of unique `(resource, action)` pairs discovered. */
|
|
306
|
+
discovered: number;
|
|
307
|
+
/**
|
|
308
|
+
* Number of permissions that already existed in the database before
|
|
309
|
+
* the sync started. (The adapter's `findOrCreatePermission` doesn't
|
|
310
|
+
* distinguish, so this is computed by counting pre-existing
|
|
311
|
+
* permissions; new inserts = `discovered - existing`.)
|
|
312
|
+
*/
|
|
313
|
+
existing: number;
|
|
314
|
+
/** Number of new permission rows inserted by this sync run. */
|
|
315
|
+
created: number;
|
|
316
|
+
/**
|
|
317
|
+
* Roles touched by `defaultRoles`, keyed by role name. `bound` is the
|
|
318
|
+
* number of permission bindings newly added during this sync run.
|
|
319
|
+
* Already-bound permissions do not double-count.
|
|
320
|
+
*/
|
|
321
|
+
roles: Record<string, {
|
|
322
|
+
created: boolean;
|
|
323
|
+
bound: number;
|
|
324
|
+
}>;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Seed permissions from a set of endpoints and (optionally) bind them onto
|
|
328
|
+
* default roles. Safe to call repeatedly. See {@link PermissionSyncOptions}
|
|
329
|
+
* for shape.
|
|
330
|
+
*/
|
|
331
|
+
declare function runPermissionSync(iam: IamService, endpoints: EndpointDefinition[], opts?: PermissionSyncOptions): Promise<PermissionSyncResult>;
|
|
332
|
+
|
|
333
|
+
type RouteClassification = 'public' | 'authenticated' | 'rbac' | 'oauth-protocol' | 'default-deny';
|
|
334
|
+
interface RouteManifestEntry {
|
|
335
|
+
method: HttpMethod;
|
|
336
|
+
path: string;
|
|
337
|
+
handler: string;
|
|
338
|
+
plugin: string | null;
|
|
339
|
+
classification: RouteClassification;
|
|
340
|
+
permission?: EndpointPermission;
|
|
341
|
+
security: SecurityRequirement[];
|
|
342
|
+
bearerKind?: 'jwt' | 'oauth';
|
|
343
|
+
csrfApplicable: boolean;
|
|
344
|
+
rateLimited: boolean;
|
|
345
|
+
mounted: boolean;
|
|
346
|
+
}
|
|
347
|
+
declare function buildRouteManifest(fortress: Pick<Fortress, 'endpoints' | 'config'>): RouteManifestEntry[];
|
|
348
|
+
|
|
349
|
+
type MigrationDialect = 'sqlite' | 'pg';
|
|
350
|
+
interface FortressMigration {
|
|
351
|
+
version: number;
|
|
352
|
+
name: string;
|
|
353
|
+
dialect: MigrationDialect;
|
|
354
|
+
up: string;
|
|
355
|
+
down: string;
|
|
356
|
+
/** SQL used only when provisioning an empty database from concatenated DDL. */
|
|
357
|
+
freshUp?: string;
|
|
358
|
+
/** Stable identity included in the migration checksum for a JS data step. */
|
|
359
|
+
dataStep?: string;
|
|
360
|
+
/** Runs immediately before `up` while the migration lock/transaction is held. */
|
|
361
|
+
beforeUp?: (db: DatabaseAdapter) => Promise<void>;
|
|
362
|
+
}
|
|
363
|
+
declare const fortressMigrations: Record<MigrationDialect, FortressMigration[]>;
|
|
364
|
+
declare function getFortressMigrations(dialect: MigrationDialect): FortressMigration[];
|
|
365
|
+
declare function getLatestMigrationVersion(dialect: MigrationDialect): number;
|
|
366
|
+
/**
|
|
367
|
+
* Concatenated forward SQL for every bundled migration of a dialect, in
|
|
368
|
+
* version order. `src/testing/index.ts` provisions its in-memory schema
|
|
369
|
+
* from this so the test adapter and production migrations can never drift.
|
|
370
|
+
*/
|
|
371
|
+
declare function getMigrationUpSql(dialect: MigrationDialect): string;
|
|
372
|
+
/**
|
|
373
|
+
* Canonical list of every Fortress-owned table. Used by
|
|
374
|
+
* {@link detectMigrationDrift} to surface live-DB drift beyond just the
|
|
375
|
+
* `fortress_schema_version` checkpoint — any of these tables missing in
|
|
376
|
+
* the target database signals an incomplete or stale migration state.
|
|
377
|
+
*
|
|
378
|
+
* Keep in sync with `src/drizzle/{schema,pg/schema}.ts` and the bundled
|
|
379
|
+
* migrations. The list is asserted by `src/core/migrations/engine.test.ts`
|
|
380
|
+
* against the test adapter so an accidental drop/add is caught at test time.
|
|
381
|
+
*/
|
|
382
|
+
declare const FORTRESS_TABLES: readonly string[];
|
|
383
|
+
/**
|
|
384
|
+
* Parse the expected column set of every Fortress table directly out of the
|
|
385
|
+
* bundled migration DDL. Because the migrations are the SQL-first source of
|
|
386
|
+
* truth, this needs no schema description maintained on the side and no
|
|
387
|
+
* Drizzle-specific tooling — it works for any adapter. Powers the
|
|
388
|
+
* column-level half of {@link detectMigrationDrift}.
|
|
389
|
+
*
|
|
390
|
+
* The parser is intentionally narrow: it understands the controlled DDL
|
|
391
|
+
* Fortress emits (`CREATE TABLE [IF NOT EXISTS] name ( ... );` with
|
|
392
|
+
* comma-separated definitions, plus the controlled `ALTER TABLE` add-column
|
|
393
|
+
* and rename forms used by incremental migrations). Constraint-only lines
|
|
394
|
+
* (PRIMARY KEY (...), UNIQUE (...), etc.) and
|
|
395
|
+
* standalone CREATE INDEX statements are skipped.
|
|
396
|
+
*/
|
|
397
|
+
declare function getExpectedColumns(dialect: MigrationDialect): Record<string, string[]>;
|
|
398
|
+
|
|
399
|
+
interface MigrationStatus {
|
|
400
|
+
dialect: MigrationDialect;
|
|
401
|
+
currentVersion: number;
|
|
402
|
+
latestVersion: number;
|
|
403
|
+
pending: FortressMigration[];
|
|
404
|
+
applied: FortressMigration[];
|
|
405
|
+
upToDate: boolean;
|
|
406
|
+
hasVersionTable: boolean;
|
|
407
|
+
}
|
|
408
|
+
interface MigrationApplyResult {
|
|
409
|
+
dialect: MigrationDialect;
|
|
410
|
+
fromVersion: number;
|
|
411
|
+
toVersion: number;
|
|
412
|
+
applied: FortressMigration[];
|
|
413
|
+
}
|
|
414
|
+
interface MigrationDownResult {
|
|
415
|
+
dialect: MigrationDialect;
|
|
416
|
+
fromVersion: number;
|
|
417
|
+
toVersion: number;
|
|
418
|
+
rolledBack: FortressMigration[];
|
|
419
|
+
}
|
|
420
|
+
interface MigrationDrift {
|
|
421
|
+
dialect: MigrationDialect;
|
|
422
|
+
currentVersion: number;
|
|
423
|
+
latestVersion: number;
|
|
424
|
+
missingVersionTable: boolean;
|
|
425
|
+
unknownFutureVersion: boolean;
|
|
426
|
+
pendingVersions: number[];
|
|
427
|
+
/**
|
|
428
|
+
* Fortress-owned tables expected by the runtime but missing from the
|
|
429
|
+
* live database. A non-empty list almost always means an upgrade was
|
|
430
|
+
* skipped or the database was provisioned from an out-of-date schema
|
|
431
|
+
* dump. Compared against {@link FORTRESS_TABLES}.
|
|
432
|
+
*/
|
|
433
|
+
missingTables: string[];
|
|
434
|
+
/**
|
|
435
|
+
* Per-table columns that the bundled migration DDL defines but the live
|
|
436
|
+
* database is missing. Surfaces a partially-applied or hand-patched
|
|
437
|
+
* schema where the table exists but is missing a column added by a later
|
|
438
|
+
* migration. Expected columns are parsed from the migration SQL itself
|
|
439
|
+
* (see `getExpectedColumns`), so this works for any adapter. Only tables
|
|
440
|
+
* that are present in the live DB are inspected — fully missing tables are
|
|
441
|
+
* reported via {@link missingTables} instead.
|
|
442
|
+
*/
|
|
443
|
+
missingColumns: {
|
|
444
|
+
table: string;
|
|
445
|
+
columns: string[];
|
|
446
|
+
}[];
|
|
447
|
+
/** Required hot-path indexes absent from the live database. */
|
|
448
|
+
missingIndexes: string[];
|
|
449
|
+
}
|
|
450
|
+
declare function getMigrationStatus(db: DatabaseAdapter, dialect?: MigrationDialect): Promise<MigrationStatus>;
|
|
451
|
+
declare function migrateUp(db: DatabaseAdapter, dialect?: MigrationDialect, targetVersion?: number): Promise<MigrationApplyResult>;
|
|
452
|
+
declare function migrateDown(db: DatabaseAdapter, dialect?: MigrationDialect, targetVersion?: number): Promise<MigrationDownResult>;
|
|
453
|
+
declare function detectMigrationDrift(db: DatabaseAdapter, dialect?: MigrationDialect): Promise<MigrationDrift>;
|
|
454
|
+
declare function hasMigrationDrift(drift: MigrationDrift): boolean;
|
|
455
|
+
|
|
456
|
+
/** Options accepted by {@link toOpenAPI}. */
|
|
457
|
+
interface ToOpenAPIOptions extends Partial<SpecBuilderOptions> {
|
|
458
|
+
/** Additional reusable schemas to place under `components.schemas`. */
|
|
459
|
+
schemas?: ComponentSchemas;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Emit an OpenAPI 3.1 spec from a set of Fortress endpoint definitions.
|
|
463
|
+
*
|
|
464
|
+
* This is the env/DB-free helper for build scripts and codegen pipelines.
|
|
465
|
+
* If you already have a configured Fortress instance, prefer
|
|
466
|
+
* `fortress.toOpenAPI()` so the endpoint list defaults to everything that
|
|
467
|
+
* instance knows about.
|
|
468
|
+
*
|
|
469
|
+
* ```ts
|
|
470
|
+
* import { toOpenAPI } from '@bajustone/fortress';
|
|
471
|
+
* import { appEndpointList } from './routes/v1/endpoints';
|
|
472
|
+
*
|
|
473
|
+
* export const OPENAPI_SPEC = toOpenAPI(appEndpointList, {
|
|
474
|
+
* title: 'My API',
|
|
475
|
+
* version: '0.0.0',
|
|
476
|
+
* servers: [{ url: 'http://localhost:3001' }],
|
|
477
|
+
* tags: [{ name: 'Schools' }],
|
|
478
|
+
* });
|
|
479
|
+
* ```
|
|
480
|
+
*
|
|
481
|
+
* Defaults to endpoint `handler` values for `operationId`, matching host
|
|
482
|
+
* route-contract names. Pass `operationId: 'methodPath'` for Fortress's
|
|
483
|
+
* historical generated IDs.
|
|
484
|
+
*/
|
|
485
|
+
declare function toOpenAPI(endpoints: readonly EndpointDefinition[], opts?: ToOpenAPIOptions): OpenAPISpec;
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Typed in-process client for fortress endpoints.
|
|
489
|
+
*
|
|
490
|
+
* `buildCall` takes a flat map of `EndpointDefinition`s (keyed by handler
|
|
491
|
+
* name) and returns an object with one callable per entry. Each callable:
|
|
492
|
+
*
|
|
493
|
+
* 1. Splits the flat input object into `body`, `query`, and `params` based
|
|
494
|
+
* on the endpoint's declared schemas.
|
|
495
|
+
* 2. Substitutes `:param` placeholders in the path.
|
|
496
|
+
* 3. Serializes the query string.
|
|
497
|
+
* 4. Builds a web-standard `Request` with a JSON body (when applicable).
|
|
498
|
+
* 5. Delegates to `fortress.handleRequest`, exercising the full pipeline
|
|
499
|
+
* (plugin middleware → token verification → RBAC → validation →
|
|
500
|
+
* dispatch → cookie attachment).
|
|
501
|
+
* 6. Parses the JSON response body. On non-2xx, throws a structured
|
|
502
|
+
* `FortressError` reconstructed via `Errors.fromHttpResponse`.
|
|
503
|
+
*
|
|
504
|
+
* This is the foundation of the typed client SDK: the same code works
|
|
505
|
+
* in-process today and will work over the network tomorrow by swapping
|
|
506
|
+
* `handleRequest` for `fetch`. All of the type machinery lives on the
|
|
507
|
+
* `EndpointDefinition` generic parameters and the `InferEndpoint*` helpers
|
|
508
|
+
* in `src/core/endpoint.ts`.
|
|
509
|
+
*
|
|
510
|
+
* **Authentication.** Protected endpoints expect a bearer token. Callers
|
|
511
|
+
* pass it via the optional second argument: `call.me({}, { headers:
|
|
512
|
+
* { authorization: 'Bearer …' } })`. The in-process client does not share
|
|
513
|
+
* state with any active session; it is purely a typed wire formatter.
|
|
514
|
+
*
|
|
515
|
+
* **Runs the full pipeline.** Because each call lands in
|
|
516
|
+
* `fortress.handleRequest`, the following all fire: plugin middleware (incl.
|
|
517
|
+
* rate limits), principal resolution, RBAC, JSON Schema / Standard Schema
|
|
518
|
+
* validation, auth observers, IAM observers, OpenTelemetry spans, and any
|
|
519
|
+
* `wrapAdapter` hooks. This is intentional — it's the same code path a
|
|
520
|
+
* network client would hit, so tests that exercise `fortress.call.*` give
|
|
521
|
+
* the same behavioral guarantees as production traffic.
|
|
522
|
+
*
|
|
523
|
+
* **Bypassing hooks in tests.** If a test fixture needs to create users or
|
|
524
|
+
* log in *without* tripping rate limits, emitting audit entries, or running
|
|
525
|
+
* observers, call the service layer directly — `fortress.auth.createUser`,
|
|
526
|
+
* `fortress.auth.login`, `fortress.iam.createRole` — instead of the typed
|
|
527
|
+
* call surface. The service layer still validates inputs but skips the
|
|
528
|
+
* middleware chain.
|
|
529
|
+
*/
|
|
530
|
+
|
|
531
|
+
/** Optional per-call options. */
|
|
532
|
+
interface CallOptions {
|
|
533
|
+
/** Extra headers to attach to the synthesized request (e.g. bearer auth). */
|
|
534
|
+
headers?: Record<string, string>;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Build an object of typed callables from a keyed {@link EndpointDefinition}
|
|
538
|
+
* map. Each callable invokes `fortress.handleRequest` with a synthesized
|
|
539
|
+
* `Request` and returns the parsed JSON body on success.
|
|
540
|
+
*
|
|
541
|
+
* The returned type is intentionally loose (`Record<string, ...>`). The
|
|
542
|
+
* strong typing happens at the call site via the `TypedCall` helper in
|
|
543
|
+
* `src/core/fortress.ts`, which layers a mapped type over the runtime
|
|
544
|
+
* callable map to expose per-handler inferred I/O.
|
|
545
|
+
*/
|
|
546
|
+
declare function buildCall(fortress: Fortress, endpoints: Record<string, EndpointDefinition>): Record<string, (input?: Record<string, unknown>, options?: CallOptions) => Promise<unknown>>;
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Type-level map of every built-in plugin name to its method-surface
|
|
550
|
+
* interface. {@link InferPlugins} uses this to expose typed plugin methods
|
|
551
|
+
* on the fortress instance.
|
|
552
|
+
*/
|
|
553
|
+
interface PluginMethodsMap {
|
|
554
|
+
'api-key': ApiKeyMethods;
|
|
555
|
+
'audit-log': AuditLogMethods;
|
|
556
|
+
'data-isolation': DataIsolationMethods;
|
|
557
|
+
'email-verification': EmailVerificationMethods;
|
|
558
|
+
'magic-link': MagicLinkMethods;
|
|
559
|
+
'oauth': OAuthMethods;
|
|
560
|
+
'openapi': OpenAPIMethods;
|
|
561
|
+
'social-login': SocialLoginMethods;
|
|
562
|
+
'tenancy': TenancyMethods;
|
|
563
|
+
'two-factor': TwoFactorMethods;
|
|
564
|
+
'webauthn': WebAuthnMethods;
|
|
565
|
+
}
|
|
566
|
+
/** Infer the typed plugin-methods record from a `plugins` tuple passed to {@link createFortress}. */
|
|
567
|
+
type InferPlugins<T extends readonly FortressPlugin[]> = {
|
|
568
|
+
[P in T[number] as P['name']]: P['name'] extends keyof PluginMethodsMap ? PluginMethodsMap[P['name']] : Record<string, (...args: any[]) => any>;
|
|
569
|
+
};
|
|
570
|
+
/** Distributes a union into an intersection — the classic TS trick. Used by {@link InferPluginCallMap}. */
|
|
571
|
+
type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends ((x: infer I) => void) ? I : never;
|
|
572
|
+
/**
|
|
573
|
+
* Turn a record of {@link EndpointDefinition}s into a record of typed
|
|
574
|
+
* callables: each key becomes a function whose input is the endpoint's
|
|
575
|
+
* inferred body+query+params intersection and whose output is the inferred
|
|
576
|
+
* success-response body.
|
|
577
|
+
*/
|
|
578
|
+
type CallableForEndpoints<E> = E extends Record<string, EndpointDefinition<any, any, any, any>> ? {
|
|
579
|
+
[K in keyof E]: (input: InferEndpointCallInput<E[K]>, options?: CallOptions) => Promise<InferEndpointSuccessResponse<E[K]>>;
|
|
580
|
+
} : Record<string, never>;
|
|
581
|
+
/**
|
|
582
|
+
* Infer the typed call surface contributed by a plugins tuple. Walks every
|
|
583
|
+
* plugin's `routes` record (if present) and unions the callables into one
|
|
584
|
+
* flat object keyed by handler name.
|
|
585
|
+
*/
|
|
586
|
+
type InferPluginCallMap<T extends readonly FortressPlugin[]> = UnionToIntersection<T[number] extends infer P ? P extends {
|
|
587
|
+
routes?: infer R;
|
|
588
|
+
} ? R extends Record<string, EndpointDefinition<any, any, any, any>> ? CallableForEndpoints<R> : Record<string, never> : Record<string, never> : Record<string, never>>;
|
|
589
|
+
|
|
590
|
+
interface EmptyInput {
|
|
591
|
+
}
|
|
592
|
+
/** Wire shape of a persisted role. `description` and `isSystem` are optional nullable on the wire. */
|
|
593
|
+
interface RoleWire {
|
|
594
|
+
id: string;
|
|
595
|
+
name: string;
|
|
596
|
+
description?: string | null;
|
|
597
|
+
isSystem?: boolean;
|
|
598
|
+
}
|
|
599
|
+
/** Wire shape of a persisted group. */
|
|
600
|
+
interface GroupWire {
|
|
601
|
+
id: string;
|
|
602
|
+
name: string;
|
|
603
|
+
description?: string | null;
|
|
604
|
+
}
|
|
605
|
+
/** Wire shape of a permission condition. `operator` is restricted to the supported set. */
|
|
606
|
+
interface PermissionConditionWire {
|
|
607
|
+
field: string;
|
|
608
|
+
operator: 'eq' | 'neq' | 'in' | 'startsWith';
|
|
609
|
+
value: string;
|
|
610
|
+
}
|
|
611
|
+
/** Wire shape of a persisted permission. */
|
|
612
|
+
interface PermissionWire {
|
|
613
|
+
id: string;
|
|
614
|
+
resource: string;
|
|
615
|
+
action: string;
|
|
616
|
+
effect: 'ALLOW' | 'DENY';
|
|
617
|
+
conditions?: PermissionConditionWire[];
|
|
618
|
+
description?: string | null;
|
|
619
|
+
}
|
|
620
|
+
/** Wire shape of a permission-creation input. */
|
|
621
|
+
interface PermissionInputWire {
|
|
622
|
+
resource: string;
|
|
623
|
+
action: string;
|
|
624
|
+
effect?: 'ALLOW' | 'DENY';
|
|
625
|
+
conditions?: PermissionConditionWire[];
|
|
626
|
+
}
|
|
627
|
+
/** Wire shape of a persisted service account. Date fields are ISO strings on the wire. */
|
|
628
|
+
interface ServiceAccountWire {
|
|
629
|
+
id: string;
|
|
630
|
+
name: string;
|
|
631
|
+
displayName?: string | null;
|
|
632
|
+
description?: string | null;
|
|
633
|
+
isActive: boolean;
|
|
634
|
+
createdAt?: string;
|
|
635
|
+
updatedAt?: string;
|
|
636
|
+
}
|
|
637
|
+
/** Explicit registry type so JSR's fast-check doesn't walk into the deeply-inferred `defineComponents` return. */
|
|
638
|
+
interface IamComponents {
|
|
639
|
+
readonly components: {
|
|
640
|
+
readonly Role: FortressSchema<RoleWire>;
|
|
641
|
+
readonly Group: FortressSchema<GroupWire>;
|
|
642
|
+
readonly Permission: FortressSchema<PermissionWire>;
|
|
643
|
+
readonly PermissionInput: FortressSchema<PermissionInputWire>;
|
|
644
|
+
readonly ServiceAccount: FortressSchema<ServiceAccountWire>;
|
|
645
|
+
};
|
|
646
|
+
readonly ref: <K extends keyof IamComponents['components']>(name: K) => FortressSchema<IamComponents['components'][K] extends FortressSchema<infer U> ? U : never>;
|
|
647
|
+
}
|
|
648
|
+
/** Reusable OpenAPI component schemas referenced by the core IAM endpoints. */
|
|
649
|
+
declare const iamComponentSchemas: IamComponents['components'];
|
|
650
|
+
/** Resource catalog response shape. */
|
|
651
|
+
interface ResourceCatalogWire {
|
|
652
|
+
resources: Record<string, {
|
|
653
|
+
actions: string[];
|
|
654
|
+
description?: string;
|
|
655
|
+
}>;
|
|
656
|
+
}
|
|
657
|
+
/** Paged service-account list response shape. */
|
|
658
|
+
interface ServiceAccountsListWire {
|
|
659
|
+
serviceAccounts: ServiceAccountWire[];
|
|
660
|
+
total: number;
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Typed record of every core IAM endpoint. Declared explicitly so JSR's
|
|
664
|
+
* fast-check passes without `--allow-slow-types`, while
|
|
665
|
+
* `InferEndpointCallInput<typeof iamEndpoints.X>` still resolves per-handler.
|
|
666
|
+
*/
|
|
667
|
+
interface IamEndpointsMap {
|
|
668
|
+
getResources: EndpointDefinition<EmptyInput, EmptyInput, EmptyInput, {
|
|
669
|
+
200: ResourceCatalogWire;
|
|
670
|
+
401: ErrorResponseWire;
|
|
671
|
+
}>;
|
|
672
|
+
getRoles: EndpointDefinition<EmptyInput, EmptyInput, EmptyInput, {
|
|
673
|
+
200: RoleWire[];
|
|
674
|
+
401: ErrorResponseWire;
|
|
675
|
+
}>;
|
|
676
|
+
createRole: EndpointDefinition<{
|
|
677
|
+
name: string;
|
|
678
|
+
permissions: PermissionInputWire[];
|
|
679
|
+
description?: string;
|
|
680
|
+
}, EmptyInput, EmptyInput, {
|
|
681
|
+
201: RoleWire;
|
|
682
|
+
401: ErrorResponseWire;
|
|
683
|
+
}>;
|
|
684
|
+
deleteRole: EndpointDefinition<EmptyInput, EmptyInput, {
|
|
685
|
+
id: string;
|
|
686
|
+
}, {
|
|
687
|
+
200: OkResponseWire;
|
|
688
|
+
400: ErrorResponseWire;
|
|
689
|
+
401: ErrorResponseWire;
|
|
690
|
+
}>;
|
|
691
|
+
bindRoleToUser: EndpointDefinition<{
|
|
692
|
+
userId: string;
|
|
693
|
+
tenantId?: string;
|
|
694
|
+
}, EmptyInput, {
|
|
695
|
+
id: string;
|
|
696
|
+
}, {
|
|
697
|
+
200: OkResponseWire;
|
|
698
|
+
401: ErrorResponseWire;
|
|
699
|
+
}>;
|
|
700
|
+
bindRoleToGroup: EndpointDefinition<{
|
|
701
|
+
groupId: string;
|
|
702
|
+
tenantId?: string;
|
|
703
|
+
}, EmptyInput, {
|
|
704
|
+
id: string;
|
|
705
|
+
}, {
|
|
706
|
+
200: OkResponseWire;
|
|
707
|
+
401: ErrorResponseWire;
|
|
708
|
+
}>;
|
|
709
|
+
unbindRole: EndpointDefinition<{
|
|
710
|
+
subjectType: 'USER' | 'GROUP' | 'SERVICE_ACCOUNT';
|
|
711
|
+
subjectId: string;
|
|
712
|
+
tenantId?: string;
|
|
713
|
+
}, EmptyInput, {
|
|
714
|
+
id: string;
|
|
715
|
+
}, {
|
|
716
|
+
200: OkResponseWire;
|
|
717
|
+
401: ErrorResponseWire;
|
|
718
|
+
}>;
|
|
719
|
+
createGroup: EndpointDefinition<{
|
|
720
|
+
name: string;
|
|
721
|
+
description?: string;
|
|
722
|
+
}, EmptyInput, EmptyInput, {
|
|
723
|
+
201: GroupWire;
|
|
724
|
+
401: ErrorResponseWire;
|
|
725
|
+
}>;
|
|
726
|
+
addUserToGroup: EndpointDefinition<{
|
|
727
|
+
userId: string;
|
|
728
|
+
}, EmptyInput, {
|
|
729
|
+
id: string;
|
|
730
|
+
}, {
|
|
731
|
+
200: OkResponseWire;
|
|
732
|
+
401: ErrorResponseWire;
|
|
733
|
+
}>;
|
|
734
|
+
removeUserFromGroup: EndpointDefinition<EmptyInput, EmptyInput, {
|
|
735
|
+
id: string;
|
|
736
|
+
userId: string;
|
|
737
|
+
}, {
|
|
738
|
+
200: OkResponseWire;
|
|
739
|
+
401: ErrorResponseWire;
|
|
740
|
+
}>;
|
|
741
|
+
getUserPermissions: EndpointDefinition<EmptyInput, {
|
|
742
|
+
tenantId?: string;
|
|
743
|
+
}, {
|
|
744
|
+
id: string;
|
|
745
|
+
}, {
|
|
746
|
+
200: PermissionWire[];
|
|
747
|
+
401: ErrorResponseWire;
|
|
748
|
+
}>;
|
|
749
|
+
checkPermission: EndpointDefinition<{
|
|
750
|
+
userId: string;
|
|
751
|
+
resource: string;
|
|
752
|
+
action: string;
|
|
753
|
+
context?: Record<string, unknown>;
|
|
754
|
+
}, EmptyInput, EmptyInput, {
|
|
755
|
+
200: {
|
|
756
|
+
allowed: boolean;
|
|
757
|
+
};
|
|
758
|
+
401: ErrorResponseWire;
|
|
759
|
+
}>;
|
|
760
|
+
bindPermissionToUser: EndpointDefinition<{
|
|
761
|
+
userId: string;
|
|
762
|
+
permission: PermissionInputWire;
|
|
763
|
+
tenantId?: string;
|
|
764
|
+
}, EmptyInput, EmptyInput, {
|
|
765
|
+
200: OkResponseWire;
|
|
766
|
+
401: ErrorResponseWire;
|
|
767
|
+
}>;
|
|
768
|
+
bindPermissionToGroup: EndpointDefinition<{
|
|
769
|
+
groupId: string;
|
|
770
|
+
permission: PermissionInputWire;
|
|
771
|
+
tenantId?: string;
|
|
772
|
+
}, EmptyInput, EmptyInput, {
|
|
773
|
+
200: OkResponseWire;
|
|
774
|
+
401: ErrorResponseWire;
|
|
775
|
+
}>;
|
|
776
|
+
unbindPermissionFromUser: EndpointDefinition<{
|
|
777
|
+
userId: string;
|
|
778
|
+
permissionId: string;
|
|
779
|
+
tenantId?: string;
|
|
780
|
+
}, EmptyInput, EmptyInput, {
|
|
781
|
+
200: OkResponseWire;
|
|
782
|
+
401: ErrorResponseWire;
|
|
783
|
+
}>;
|
|
784
|
+
unbindPermissionFromGroup: EndpointDefinition<{
|
|
785
|
+
groupId: string;
|
|
786
|
+
permissionId: string;
|
|
787
|
+
tenantId?: string;
|
|
788
|
+
}, EmptyInput, EmptyInput, {
|
|
789
|
+
200: OkResponseWire;
|
|
790
|
+
401: ErrorResponseWire;
|
|
791
|
+
}>;
|
|
792
|
+
createServiceAccount: EndpointDefinition<{
|
|
793
|
+
name: string;
|
|
794
|
+
displayName?: string;
|
|
795
|
+
description?: string;
|
|
796
|
+
}, EmptyInput, EmptyInput, {
|
|
797
|
+
201: ServiceAccountWire;
|
|
798
|
+
400: ErrorResponseWire;
|
|
799
|
+
401: ErrorResponseWire;
|
|
800
|
+
}>;
|
|
801
|
+
listServiceAccounts: EndpointDefinition<EmptyInput, {
|
|
802
|
+
limit?: number;
|
|
803
|
+
offset?: number;
|
|
804
|
+
}, EmptyInput, {
|
|
805
|
+
200: ServiceAccountsListWire;
|
|
806
|
+
401: ErrorResponseWire;
|
|
807
|
+
}>;
|
|
808
|
+
getServiceAccount: EndpointDefinition<EmptyInput, EmptyInput, {
|
|
809
|
+
id: string;
|
|
810
|
+
}, {
|
|
811
|
+
200: ServiceAccountWire;
|
|
812
|
+
401: ErrorResponseWire;
|
|
813
|
+
404: ErrorResponseWire;
|
|
814
|
+
}>;
|
|
815
|
+
updateServiceAccount: EndpointDefinition<{
|
|
816
|
+
displayName?: string | null;
|
|
817
|
+
description?: string | null;
|
|
818
|
+
isActive?: boolean;
|
|
819
|
+
}, EmptyInput, {
|
|
820
|
+
id: string;
|
|
821
|
+
}, {
|
|
822
|
+
200: ServiceAccountWire;
|
|
823
|
+
401: ErrorResponseWire;
|
|
824
|
+
404: ErrorResponseWire;
|
|
825
|
+
}>;
|
|
826
|
+
deleteServiceAccount: EndpointDefinition<EmptyInput, EmptyInput, {
|
|
827
|
+
id: string;
|
|
828
|
+
}, {
|
|
829
|
+
200: OkResponseWire;
|
|
830
|
+
401: ErrorResponseWire;
|
|
831
|
+
404: ErrorResponseWire;
|
|
832
|
+
}>;
|
|
833
|
+
getServiceAccountPermissions: EndpointDefinition<EmptyInput, {
|
|
834
|
+
tenantId?: string;
|
|
835
|
+
}, {
|
|
836
|
+
id: string;
|
|
837
|
+
}, {
|
|
838
|
+
200: PermissionWire[];
|
|
839
|
+
401: ErrorResponseWire;
|
|
840
|
+
}>;
|
|
841
|
+
bindRoleToServiceAccount: EndpointDefinition<{
|
|
842
|
+
serviceAccountId: string;
|
|
843
|
+
tenantId?: string;
|
|
844
|
+
}, EmptyInput, {
|
|
845
|
+
id: string;
|
|
846
|
+
}, {
|
|
847
|
+
200: OkResponseWire;
|
|
848
|
+
401: ErrorResponseWire;
|
|
849
|
+
}>;
|
|
850
|
+
unbindRoleFromServiceAccount: EndpointDefinition<{
|
|
851
|
+
serviceAccountId: string;
|
|
852
|
+
tenantId?: string;
|
|
853
|
+
}, EmptyInput, {
|
|
854
|
+
id: string;
|
|
855
|
+
}, {
|
|
856
|
+
200: OkResponseWire;
|
|
857
|
+
401: ErrorResponseWire;
|
|
858
|
+
}>;
|
|
859
|
+
bindPermissionToServiceAccount: EndpointDefinition<{
|
|
860
|
+
serviceAccountId: string;
|
|
861
|
+
permission: PermissionInputWire;
|
|
862
|
+
tenantId?: string;
|
|
863
|
+
}, EmptyInput, EmptyInput, {
|
|
864
|
+
200: OkResponseWire;
|
|
865
|
+
401: ErrorResponseWire;
|
|
866
|
+
}>;
|
|
867
|
+
unbindPermissionFromServiceAccount: EndpointDefinition<{
|
|
868
|
+
serviceAccountId: string;
|
|
869
|
+
permissionId: string;
|
|
870
|
+
tenantId?: string;
|
|
871
|
+
}, EmptyInput, EmptyInput, {
|
|
872
|
+
200: OkResponseWire;
|
|
873
|
+
401: ErrorResponseWire;
|
|
874
|
+
}>;
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Declarative endpoint definitions for fortress's built-in IAM admin routes.
|
|
878
|
+
* The explicit `IamEndpointsMap` annotation keeps JSR fast-check happy
|
|
879
|
+
* without sacrificing per-handler inference for `fortress.call.*`.
|
|
880
|
+
*/
|
|
881
|
+
declare const iamEndpoints: IamEndpointsMap;
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Typed in-process call surface. Composes auth + IAM endpoint callables
|
|
885
|
+
* with any plugin-contributed routes (derived from the plugins tuple).
|
|
886
|
+
*
|
|
887
|
+
* Each handler becomes a `(input, options?) => Promise<successBody>` where
|
|
888
|
+
* the input shape is the inferred intersection of the endpoint's body,
|
|
889
|
+
* query, and params schemas, and the output is the inferred 2xx response
|
|
890
|
+
* type. Non-2xx responses throw a `FortressError`.
|
|
891
|
+
*/
|
|
892
|
+
type TypedCall<T extends readonly FortressPlugin[]> = CallableForEndpoints<typeof authEndpoints> & CallableForEndpoints<typeof iamEndpoints> & InferPluginCallMap<T>;
|
|
893
|
+
/**
|
|
894
|
+
* Configured fortress instance returned by {@link createFortress}. Holds the
|
|
895
|
+
* core auth and IAM services, the resolved config, every endpoint definition
|
|
896
|
+
* (auth + IAM + plugin routes), and the typed plugin method surface.
|
|
897
|
+
*
|
|
898
|
+
* Also exposes the framework-agnostic HTTP entry points
|
|
899
|
+
* ({@link Fortress.handleRequest}, {@link Fortress.runPluginMiddleware},
|
|
900
|
+
* {@link Fortress.extractAccessToken}, {@link Fortress.serializeAuthCookies})
|
|
901
|
+
* that adapters delegate to. See `src/core/http/`.
|
|
902
|
+
*/
|
|
903
|
+
interface Fortress<TPlugins = Record<string, Record<string, Function>>, TCall = Record<string, (input?: any, options?: any) => Promise<any>>> {
|
|
904
|
+
auth: AuthService;
|
|
905
|
+
iam: IamService;
|
|
906
|
+
plugins: TPlugins;
|
|
907
|
+
/**
|
|
908
|
+
* Typed in-process client. Each key is a handler name (from the core
|
|
909
|
+
* auth/IAM endpoint records or a plugin's `routes` record); each value is
|
|
910
|
+
* an async function whose input/output types are inferred from the
|
|
911
|
+
* endpoint's declared body/query/params/response schemas.
|
|
912
|
+
*
|
|
913
|
+
* Under the hood, each callable serializes its input to a `Request` and
|
|
914
|
+
* delegates to `fortress.handleRequest`, so middleware, token
|
|
915
|
+
* verification, RBAC, and validation all run — the same path a network
|
|
916
|
+
* client would eventually hit. See `src/core/http/call.ts`.
|
|
917
|
+
*/
|
|
918
|
+
call: TCall;
|
|
919
|
+
config: Readonly<FortressConfig>;
|
|
920
|
+
/** All endpoint definitions (auth + IAM + plugins) with JSON Schema metadata. */
|
|
921
|
+
endpoints: EndpointDefinition[];
|
|
922
|
+
/** Canonical generated route-security manifest derived from endpoint metadata. */
|
|
923
|
+
manifest: RouteManifestEntry[];
|
|
924
|
+
/** Resolved auth-cookie names and attributes (NODE_ENV-aware). */
|
|
925
|
+
cookies: ResolvedCookieConfig;
|
|
926
|
+
/** Resolved logger (defaults to a silent no-op when `config.logger` is unset). */
|
|
927
|
+
logger: FortressLogger;
|
|
928
|
+
/** Resolved telemetry provider (defaults to a no-op when `config.observability` is unset). */
|
|
929
|
+
telemetry: TelemetryProvider;
|
|
930
|
+
/**
|
|
931
|
+
* Handle a Fortress-managed request and return a web-standard `Response`.
|
|
932
|
+
* Composes plugin middleware, token verification, default-deny RBAC,
|
|
933
|
+
* validation, and dispatch.
|
|
934
|
+
*
|
|
935
|
+
* Adapters call this for paths owned by Fortress (e.g. `/auth/*`,
|
|
936
|
+
* `/iam/*`, plugin-registered routes). See `src/core/http/handle-request.ts`.
|
|
937
|
+
*/
|
|
938
|
+
handleRequest: (request: Request) => Promise<Response>;
|
|
939
|
+
/**
|
|
940
|
+
* Run plugin middleware at a given lifecycle phase, passing a duck-typed
|
|
941
|
+
* request context. Adapters call this on user-owned routes so plugins
|
|
942
|
+
* like rate-limit and audit-log still apply outside Fortress paths.
|
|
943
|
+
*/
|
|
944
|
+
runPluginMiddleware: (phase: MiddlewareDefinition['position'], ctx: PluginRequestContext) => Promise<void>;
|
|
945
|
+
/**
|
|
946
|
+
* Extract the access token from a request, preferring the configured
|
|
947
|
+
* cookie and falling back to `Authorization: Bearer`.
|
|
948
|
+
*/
|
|
949
|
+
extractAccessToken: (request: Request) => string | null;
|
|
950
|
+
/**
|
|
951
|
+
* Resolve the request principal by trying plugin `resolvePrincipal`
|
|
952
|
+
* hooks (e.g. `api-key`) first, then falling back to the configured JWT
|
|
953
|
+
* bearer token. Non-throwing — returns `null` when no credential is
|
|
954
|
+
* present or the JWT fails to verify.
|
|
955
|
+
*
|
|
956
|
+
* Adapter user-route middleware calls this so api-keys, cookies, and
|
|
957
|
+
* bearer tokens authenticate uniformly on user-owned routes — not just
|
|
958
|
+
* Fortress-owned routes dispatched through `handleRequest`.
|
|
959
|
+
*/
|
|
960
|
+
resolvePrincipal: (request: Request) => Promise<ResolvedPrincipal | null>;
|
|
961
|
+
/**
|
|
962
|
+
* Serialize an auth result (or pair of tokens) into one or two
|
|
963
|
+
* `Set-Cookie` header values using the resolved cookie config and the
|
|
964
|
+
* configured token expiries.
|
|
965
|
+
*/
|
|
966
|
+
serializeAuthCookies: (payload: AuthCookiePayload) => string[];
|
|
967
|
+
/**
|
|
968
|
+
* Run Fortress's pending schema migrations against the configured database
|
|
969
|
+
* and, optionally, an application-supplied migration step afterwards.
|
|
970
|
+
*
|
|
971
|
+
* Fortress migrations always run first because app schemas commonly
|
|
972
|
+
* reference fortress tables (foreign keys to `user.id`, etc.). The
|
|
973
|
+
* `migrateApp` callback (when provided) is awaited after Fortress
|
|
974
|
+
* migrations complete, so a host can do something like:
|
|
975
|
+
*
|
|
976
|
+
* ```ts
|
|
977
|
+
* await fortress.migrate({
|
|
978
|
+
* migrateApp: () => migrate(db, { migrationsFolder: './drizzle' }),
|
|
979
|
+
* });
|
|
980
|
+
* ```
|
|
981
|
+
*
|
|
982
|
+
* Eliminates the two-step "forget to call `migrateUp` and silently
|
|
983
|
+
* have no auth tables" footgun.
|
|
984
|
+
*/
|
|
985
|
+
migrate: (opts?: MigrateOptions) => Promise<MigrateResult>;
|
|
986
|
+
/**
|
|
987
|
+
* Seed permissions discovered on registered endpoints (`fortress.endpoints`)
|
|
988
|
+
* into the IAM database and, optionally, bind them onto a set of default
|
|
989
|
+
* roles. Idempotent — re-running adds only what's missing and never
|
|
990
|
+
* revokes anything that was already there.
|
|
991
|
+
*
|
|
992
|
+
* Typical bootstrap sequence:
|
|
993
|
+
*
|
|
994
|
+
* ```ts
|
|
995
|
+
* await fortress.migrate({ migrateApp: () => migrate(db, '...') });
|
|
996
|
+
* await fortress.syncPermissionsFromManifest({
|
|
997
|
+
* defaultRoles: { admin: '*', member: ['school:read', 'school:list'] },
|
|
998
|
+
* });
|
|
999
|
+
* ```
|
|
1000
|
+
*
|
|
1001
|
+
* Replaces the seed script every consumer used to write that walked
|
|
1002
|
+
* `fortress.endpoints`, deduped `(resource, action)` pairs, and called
|
|
1003
|
+
* `iam.createPermission(...)` for each.
|
|
1004
|
+
*/
|
|
1005
|
+
syncPermissionsFromManifest: (opts?: PermissionSyncOptions) => Promise<PermissionSyncResult>;
|
|
1006
|
+
/**
|
|
1007
|
+
* Emit a complete OpenAPI 3.1 spec from the endpoint definitions Fortress
|
|
1008
|
+
* knows about: core auth/IAM routes, plugin routes, and any top-level
|
|
1009
|
+
* host `routes` registered on {@link FortressConfig}.
|
|
1010
|
+
*
|
|
1011
|
+
* This is the programmatic companion to the OpenAPI plugin. Use it when
|
|
1012
|
+
* you already own the docs route or when a build script needs to write
|
|
1013
|
+
* `openapi.json` for client codegen:
|
|
1014
|
+
*
|
|
1015
|
+
* ```ts
|
|
1016
|
+
* const spec = fortress.toOpenAPI({
|
|
1017
|
+
* title: 'My API',
|
|
1018
|
+
* version: '0.0.0',
|
|
1019
|
+
* servers: [{ url: 'http://localhost:3001', description: 'Local' }],
|
|
1020
|
+
* tags: [{ name: 'Schools' }],
|
|
1021
|
+
* });
|
|
1022
|
+
* ```
|
|
1023
|
+
*
|
|
1024
|
+
* Defaults to endpoint `handler` names for `operationId`, matching most
|
|
1025
|
+
* host route-contract files. Pass `operationId: 'methodPath'` to keep the
|
|
1026
|
+
* historical OpenAPI-plugin ID style.
|
|
1027
|
+
*/
|
|
1028
|
+
toOpenAPI: (opts?: FortressToOpenAPIOptions) => OpenAPISpec;
|
|
1029
|
+
}
|
|
1030
|
+
/** Options accepted by {@link Fortress.toOpenAPI}. */
|
|
1031
|
+
interface FortressToOpenAPIOptions extends ToOpenAPIOptions {
|
|
1032
|
+
/** Override the endpoints to emit. Defaults to `fortress.endpoints`. */
|
|
1033
|
+
endpoints?: EndpointDefinition[];
|
|
1034
|
+
}
|
|
1035
|
+
/** Options accepted by {@link Fortress.migrate}. */
|
|
1036
|
+
interface MigrateOptions {
|
|
1037
|
+
/**
|
|
1038
|
+
* Optional host-app migration step. Runs after Fortress migrations
|
|
1039
|
+
* complete; any thrown error propagates after Fortress migrations have
|
|
1040
|
+
* already been applied. Library-agnostic: pass any `() => Promise<void>`
|
|
1041
|
+
* — e.g. drizzle's `migrate(db, { migrationsFolder })`, Knex's
|
|
1042
|
+
* `db.migrate.latest()`, a hand-rolled SQL runner.
|
|
1043
|
+
*/
|
|
1044
|
+
migrateApp?: () => Promise<void>;
|
|
1045
|
+
/**
|
|
1046
|
+
* Override the migration dialect. Defaults to the adapter's `dialect`
|
|
1047
|
+
* property (matches the behavior of {@link migrateUp}).
|
|
1048
|
+
*/
|
|
1049
|
+
dialect?: 'sqlite' | 'pg';
|
|
1050
|
+
/** Stop applying Fortress migrations after this version. Defaults to the latest. */
|
|
1051
|
+
targetVersion?: number;
|
|
1052
|
+
}
|
|
1053
|
+
/** Result returned by {@link Fortress.migrate}. */
|
|
1054
|
+
interface MigrateResult {
|
|
1055
|
+
/** Result of the Fortress-managed migration step. */
|
|
1056
|
+
fortress: MigrationApplyResult;
|
|
1057
|
+
/** `true` if `migrateApp` was supplied and ran to completion. */
|
|
1058
|
+
appRan: boolean;
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Type-safe helper to retrieve a plugin's methods from a Fortress instance.
|
|
1062
|
+
*
|
|
1063
|
+
* Since plugin methods are dynamically typed at runtime, this helper lets
|
|
1064
|
+
* consumers provide a known interface for type-safe access without casting.
|
|
1065
|
+
*
|
|
1066
|
+
* @example
|
|
1067
|
+
* ```ts
|
|
1068
|
+
* interface TwoFactorMethods {
|
|
1069
|
+
* setup: (userId: string) => Promise<{ secret: string; qrCode: string }>;
|
|
1070
|
+
* verify: (userId: string, code: string) => Promise<boolean>;
|
|
1071
|
+
* }
|
|
1072
|
+
*
|
|
1073
|
+
* const twoFactor = getPluginMethods<TwoFactorMethods>(fortress, 'two-factor');
|
|
1074
|
+
* const result = await twoFactor.setup(userId); // fully typed
|
|
1075
|
+
* ```
|
|
1076
|
+
*/
|
|
1077
|
+
declare function getPluginMethods<T>(fortress: Fortress, pluginName: string): T;
|
|
1078
|
+
/**
|
|
1079
|
+
* Build a configured {@link Fortress} instance from a {@link FortressConfig}.
|
|
1080
|
+
*
|
|
1081
|
+
* Validates the JWT secret strength, deduplicates and processes the plugin
|
|
1082
|
+
* list, wires up auth/IAM services, and returns the assembled instance with
|
|
1083
|
+
* type-safe plugin method access for any plugins listed in the config.
|
|
1084
|
+
*/
|
|
1085
|
+
declare function createFortress<const T extends readonly FortressPlugin[]>(config: FortressConfig & {
|
|
1086
|
+
plugins?: T;
|
|
1087
|
+
}): Fortress<InferPlugins<T>, TypedCall<T>>;
|
|
1088
|
+
|
|
1089
|
+
export { type AuthChallengeWire as A, getExpectedColumns as B, type CallOptions as C, getFortressMigrations as D, getLatestMigrationVersion as E, type Fortress as F, getMigrationStatus as G, getMigrationUpSql as H, type InferPluginCallMap as I, getPluginMethods as J, hasMigrationDrift as K, iamComponentSchemas as L, type MigrationDialect as M, iamEndpoints as N, migrateDown as O, type PermissionSyncOptions as P, migrateUp as Q, type RouteManifestEntry as R, runPermissionSync as S, type ToOpenAPIOptions as T, toOpenAPI as U, type AuthCookiePayload as V, type RouteClassification as a, type MigrationDrift as b, type AuthImpersonationWire as c, type AuthPendingWire as d, type AuthResultWire as e, type AuthSuccessWire as f, type CallableForEndpoints as g, FORTRESS_TABLES as h, type FortressMigration as i, type FortressToOpenAPIOptions as j, type InferPlugins as k, type MigrateOptions as l, type MigrateResult as m, type MigrationApplyResult as n, type MigrationDownResult as o, type MigrationStatus as p, type PermissionSyncResult as q, type PluginMethodsMap as r, type TypedCall as s, authComponentSchemas as t, authEndpoints as u, buildCall as v, buildRouteManifest as w, createFortress as x, detectMigrationDrift as y, fortressMigrations as z };
|