@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,665 @@
|
|
|
1
|
+
import { D as DatabaseAdapter, S as ScopeRule } from './index-CxEkfCA0.js';
|
|
2
|
+
import { JwtKeyMaterial } from './jwt.js';
|
|
3
|
+
import { i as AuthService, g as IamService, F as FortressLogger, T as TelemetryProvider } from './auth-service-BkzZkWfH.js';
|
|
4
|
+
import { S as Subject, T as TokenClaims, i as CreateUserInput, l as PendingReason, F as FortressUser, p as AuthSuccess, h as AuthTokenPair, R as RequestMeta } from './types-et0R8tsC.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Standard Schema V1 interfaces — inlined from the spec.
|
|
8
|
+
* No `@standard-schema/spec` dependency needed.
|
|
9
|
+
*
|
|
10
|
+
* @see https://standardschema.dev
|
|
11
|
+
* @see https://github.com/standard-schema/standard-schema
|
|
12
|
+
*/
|
|
13
|
+
declare namespace StandardSchemaV1 {
|
|
14
|
+
interface Props<Input = unknown, Output = Input> {
|
|
15
|
+
readonly version: 1;
|
|
16
|
+
readonly vendor: string;
|
|
17
|
+
readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
|
|
18
|
+
readonly types?: Types<Input, Output> | undefined;
|
|
19
|
+
}
|
|
20
|
+
type Result<Output> = SuccessResult<Output> | FailureResult;
|
|
21
|
+
interface SuccessResult<Output> {
|
|
22
|
+
readonly value: Output;
|
|
23
|
+
readonly issues?: undefined;
|
|
24
|
+
}
|
|
25
|
+
interface FailureResult {
|
|
26
|
+
readonly issues: ReadonlyArray<Issue>;
|
|
27
|
+
}
|
|
28
|
+
interface Issue {
|
|
29
|
+
readonly message: string;
|
|
30
|
+
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
|
|
31
|
+
}
|
|
32
|
+
interface PathSegment {
|
|
33
|
+
readonly key: PropertyKey;
|
|
34
|
+
}
|
|
35
|
+
interface Types<Input = unknown, Output = Input> {
|
|
36
|
+
readonly input: Input;
|
|
37
|
+
readonly output: Output;
|
|
38
|
+
}
|
|
39
|
+
type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['input'];
|
|
40
|
+
type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output'];
|
|
41
|
+
}
|
|
42
|
+
interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
43
|
+
readonly '~standard': StandardSchemaV1.Props<Input, Output>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* JSON Schema types (draft 2020-12 subset) + FortressSchema.
|
|
48
|
+
*
|
|
49
|
+
* FortressSchema<T> extends JSONSchema with Standard Schema V1 support,
|
|
50
|
+
* giving each schema: JSON Schema fields (OpenAPI), runtime validation,
|
|
51
|
+
* and TypeScript type inference — all from one object.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/** A subset of JSON Schema (draft 2020-12) sufficient for fortress endpoint definitions and OpenAPI generation. */
|
|
55
|
+
interface JSONSchema {
|
|
56
|
+
type?: 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object' | 'null';
|
|
57
|
+
properties?: Record<string, JSONSchema>;
|
|
58
|
+
required?: string[];
|
|
59
|
+
items?: JSONSchema;
|
|
60
|
+
enum?: (string | number | boolean | null)[];
|
|
61
|
+
const?: string | number | boolean | null;
|
|
62
|
+
format?: string;
|
|
63
|
+
description?: string;
|
|
64
|
+
default?: unknown;
|
|
65
|
+
nullable?: boolean;
|
|
66
|
+
oneOf?: JSONSchema[];
|
|
67
|
+
anyOf?: JSONSchema[];
|
|
68
|
+
allOf?: JSONSchema[];
|
|
69
|
+
$ref?: string;
|
|
70
|
+
discriminator?: {
|
|
71
|
+
propertyName: string;
|
|
72
|
+
mapping?: Record<string, string>;
|
|
73
|
+
};
|
|
74
|
+
additionalProperties?: boolean | JSONSchema;
|
|
75
|
+
minimum?: number;
|
|
76
|
+
maximum?: number;
|
|
77
|
+
minLength?: number;
|
|
78
|
+
maxLength?: number;
|
|
79
|
+
pattern?: string;
|
|
80
|
+
title?: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* A JSON Schema object that also implements Standard Schema V1.
|
|
84
|
+
*
|
|
85
|
+
* - JSON Schema fields → OpenAPI 3.1 spec generation
|
|
86
|
+
* - `~standard.validate()` → runtime validation
|
|
87
|
+
* - `StandardSchemaV1.InferOutput<typeof schema>` → TypeScript type inference
|
|
88
|
+
*/
|
|
89
|
+
type FortressSchema<T = unknown> = JSONSchema & StandardSchemaV1<T, T>;
|
|
90
|
+
/** Extract the TypeScript type from a FortressSchema or StandardSchemaV1. */
|
|
91
|
+
type Infer<T> = T extends StandardSchemaV1<any, infer O> ? O : unknown;
|
|
92
|
+
/** Flatten intersection types for clean IDE tooltips. */
|
|
93
|
+
type Simplify<T> = {
|
|
94
|
+
[K in keyof T]: T[K];
|
|
95
|
+
} & {};
|
|
96
|
+
|
|
97
|
+
/** HTTP method an {@link EndpointDefinition} can declare. */
|
|
98
|
+
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
99
|
+
/** Authentication scheme required to call an endpoint. */
|
|
100
|
+
type SecurityRequirement = 'bearer' | 'basic' | 'apiKey' | 'none';
|
|
101
|
+
/** A required IAM permission, expressed as a `(resource, action)` pair. */
|
|
102
|
+
interface EndpointPermission {
|
|
103
|
+
resource: string;
|
|
104
|
+
action: string;
|
|
105
|
+
}
|
|
106
|
+
/** OpenAPI / IAM metadata attached to an {@link EndpointDefinition}. */
|
|
107
|
+
interface EndpointMeta {
|
|
108
|
+
summary: string;
|
|
109
|
+
description?: string;
|
|
110
|
+
tags?: string[];
|
|
111
|
+
security?: SecurityRequirement[];
|
|
112
|
+
deprecated?: boolean;
|
|
113
|
+
/** IAM permission required to access this endpoint. Enforced by RBAC middleware. */
|
|
114
|
+
permission?: EndpointPermission;
|
|
115
|
+
/**
|
|
116
|
+
* What kind of bearer token this route accepts.
|
|
117
|
+
*
|
|
118
|
+
* - `'jwt'` (default) — the route expects a Fortress session JWT in
|
|
119
|
+
* `Authorization: Bearer <jwt>` (or the cookie). The dispatcher runs
|
|
120
|
+
* the plugin principal chain, JWT verification, and RBAC enforcement
|
|
121
|
+
* automatically before invoking the handler.
|
|
122
|
+
* - `'oauth'` — the route's bearer is an OAuth 2.0 access token (or no
|
|
123
|
+
* token at all, e.g. `/oauth/authorize`). The dispatcher skips its
|
|
124
|
+
* auth pipeline entirely and the handler self-parses the bearer
|
|
125
|
+
* (typical for `/oauth/userinfo`, `/oauth/token`, etc.). Body parsing
|
|
126
|
+
* and validation are also skipped, since OAuth bodies are
|
|
127
|
+
* `application/x-www-form-urlencoded` per RFC 6749.
|
|
128
|
+
*
|
|
129
|
+
* Routes that don't set this field default to `'jwt'`. Without this
|
|
130
|
+
* marker, the OAuth plugin's consent-flow endpoints
|
|
131
|
+
* (`/oauth/flows/:flowId{,/approve,/deny}`) — which are SPA-driven and
|
|
132
|
+
* require a Fortress JWT — used to be silently unauthenticated due to
|
|
133
|
+
* a path-based `startsWith('/oauth/')` short-circuit in the dispatcher,
|
|
134
|
+
* forcing every host app to ship a workaround shim. Setting
|
|
135
|
+
* `bearerKind: 'oauth'` only on the actual protocol routes lets the
|
|
136
|
+
* dispatcher honour `security: ['bearer']` everywhere else.
|
|
137
|
+
*/
|
|
138
|
+
bearerKind?: 'jwt' | 'oauth';
|
|
139
|
+
}
|
|
140
|
+
/** Request input declarations for an endpoint — JSON Schemas for OpenAPI plus Standard Schemas for runtime validation. */
|
|
141
|
+
interface EndpointInput {
|
|
142
|
+
/** JSON Schema for OpenAPI spec generation. */
|
|
143
|
+
body?: JSONSchema;
|
|
144
|
+
query?: JSONSchema;
|
|
145
|
+
params?: JSONSchema;
|
|
146
|
+
/** Standard Schema references for runtime validation (set by endpoint builder). */
|
|
147
|
+
bodySchema?: StandardSchemaV1;
|
|
148
|
+
querySchema?: StandardSchemaV1;
|
|
149
|
+
paramsSchema?: StandardSchemaV1;
|
|
150
|
+
}
|
|
151
|
+
/** A single OpenAPI response definition (description plus optional JSON Schema). */
|
|
152
|
+
interface EndpointResponse {
|
|
153
|
+
description: string;
|
|
154
|
+
schema?: JSONSchema;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Declarative description of one HTTP endpoint.
|
|
158
|
+
*
|
|
159
|
+
* The four generic parameters are **phantom types** — they're populated by
|
|
160
|
+
* the fluent {@link EndpointBuilder} as schemas are declared via `.body()`,
|
|
161
|
+
* `.query()`, `.params()`, and `.response()`, and then extracted at the call
|
|
162
|
+
* site by the `InferEndpoint*` helpers (and by the typed `fortress.call.*`
|
|
163
|
+
* proxy). None of them exist at runtime.
|
|
164
|
+
*
|
|
165
|
+
* The defaults are `{}` (empty object), not `unknown`, so that the
|
|
166
|
+
* intersection-based `InferEndpointCallInput` collapses cleanly for
|
|
167
|
+
* endpoints that only declare one of the three input slots.
|
|
168
|
+
*/
|
|
169
|
+
interface EndpointDefinition<TBody = {}, TQuery = {}, TParams = {}, TResponses extends Record<number, unknown> = {}> {
|
|
170
|
+
method: HttpMethod;
|
|
171
|
+
path: string;
|
|
172
|
+
handler: string;
|
|
173
|
+
meta?: EndpointMeta;
|
|
174
|
+
input?: EndpointInput;
|
|
175
|
+
responses?: Record<number, EndpointResponse>;
|
|
176
|
+
/** Phantom — never present at runtime. Consumed by `InferEndpoint*` helpers. */
|
|
177
|
+
__types?: {
|
|
178
|
+
body: TBody;
|
|
179
|
+
query: TQuery;
|
|
180
|
+
params: TParams;
|
|
181
|
+
responses: TResponses;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/** Extract the request-body type from an {@link EndpointDefinition}. */
|
|
185
|
+
type InferEndpointBody<E> = E extends EndpointDefinition<infer B, any, any, any> ? B : never;
|
|
186
|
+
/** Extract the query-string type from an {@link EndpointDefinition}. */
|
|
187
|
+
type InferEndpointQuery<E> = E extends EndpointDefinition<any, infer Q, any, any> ? Q : never;
|
|
188
|
+
/** Extract the path-params type from an {@link EndpointDefinition}. */
|
|
189
|
+
type InferEndpointParams<E> = E extends EndpointDefinition<any, any, infer P, any> ? P : never;
|
|
190
|
+
/** Extract the full `Record<status, body>` response map from an {@link EndpointDefinition}. */
|
|
191
|
+
type InferEndpointResponses<E> = E extends EndpointDefinition<any, any, any, infer R> ? R : never;
|
|
192
|
+
/**
|
|
193
|
+
* Extract the success-response body from an {@link EndpointDefinition}.
|
|
194
|
+
*
|
|
195
|
+
* Tries `200`, then `201`, then `204`, then falls back to `unknown`.
|
|
196
|
+
* This matches the behavior of the `fortress.call.*` proxy, which always
|
|
197
|
+
* resolves to the first successful status declared.
|
|
198
|
+
*/
|
|
199
|
+
type InferEndpointSuccessResponse<E> = InferEndpointResponses<E> extends infer R ? R extends {
|
|
200
|
+
200: infer T;
|
|
201
|
+
} ? T : R extends {
|
|
202
|
+
201: infer T;
|
|
203
|
+
} ? T : R extends {
|
|
204
|
+
204: infer T;
|
|
205
|
+
} ? T : unknown : never;
|
|
206
|
+
/**
|
|
207
|
+
* The flat input shape accepted by the typed `fortress.call.<handler>(input)`
|
|
208
|
+
* proxy — the intersection of body, query, and params. For endpoints that
|
|
209
|
+
* only declare one of the three, the other slots are `{}` and disappear from
|
|
210
|
+
* the intersection.
|
|
211
|
+
*/
|
|
212
|
+
type InferEndpointCallInput<E> = Simplify<InferEndpointBody<E> & InferEndpointQuery<E> & InferEndpointParams<E>>;
|
|
213
|
+
/**
|
|
214
|
+
* Component schemas are reusable JSON Schema definitions
|
|
215
|
+
* that endpoints reference via $ref (e.g., '#/components/schemas/User').
|
|
216
|
+
*/
|
|
217
|
+
interface ComponentSchemas {
|
|
218
|
+
[name: string]: JSONSchema;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
type PasswordBreachFailureMode = 'open' | 'closed';
|
|
222
|
+
interface PasswordBreachDegradedEvent {
|
|
223
|
+
failureMode: PasswordBreachFailureMode;
|
|
224
|
+
status?: number;
|
|
225
|
+
error?: unknown;
|
|
226
|
+
}
|
|
227
|
+
/** Called whenever the HIBP control is unavailable, regardless of failure mode. */
|
|
228
|
+
type PasswordPolicyObserver = (event: PasswordBreachDegradedEvent) => void;
|
|
229
|
+
interface PasswordPolicyConfig {
|
|
230
|
+
/** Minimum password length. Default: 15 (NIST 800-63B). */
|
|
231
|
+
minLength?: number;
|
|
232
|
+
/** Maximum password length. Default: 128 (NIST 800-63B). */
|
|
233
|
+
maxLength?: number;
|
|
234
|
+
/** Check password against HIBP breached password database via k-anonymity API. Default: false. */
|
|
235
|
+
checkBreached?: boolean;
|
|
236
|
+
/** Cache TTL for HIBP range responses in milliseconds. Default: 86400000 (24h). */
|
|
237
|
+
breachedCacheTtlMs?: number;
|
|
238
|
+
/** Maximum cached HIBP range responses. Default: 1000. Set to 0 to disable caching. */
|
|
239
|
+
breachedCacheMaxEntries?: number;
|
|
240
|
+
/** Whether HIBP unavailability permits password writes. Default: 'open'. */
|
|
241
|
+
breachedFailureMode?: PasswordBreachFailureMode;
|
|
242
|
+
}
|
|
243
|
+
type HibpCache = Map<string, {
|
|
244
|
+
data: string;
|
|
245
|
+
expiresAt: number;
|
|
246
|
+
}>;
|
|
247
|
+
interface PasswordBreachCheckOptions {
|
|
248
|
+
cacheTtlMs?: number;
|
|
249
|
+
cacheMaxEntries?: number;
|
|
250
|
+
failureMode?: PasswordBreachFailureMode;
|
|
251
|
+
observer?: PasswordPolicyObserver;
|
|
252
|
+
/** Internal per-Fortress-instance cache supplied by validatePassword. */
|
|
253
|
+
cache?: HibpCache;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Pipeline-level CSRF protection.
|
|
258
|
+
*
|
|
259
|
+
* Activated by default for unsafe HTTP methods (`POST/PUT/PATCH/DELETE`)
|
|
260
|
+
* when the request authenticated via a Fortress auth cookie. Bearer-only
|
|
261
|
+
* and API-key requests are CSRF-immune by construction (no ambient
|
|
262
|
+
* credential the browser would auto-attach) and are skipped.
|
|
263
|
+
*
|
|
264
|
+
* The check is intentionally narrow:
|
|
265
|
+
*
|
|
266
|
+
* 1. Reject when `Sec-Fetch-Site: cross-site`. Modern browsers send this
|
|
267
|
+
* header on every fetch — its presence + value is a reliable signal.
|
|
268
|
+
* 2. Reject when the required custom header (`X-Fortress-CSRF` by default)
|
|
269
|
+
* is absent. Custom-header CSRF leverages the browser preflight rule:
|
|
270
|
+
* a cross-origin request can't set the header without an explicit
|
|
271
|
+
* server-side CORS allowance.
|
|
272
|
+
*
|
|
273
|
+
* Closes H5 — before this, `mountFortress` / `createSvelteKitHandle`
|
|
274
|
+
* dispatched straight to `handleRequest` with no CSRF check. This check now
|
|
275
|
+
* runs inside `handleRequest`, so every Fortress-managed route is covered
|
|
276
|
+
* regardless of adapter (Hono, Express, SvelteKit). The SvelteKit adapter's
|
|
277
|
+
* user-route silent token refresh — the one remaining Fortress state-change
|
|
278
|
+
* outside this pipeline — is independently gated to safe HTTP methods.
|
|
279
|
+
*/
|
|
280
|
+
|
|
281
|
+
/** User-facing CSRF configuration (all fields optional). */
|
|
282
|
+
interface CsrfConfig {
|
|
283
|
+
/**
|
|
284
|
+
* Set to `false` to disable the pipeline CSRF check entirely. Useful for
|
|
285
|
+
* pure bearer-only API deployments where no Fortress route is reachable
|
|
286
|
+
* with ambient credentials. Default: `true`.
|
|
287
|
+
*/
|
|
288
|
+
enabled?: boolean;
|
|
289
|
+
/** Required header name. Default: `'X-Fortress-CSRF'`. */
|
|
290
|
+
headerName?: string;
|
|
291
|
+
/**
|
|
292
|
+
* Exact-path allow-list of routes the check should skip. Matched at
|
|
293
|
+
* segment boundaries (i.e. `'/foo'` matches `/foo` and `/foo/bar` but
|
|
294
|
+
* not `/foobar`).
|
|
295
|
+
*/
|
|
296
|
+
skipPaths?: string[];
|
|
297
|
+
/**
|
|
298
|
+
* Additionally reject `Sec-Fetch-Site: same-site` requests. Single-host
|
|
299
|
+
* deployments (web app + Fortress on the same origin) can opt in to the
|
|
300
|
+
* stricter posture; multi-host (`api.example.com` + `app.example.com`)
|
|
301
|
+
* deployments leave this `false` so the legit app traffic passes.
|
|
302
|
+
* Default: `false`.
|
|
303
|
+
*/
|
|
304
|
+
rejectSameSite?: boolean;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Framework-agnostic plugin middleware execution for `fortress.handleRequest`
|
|
309
|
+
* and adapter handles.
|
|
310
|
+
*
|
|
311
|
+
* Wraps {@link executePluginMiddleware} from `plugin-runner.ts` so the
|
|
312
|
+
* caller can hand it a {@link PluginRequestContext} (a duck-typed object with
|
|
313
|
+
* `fortressUserId` and friends) instead of a framework-specific Hono context
|
|
314
|
+
* or Express request.
|
|
315
|
+
*
|
|
316
|
+
* Plugins that short-circuit do so by throwing a {@link FortressError}; the
|
|
317
|
+
* caller is expected to wrap this in a try/catch and translate via
|
|
318
|
+
* `errorToResponse`.
|
|
319
|
+
*/
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Object passed to plugin middleware as the "request" argument when running
|
|
323
|
+
* inside `fortress.handleRequest` or any framework adapter that delegates to
|
|
324
|
+
* core. Carries the web-standard `Request` plus the resolved auth context.
|
|
325
|
+
*
|
|
326
|
+
* The shape is intentionally compatible with the existing admin-plugin
|
|
327
|
+
* `extractUserId` helper, which checks `'fortressUserId' in request`.
|
|
328
|
+
*/
|
|
329
|
+
interface PluginRequestContext {
|
|
330
|
+
request: Request;
|
|
331
|
+
/**
|
|
332
|
+
* The resolved principal for this request, if any. Populated from a
|
|
333
|
+
* plugin `resolvePrincipal` hit or the JWT fallback. Plugins preferring
|
|
334
|
+
* subject-level identity (non-USER principals) should read this instead
|
|
335
|
+
* of `fortressUserId`.
|
|
336
|
+
*/
|
|
337
|
+
fortressSubject?: Subject;
|
|
338
|
+
/**
|
|
339
|
+
* Convenience alias — set only when the resolved subject is a `USER`.
|
|
340
|
+
* Non-USER principals (e.g. `SERVICE_ACCOUNT` via api-key) leave this
|
|
341
|
+
* field undefined; consumers should fall back to `fortressSubject`.
|
|
342
|
+
*/
|
|
343
|
+
fortressUserId?: string;
|
|
344
|
+
fortressClaims?: TokenClaims;
|
|
345
|
+
/** Credential-level narrowing scopes (for example API-key scopes), if any. */
|
|
346
|
+
fortressScopes?: string[] | null;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
interface FortressPlugin {
|
|
350
|
+
/** Unique plugin identifier */
|
|
351
|
+
name: string;
|
|
352
|
+
/** DB models this plugin needs */
|
|
353
|
+
models?: ModelDefinition[];
|
|
354
|
+
/** Hooks into auth lifecycle (executed in plugin registration order) */
|
|
355
|
+
hooks?: PluginHooks;
|
|
356
|
+
/** Extra methods exposed on fortress.plugins.<name> */
|
|
357
|
+
methods?: (ctx: PluginContext) => Record<string, Function>;
|
|
358
|
+
/**
|
|
359
|
+
* HTTP routes this plugin adds, keyed by handler name.
|
|
360
|
+
*
|
|
361
|
+
* A keyed record (not an array) so each entry's full
|
|
362
|
+
* `EndpointDefinition<TBody, TQuery, TParams, TResponses>` type is
|
|
363
|
+
* preserved for the typed `fortress.call.*` proxy. The dispatcher looks
|
|
364
|
+
* plugin route handlers up by name (`fortress.plugins[pluginName][handlerName]`),
|
|
365
|
+
* so the keyed shape is already the natural fit at dispatch time — the
|
|
366
|
+
* key just needs to match the `EndpointDefinition.handler` string.
|
|
367
|
+
*/
|
|
368
|
+
routes?: Record<string, EndpointDefinition>;
|
|
369
|
+
/** Middleware to inject into the request pipeline */
|
|
370
|
+
middleware?: MiddlewareDefinition[];
|
|
371
|
+
/** Wrap the DatabaseAdapter per-request */
|
|
372
|
+
wrapAdapter?: (adapter: DatabaseAdapter, requestContext: Record<string, unknown>) => DatabaseAdapter;
|
|
373
|
+
/** Extend JWT token claims */
|
|
374
|
+
enrichTokenClaims?: (userId: string, ctx: PluginContext) => Promise<Record<string, unknown>>;
|
|
375
|
+
/** Scope data access by user context (row-level data isolation) */
|
|
376
|
+
scopeRules?: (userId: string, model: string, ctx: PluginContext) => Promise<ScopeRule | null>;
|
|
377
|
+
/**
|
|
378
|
+
* Resolve a request principal from a non-JWT credential — API key,
|
|
379
|
+
* OAuth bearer, mTLS client cert, signed JWT assertion, etc.
|
|
380
|
+
*
|
|
381
|
+
* Called by `fortress.handleRequest` **before** the JWT fallback. Plugins
|
|
382
|
+
* are tried in registration order; the first to return a non-null result
|
|
383
|
+
* wins, and its subject (plus optional claims) become the request
|
|
384
|
+
* principal for downstream RBAC. Returning `null` means "defer" — the
|
|
385
|
+
* next plugin is tried, and if none resolve, the core JWT path runs.
|
|
386
|
+
*
|
|
387
|
+
* This is the extension point for any future credential mechanism. The
|
|
388
|
+
* api-key plugin implements it to turn `Authorization: ApiKey <key>` /
|
|
389
|
+
* `X-API-Key: <key>` headers into a `USER` or `SERVICE_ACCOUNT`
|
|
390
|
+
* principal.
|
|
391
|
+
*/
|
|
392
|
+
resolvePrincipal?: (request: Request, ctx: PluginContext) => Promise<{
|
|
393
|
+
subject: Subject;
|
|
394
|
+
claims?: TokenClaims;
|
|
395
|
+
scopes?: string[] | null;
|
|
396
|
+
} | null>;
|
|
397
|
+
}
|
|
398
|
+
interface PluginHooks {
|
|
399
|
+
beforeLogin?: (ctx: HookContext & {
|
|
400
|
+
email: string;
|
|
401
|
+
}) => Promise<HookResult | void>;
|
|
402
|
+
beforeRegister?: (ctx: HookContext & {
|
|
403
|
+
data: CreateUserInput;
|
|
404
|
+
}) => Promise<HookResult | void>;
|
|
405
|
+
beforeTokenRefresh?: (ctx: HookContext & {
|
|
406
|
+
token: string;
|
|
407
|
+
}) => Promise<HookResult | void>;
|
|
408
|
+
beforeLogout?: (ctx: HookContext & {
|
|
409
|
+
token: string;
|
|
410
|
+
}) => Promise<void>;
|
|
411
|
+
onLoginFailure?: (ctx: HookContext & {
|
|
412
|
+
identifier: string;
|
|
413
|
+
error: Error;
|
|
414
|
+
}) => Promise<void>;
|
|
415
|
+
/** Runs after primary credentials succeed but before any token is issued. */
|
|
416
|
+
postAuthGate?: PostAuthGateProvider;
|
|
417
|
+
afterLogin?: (ctx: AfterHookContext, result: AuthSuccess) => Promise<AuthSuccess>;
|
|
418
|
+
afterRegister?: (ctx: AfterHookContext, user: FortressUser) => Promise<void>;
|
|
419
|
+
afterTokenRefresh?: (ctx: AfterHookContext, result: AuthTokenPair) => Promise<AuthTokenPair>;
|
|
420
|
+
}
|
|
421
|
+
interface HookContext {
|
|
422
|
+
db: DatabaseAdapter;
|
|
423
|
+
config: FortressConfig;
|
|
424
|
+
meta?: RequestMeta;
|
|
425
|
+
}
|
|
426
|
+
interface AfterHookContext extends HookContext {
|
|
427
|
+
responseHeaders: Headers;
|
|
428
|
+
/** Normalized login identifier used for this auth flow, when applicable. */
|
|
429
|
+
identifier?: string;
|
|
430
|
+
}
|
|
431
|
+
interface HookResult {
|
|
432
|
+
stop: true;
|
|
433
|
+
response: Record<string, unknown>;
|
|
434
|
+
}
|
|
435
|
+
interface PostAuthGateContext extends HookContext {
|
|
436
|
+
user: FortressUser;
|
|
437
|
+
/** Reasons already satisfied by the continuation currently being completed. */
|
|
438
|
+
completedReasons: readonly PendingReason[];
|
|
439
|
+
}
|
|
440
|
+
interface PostAuthGateDecision {
|
|
441
|
+
pluginData?: Record<string, unknown>;
|
|
442
|
+
}
|
|
443
|
+
interface PostAuthGateVerificationContext extends HookContext {
|
|
444
|
+
user: FortressUser;
|
|
445
|
+
continuation: {
|
|
446
|
+
id: string;
|
|
447
|
+
reason: PendingReason;
|
|
448
|
+
expiresAt: Date;
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
interface PostAuthGateProvider {
|
|
452
|
+
reason: PendingReason;
|
|
453
|
+
/** Durable failed-proof policy stored on each continuation. */
|
|
454
|
+
maxAttempts?: number;
|
|
455
|
+
cooldownSeconds?: number;
|
|
456
|
+
evaluate: (ctx: PostAuthGateContext) => Promise<PostAuthGateDecision | void>;
|
|
457
|
+
/** Return optional plugin data to include in the final AuthResult. */
|
|
458
|
+
verify: (ctx: PostAuthGateVerificationContext, completion: unknown) => Promise<Record<string, unknown> | void>;
|
|
459
|
+
}
|
|
460
|
+
type ModelConstraint = {
|
|
461
|
+
type: 'unique';
|
|
462
|
+
fields: string[];
|
|
463
|
+
} | {
|
|
464
|
+
type: 'index';
|
|
465
|
+
fields: string[];
|
|
466
|
+
name?: string;
|
|
467
|
+
};
|
|
468
|
+
interface ModelDefinition {
|
|
469
|
+
name: string;
|
|
470
|
+
fields: Record<string, FieldDefinition>;
|
|
471
|
+
constraints?: ModelConstraint[];
|
|
472
|
+
}
|
|
473
|
+
interface FieldDefinition {
|
|
474
|
+
type: 'string' | 'number' | 'boolean' | 'date';
|
|
475
|
+
required?: boolean;
|
|
476
|
+
unique?: boolean;
|
|
477
|
+
references?: {
|
|
478
|
+
model: string;
|
|
479
|
+
field: string;
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
interface PluginContext {
|
|
483
|
+
db: DatabaseAdapter;
|
|
484
|
+
config: FortressConfig;
|
|
485
|
+
/** Auth service reference. Optional at init time; available at runtime (enrichTokenClaims, scopeRules). */
|
|
486
|
+
auth?: AuthService;
|
|
487
|
+
/** IAM service reference. Optional at init time; available at runtime. */
|
|
488
|
+
iam?: IamService;
|
|
489
|
+
/** Resolved logger (silent no-op if `config.logger` is unset). */
|
|
490
|
+
logger?: FortressLogger;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Second argument passed to plugin HTTP route handlers by the dispatcher.
|
|
494
|
+
* Carries the verified caller identity and the raw Request so handlers can
|
|
495
|
+
* make authorization decisions, stamp audit entries, or read headers/cookies
|
|
496
|
+
* without trusting client-supplied body fields.
|
|
497
|
+
*
|
|
498
|
+
* `subject` / `claims` are populated whenever the endpoint's `meta.security`
|
|
499
|
+
* declared bearer auth (the dispatcher resolves principals first). `userId`
|
|
500
|
+
* is a convenience — it's present iff `subject?.type === 'USER'`, matching
|
|
501
|
+
* the pre-SERVICE_ACCOUNT shape. For public endpoints all three are
|
|
502
|
+
* `undefined`.
|
|
503
|
+
*/
|
|
504
|
+
interface PluginRouteContext {
|
|
505
|
+
/** Resolved request principal — USER or SERVICE_ACCOUNT. */
|
|
506
|
+
subject?: Subject;
|
|
507
|
+
/** Convenience alias for `subject?.id` when the subject is a USER. */
|
|
508
|
+
userId?: string;
|
|
509
|
+
claims?: TokenClaims;
|
|
510
|
+
/** Credential-level narrowing scopes from principal resolution, if any. */
|
|
511
|
+
scopes?: string[] | null;
|
|
512
|
+
meta?: RequestMeta;
|
|
513
|
+
request: Request;
|
|
514
|
+
}
|
|
515
|
+
interface MiddlewareDefinition {
|
|
516
|
+
path: string;
|
|
517
|
+
position: 'before-auth' | 'after-auth' | 'after-rbac';
|
|
518
|
+
handler: (ctx: PluginContext, request: PluginRequestContext, next: () => Promise<void>) => Promise<void>;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/** Pluggable password hashing contract — implement to swap fortress's default Argon2id WASM hasher. */
|
|
522
|
+
interface PasswordHasher {
|
|
523
|
+
hash: (password: string) => Promise<string>;
|
|
524
|
+
verify: (hash: string, password: string) => Promise<boolean>;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Cookie configuration consumed by `fortress.handleRequest` and any framework
|
|
528
|
+
* adapter that wants to serialize auth tokens to `Set-Cookie` headers.
|
|
529
|
+
*
|
|
530
|
+
* Defaults are environment-independent and production-safe: `__Host-`
|
|
531
|
+
* prefixed names, `httpOnly`, `secure`, `sameSite: 'lax'`, `path: '/'`.
|
|
532
|
+
* Plain-HTTP local development must explicitly set `secure: false`, which
|
|
533
|
+
* also selects non-`__Host-` default names.
|
|
534
|
+
*/
|
|
535
|
+
interface CookieConfig {
|
|
536
|
+
/** Override the access-token cookie name. Default: `__Host-fortress_access` when host-prefix constraints permit. */
|
|
537
|
+
accessName?: string;
|
|
538
|
+
/** Override the refresh-token cookie name. Default: `__Host-fortress_refresh` when host-prefix constraints permit. */
|
|
539
|
+
refreshName?: string;
|
|
540
|
+
/** SameSite attribute. Default: `'lax'`. */
|
|
541
|
+
sameSite?: 'lax' | 'strict' | 'none';
|
|
542
|
+
/** Force the `Secure` attribute on or off. Default: `true` in every environment. */
|
|
543
|
+
secure?: boolean;
|
|
544
|
+
/** Optional `Domain` attribute. Defaults to host-only when omitted. */
|
|
545
|
+
domain?: string;
|
|
546
|
+
/** `Path` attribute. Default: `'/'`. */
|
|
547
|
+
path?: string;
|
|
548
|
+
}
|
|
549
|
+
/** Opt-in refresh-session controls. Omitted fields preserve existing behavior. */
|
|
550
|
+
interface SessionConfig {
|
|
551
|
+
/** Accept the immediately previous refresh token for this many seconds. */
|
|
552
|
+
refreshGraceSeconds?: number;
|
|
553
|
+
/** Reject a session after this many seconds without refresh activity. */
|
|
554
|
+
idleTimeoutSeconds?: number;
|
|
555
|
+
/** Reject a refresh family after this many seconds, regardless of activity. */
|
|
556
|
+
absoluteTimeoutSeconds?: number;
|
|
557
|
+
/** Maximum active refresh families per user; oldest sessions are revoked first. */
|
|
558
|
+
maxSessionsPerUser?: number;
|
|
559
|
+
}
|
|
560
|
+
/** Top-level fortress configuration accepted by {@link createFortress}. */
|
|
561
|
+
interface FortressConfig {
|
|
562
|
+
jwt: {
|
|
563
|
+
/**
|
|
564
|
+
* Signing/verification key material. Today: an HS256 shared secret
|
|
565
|
+
* (string) or a rotation array of shared secrets (first signs, all
|
|
566
|
+
* verify). See {@link JwtKeyMaterial} for the planned expansion to
|
|
567
|
+
* asymmetric keys / JWKS.
|
|
568
|
+
*/
|
|
569
|
+
key: JwtKeyMaterial;
|
|
570
|
+
issuer?: string;
|
|
571
|
+
/** Optional audience included in and required for access tokens. */
|
|
572
|
+
audience?: string | string[];
|
|
573
|
+
accessTokenExpirySeconds?: number;
|
|
574
|
+
refreshTokenExpirySeconds?: number;
|
|
575
|
+
/**
|
|
576
|
+
* Compare a domain-separated keyed fingerprint of User-Agent + source IP
|
|
577
|
+
* across refreshes. This is an anomaly signal, not proof of device identity;
|
|
578
|
+
* enable hard rejection only where client IP stability is acceptable.
|
|
579
|
+
*/
|
|
580
|
+
validateRefreshFingerprint?: boolean | 'warn';
|
|
581
|
+
/** Optional session rotation/cap controls. No caps or grace apply when omitted. */
|
|
582
|
+
session?: SessionConfig;
|
|
583
|
+
};
|
|
584
|
+
rbac?: {
|
|
585
|
+
evaluationMode?: 'allow-only' | 'deny-overrides';
|
|
586
|
+
resourceFile?: string;
|
|
587
|
+
cache?: {
|
|
588
|
+
ttlSeconds?: number;
|
|
589
|
+
maxEntries?: number;
|
|
590
|
+
};
|
|
591
|
+
};
|
|
592
|
+
database: DatabaseAdapter;
|
|
593
|
+
passwordHasher?: PasswordHasher;
|
|
594
|
+
passwordPolicy?: PasswordPolicyConfig;
|
|
595
|
+
/**
|
|
596
|
+
* Impersonation hardening (RFC 8693 `act` tokens). All fields optional;
|
|
597
|
+
* defaults to a 1-hour ceiling on caller-supplied `expirySeconds`.
|
|
598
|
+
*/
|
|
599
|
+
impersonation?: {
|
|
600
|
+
/**
|
|
601
|
+
* Hard ceiling on the impersonation access-token lifetime in seconds.
|
|
602
|
+
* `auth.impersonate({ expirySeconds })` is clamped to this. Default: 3600.
|
|
603
|
+
*/
|
|
604
|
+
maxTtlSeconds?: number;
|
|
605
|
+
};
|
|
606
|
+
plugins?: readonly FortressPlugin[];
|
|
607
|
+
/**
|
|
608
|
+
* Host-application endpoint definitions to register with the manifest,
|
|
609
|
+
* OpenAPI generation, and `protect()` / adapter `protectedRoute()` helpers
|
|
610
|
+
* without authoring a plugin. Fortress tracks them internally under the
|
|
611
|
+
* reserved name `__host`.
|
|
612
|
+
*
|
|
613
|
+
* Top-level `routes` are metadata-only: adapters leave them to the host
|
|
614
|
+
* router and they do not add `fortress.call.*` entries because no handler
|
|
615
|
+
* methods are registered. If you need Fortress-mounted routes or typed
|
|
616
|
+
* in-process callables for custom routes, declare a real plugin with both
|
|
617
|
+
* `routes` and matching `methods`.
|
|
618
|
+
*
|
|
619
|
+
* Keyed by handler name, matching {@link FortressPlugin.routes}:
|
|
620
|
+
*
|
|
621
|
+
* ```ts
|
|
622
|
+
* createFortress({
|
|
623
|
+
* database, jwt, cookies,
|
|
624
|
+
* routes: appEndpoints,
|
|
625
|
+
* });
|
|
626
|
+
* ```
|
|
627
|
+
*
|
|
628
|
+
* The name `__host` is reserved — declaring a plugin called `__host`
|
|
629
|
+
* alongside `routes` is a configuration error.
|
|
630
|
+
*/
|
|
631
|
+
routes?: Record<string, EndpointDefinition>;
|
|
632
|
+
/** Auth-cookie naming and attributes used by `fortress.handleRequest` and framework adapters. */
|
|
633
|
+
cookies?: CookieConfig;
|
|
634
|
+
/**
|
|
635
|
+
* Pipeline CSRF protection (H5). Defaults to `{ enabled: true }`.
|
|
636
|
+
* Bearer/API-key requests are exempt automatically — only cookie-auth
|
|
637
|
+
* traffic is checked. See {@link CsrfConfig}.
|
|
638
|
+
*/
|
|
639
|
+
csrf?: CsrfConfig;
|
|
640
|
+
/**
|
|
641
|
+
* Optional pluggable logger. Accepts any object that conforms structurally
|
|
642
|
+
* to {@link FortressLogger} — a `pino()` instance, Fastify's `app.log`,
|
|
643
|
+
* or a hand-rolled `console` wrapper. Defaults to a silent no-op so
|
|
644
|
+
* Fortress never writes to stderr unless the caller opts in.
|
|
645
|
+
*/
|
|
646
|
+
logger?: FortressLogger;
|
|
647
|
+
/**
|
|
648
|
+
* Optional telemetry provider (tracer + meter). Wire this to the
|
|
649
|
+
* OpenTelemetry adapter with `createOtelTelemetry` from the
|
|
650
|
+
* `@bajustone/fortress/otel` sub-path, or provide any custom
|
|
651
|
+
* implementation. Defaults to a zero-alloc no-op provider.
|
|
652
|
+
*/
|
|
653
|
+
observability?: TelemetryProvider;
|
|
654
|
+
}
|
|
655
|
+
/** Validated cookie attributes resolved independently of `NODE_ENV`. */
|
|
656
|
+
interface ResolvedCookieConfig {
|
|
657
|
+
accessName: string;
|
|
658
|
+
refreshName: string;
|
|
659
|
+
sameSite: 'lax' | 'strict' | 'none';
|
|
660
|
+
secure: boolean;
|
|
661
|
+
domain?: string;
|
|
662
|
+
path: string;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
export { type AfterHookContext as A, type PluginRequestContext as B, type ComponentSchemas as C, type PostAuthGateContext as D, type EndpointDefinition as E, type FortressPlugin as F, type PostAuthGateDecision as G, type HttpMethod as H, type InferEndpointParams as I, type JSONSchema as J, type PostAuthGateProvider as K, type PostAuthGateVerificationContext as L, type MiddlewareDefinition as M, type SessionConfig as N, type EndpointPermission as O, type PasswordHasher as P, type ResolvedCookieConfig as R, StandardSchemaV1 as S, type InferEndpointQuery as a, type InferEndpointBody as b, type InferEndpointCallInput as c, type InferEndpointResponses as d, type PluginContext as e, type PluginRouteContext as f, type SecurityRequirement as g, type FortressSchema as h, type Infer as i, type Simplify as j, type EndpointInput as k, type EndpointMeta as l, type EndpointResponse as m, type FieldDefinition as n, type FortressConfig as o, type HookContext as p, type HookResult as q, type InferEndpointSuccessResponse as r, type ModelConstraint as s, type ModelDefinition as t, type PasswordBreachCheckOptions as u, type PasswordBreachDegradedEvent as v, type PasswordBreachFailureMode as w, type PasswordPolicyConfig as x, type PasswordPolicyObserver as y, type PluginHooks as z };
|