@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,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A fortress user record as returned by the database adapter.
|
|
3
|
+
*
|
|
4
|
+
* `id` is always a string at the fortress API surface. Adapters backed by
|
|
5
|
+
* numeric primary keys are responsible for stringifying on read and parsing
|
|
6
|
+
* on write at the adapter boundary — consumers of fortress never see the
|
|
7
|
+
* underlying representation. This matches the JWT/OIDC `sub` wire shape
|
|
8
|
+
* (RFC 7519 §4.1.2) and unblocks UUID/ULID/snowflake-keyed adapters.
|
|
9
|
+
*/
|
|
10
|
+
interface FortressUser {
|
|
11
|
+
id: string;
|
|
12
|
+
email: string;
|
|
13
|
+
name: string;
|
|
14
|
+
isActive: boolean;
|
|
15
|
+
emailVerified?: boolean;
|
|
16
|
+
createdAt: Date;
|
|
17
|
+
updatedAt: Date;
|
|
18
|
+
}
|
|
19
|
+
/** JWT claims fortress signs into the access token, plus the optional impersonation actor (`act`) and per-deployment custom claims. */
|
|
20
|
+
interface TokenClaims {
|
|
21
|
+
/** Subject identifier — string per RFC 7519 §4.1.2. */
|
|
22
|
+
sub: string;
|
|
23
|
+
/** Subject kind — defaults to 'USER' on legacy tokens that do not carry the claim. */
|
|
24
|
+
subjectType: SubjectType;
|
|
25
|
+
name: string;
|
|
26
|
+
groups: string[];
|
|
27
|
+
iss: string;
|
|
28
|
+
/** Optional JWT audience, when configured for this deployment. */
|
|
29
|
+
aud?: string | string[];
|
|
30
|
+
iat: number;
|
|
31
|
+
exp: number;
|
|
32
|
+
act?: {
|
|
33
|
+
sub: string;
|
|
34
|
+
subjectType?: SubjectType;
|
|
35
|
+
};
|
|
36
|
+
customClaims?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
/** A short-lived access token paired with its longer-lived refresh token. */
|
|
39
|
+
interface AuthTokenPair {
|
|
40
|
+
accessToken: string;
|
|
41
|
+
refreshToken: string;
|
|
42
|
+
}
|
|
43
|
+
/** The credential method that completed authentication. Carried on a successful result. */
|
|
44
|
+
type AuthMethod = 'password' | 'refresh' | 'two-factor' | 'webauthn' | 'magic-link' | 'impersonation';
|
|
45
|
+
/** Why a sign-in is pending an additional step before tokens are issued. */
|
|
46
|
+
type PendingReason = 'two-factor' | 'webauthn' | 'email-verification' | 'magic-link';
|
|
47
|
+
/**
|
|
48
|
+
* The challenge a pending sign-in must satisfy before tokens are issued.
|
|
49
|
+
* `reason` tells the client which step to run; the single-use `continuationToken`
|
|
50
|
+
* is presented back to `auth.completePendingAuth` to finish the flow.
|
|
51
|
+
*/
|
|
52
|
+
interface AuthChallenge {
|
|
53
|
+
reason: PendingReason;
|
|
54
|
+
continuationToken: string;
|
|
55
|
+
}
|
|
56
|
+
/** Successful sign-in / refresh result — both tokens are issued. */
|
|
57
|
+
interface AuthSuccess {
|
|
58
|
+
status: 'success';
|
|
59
|
+
user: FortressUser;
|
|
60
|
+
method: AuthMethod;
|
|
61
|
+
accessToken: string;
|
|
62
|
+
refreshToken: string;
|
|
63
|
+
pluginData?: Record<string, unknown>;
|
|
64
|
+
}
|
|
65
|
+
/** Impersonation sign-in result — only an access token is issued (refresh is suppressed for safety). */
|
|
66
|
+
interface AuthImpersonation {
|
|
67
|
+
status: 'impersonation';
|
|
68
|
+
user: FortressUser;
|
|
69
|
+
accessToken: string;
|
|
70
|
+
refreshToken: null;
|
|
71
|
+
pluginData?: Record<string, unknown>;
|
|
72
|
+
}
|
|
73
|
+
/** Pending sign-in result — the user must complete an additional step before tokens are issued. */
|
|
74
|
+
interface AuthPending {
|
|
75
|
+
status: 'pending';
|
|
76
|
+
user: FortressUser;
|
|
77
|
+
pending: AuthChallenge;
|
|
78
|
+
pluginData?: Record<string, unknown>;
|
|
79
|
+
}
|
|
80
|
+
/** Discriminated union of every possible auth flow outcome. */
|
|
81
|
+
type AuthResult = AuthSuccess | AuthImpersonation | AuthPending;
|
|
82
|
+
/** Narrow an {@link AuthResult} to the successful (tokens-issued) variant. */
|
|
83
|
+
declare function isSuccess(result: AuthResult): result is AuthSuccess;
|
|
84
|
+
/** Narrow an {@link AuthResult} to the pending (additional-step-required) variant. */
|
|
85
|
+
declare function isPending(result: AuthResult): result is AuthPending;
|
|
86
|
+
/** Narrow an {@link AuthResult} to the impersonation variant. */
|
|
87
|
+
declare function isImpersonation(result: AuthResult): result is AuthImpersonation;
|
|
88
|
+
/**
|
|
89
|
+
* Assert an {@link AuthResult} is successful, throwing otherwise. Use when a
|
|
90
|
+
* caller has already ruled out the pending/impersonation variants and wants the
|
|
91
|
+
* token fields without a manual narrow.
|
|
92
|
+
*/
|
|
93
|
+
declare function assertSuccess(result: AuthResult): asserts result is AuthSuccess;
|
|
94
|
+
/** Per-request metadata fortress threads through hooks for audit logging, lockout, and trusted-device tracking. */
|
|
95
|
+
interface RequestMeta {
|
|
96
|
+
ipAddress?: string;
|
|
97
|
+
userAgent?: string;
|
|
98
|
+
deviceName?: string;
|
|
99
|
+
/** Raw server-issued trusted-device secret from the host-owned cookie. */
|
|
100
|
+
trustedDeviceToken?: string;
|
|
101
|
+
/** Explicitly request enrollment of this browser/device after a successful factor. */
|
|
102
|
+
rememberDevice?: boolean;
|
|
103
|
+
}
|
|
104
|
+
/** A persisted refresh-token session as exposed to session-management UIs. */
|
|
105
|
+
interface SessionInfo {
|
|
106
|
+
id: string;
|
|
107
|
+
ipAddress: string | null;
|
|
108
|
+
userAgent: string | null;
|
|
109
|
+
deviceName: string | null;
|
|
110
|
+
createdAt: Date;
|
|
111
|
+
lastActiveAt: Date | null;
|
|
112
|
+
}
|
|
113
|
+
/** Input shape accepted by `fortress.createUser` and the admin user-creation endpoint. */
|
|
114
|
+
interface CreateUserInput {
|
|
115
|
+
email: string;
|
|
116
|
+
name: string;
|
|
117
|
+
password?: string;
|
|
118
|
+
isActive?: boolean;
|
|
119
|
+
}
|
|
120
|
+
/** The kind of subject a role or permission can be bound to. */
|
|
121
|
+
type SubjectType = 'USER' | 'GROUP' | 'SERVICE_ACCOUNT';
|
|
122
|
+
/** A subject identity — a discriminated (type, id) pair used by IAM and the auth pipeline. */
|
|
123
|
+
interface Subject {
|
|
124
|
+
type: SubjectType;
|
|
125
|
+
id: string;
|
|
126
|
+
}
|
|
127
|
+
/** A persisted IAM permission — a (resource, action) pair with optional conditions and an allow/deny effect. */
|
|
128
|
+
interface Permission {
|
|
129
|
+
id: string;
|
|
130
|
+
resource: string;
|
|
131
|
+
action: string;
|
|
132
|
+
effect: 'ALLOW' | 'DENY';
|
|
133
|
+
conditions?: PermissionCondition[];
|
|
134
|
+
description?: string;
|
|
135
|
+
}
|
|
136
|
+
/** Input shape for creating a new {@link Permission}. */
|
|
137
|
+
interface PermissionInput {
|
|
138
|
+
resource: string;
|
|
139
|
+
action: string;
|
|
140
|
+
effect?: 'ALLOW' | 'DENY';
|
|
141
|
+
conditions?: PermissionCondition[];
|
|
142
|
+
}
|
|
143
|
+
/** Reference to a value resolved from the {@link PermissionContext} at evaluation time. */
|
|
144
|
+
interface ConditionRef {
|
|
145
|
+
ref: string;
|
|
146
|
+
}
|
|
147
|
+
/** A literal or {@link ConditionRef}-based value used by a {@link PermissionCondition}. */
|
|
148
|
+
type ConditionValue = string | string[] | ConditionRef | ConditionRef[];
|
|
149
|
+
/** A single ABAC-style condition guarding a {@link Permission}. */
|
|
150
|
+
interface PermissionCondition {
|
|
151
|
+
field: string;
|
|
152
|
+
operator: 'eq' | 'neq' | 'in' | 'startsWith';
|
|
153
|
+
value: ConditionValue;
|
|
154
|
+
}
|
|
155
|
+
/** Context object passed into the permission evaluator at check time. */
|
|
156
|
+
interface PermissionContext {
|
|
157
|
+
resource?: Record<string, unknown>;
|
|
158
|
+
request?: Record<string, unknown>;
|
|
159
|
+
user?: Record<string, unknown>;
|
|
160
|
+
tenantId?: string;
|
|
161
|
+
/**
|
|
162
|
+
* Optional narrowing scopes from the credential used for this request
|
|
163
|
+
* (for example an API key). `null`/`undefined` means unscoped/full
|
|
164
|
+
* subject permissions; an empty array means no permissions.
|
|
165
|
+
*/
|
|
166
|
+
credentialScopes?: string[] | null;
|
|
167
|
+
}
|
|
168
|
+
/** A persisted IAM role grouping a set of {@link Permission}s. */
|
|
169
|
+
interface Role {
|
|
170
|
+
id: string;
|
|
171
|
+
name: string;
|
|
172
|
+
description?: string | null;
|
|
173
|
+
isSystem?: boolean;
|
|
174
|
+
}
|
|
175
|
+
/** Binding of a {@link Role} to a subject (user, group, or service account), optionally scoped to a tenant. */
|
|
176
|
+
interface RoleBinding {
|
|
177
|
+
id: string;
|
|
178
|
+
roleId: string;
|
|
179
|
+
subjectType: SubjectType;
|
|
180
|
+
subjectId: string;
|
|
181
|
+
tenantId?: string | null;
|
|
182
|
+
}
|
|
183
|
+
/** The kind of identifier a user can sign in with. */
|
|
184
|
+
type LoginIdentifierType = 'email' | 'phone' | 'username';
|
|
185
|
+
/** A persisted login identifier (e.g. email, phone, username) attached to a user. */
|
|
186
|
+
interface LoginIdentifier {
|
|
187
|
+
id: string;
|
|
188
|
+
userId: string;
|
|
189
|
+
type: LoginIdentifierType;
|
|
190
|
+
value: string;
|
|
191
|
+
/** Reserved for future multi-tenant support. Not yet stored in the database. */
|
|
192
|
+
tenantId?: string | null;
|
|
193
|
+
}
|
|
194
|
+
/** A persisted IAM group used to grant permissions to many users at once. */
|
|
195
|
+
interface Group {
|
|
196
|
+
id: string;
|
|
197
|
+
name: string;
|
|
198
|
+
description?: string;
|
|
199
|
+
}
|
|
200
|
+
/** A persisted IAM service account — a non-human principal that can hold roles and direct permissions. */
|
|
201
|
+
interface ServiceAccount {
|
|
202
|
+
id: string;
|
|
203
|
+
name: string;
|
|
204
|
+
displayName: string | null;
|
|
205
|
+
description: string | null;
|
|
206
|
+
isActive: boolean;
|
|
207
|
+
createdAt: Date;
|
|
208
|
+
updatedAt: Date;
|
|
209
|
+
}
|
|
210
|
+
/** Input shape for creating a new {@link ServiceAccount}. `name` is the immutable machine identifier. */
|
|
211
|
+
interface CreateServiceAccountInput {
|
|
212
|
+
name: string;
|
|
213
|
+
displayName?: string;
|
|
214
|
+
description?: string;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export { type AuthResult as A, type CreateServiceAccountInput as C, type FortressUser as F, type Group as G, type LoginIdentifierType as L, type PermissionContext as P, type RequestMeta as R, type Subject as S, type TokenClaims as T, type SubjectType as a, type Permission as b, type PermissionInput as c, type Role as d, type RoleBinding as e, type ServiceAccount as f, type AuthMethod as g, type AuthTokenPair as h, type CreateUserInput as i, type SessionInfo as j, type LoginIdentifier as k, type PendingReason as l, type AuthChallenge as m, type AuthImpersonation as n, type AuthPending as o, type AuthSuccess as p, type ConditionRef as q, type ConditionValue as r, type PermissionCondition as s, assertSuccess as t, isImpersonation as u, isPending as v, isSuccess as w };
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A fortress user record as returned by the database adapter.
|
|
3
|
+
*
|
|
4
|
+
* `id` is always a string at the fortress API surface. Adapters backed by
|
|
5
|
+
* numeric primary keys are responsible for stringifying on read and parsing
|
|
6
|
+
* on write at the adapter boundary — consumers of fortress never see the
|
|
7
|
+
* underlying representation. This matches the JWT/OIDC `sub` wire shape
|
|
8
|
+
* (RFC 7519 §4.1.2) and unblocks UUID/ULID/snowflake-keyed adapters.
|
|
9
|
+
*/
|
|
10
|
+
interface FortressUser {
|
|
11
|
+
id: string;
|
|
12
|
+
email: string;
|
|
13
|
+
name: string;
|
|
14
|
+
isActive: boolean;
|
|
15
|
+
emailVerified?: boolean;
|
|
16
|
+
createdAt: Date;
|
|
17
|
+
updatedAt: Date;
|
|
18
|
+
}
|
|
19
|
+
/** JWT claims fortress signs into the access token, plus the optional impersonation actor (`act`) and per-deployment custom claims. */
|
|
20
|
+
interface TokenClaims {
|
|
21
|
+
/** Subject identifier — string per RFC 7519 §4.1.2. */
|
|
22
|
+
sub: string;
|
|
23
|
+
/** Subject kind — defaults to 'USER' on legacy tokens that do not carry the claim. */
|
|
24
|
+
subjectType: SubjectType;
|
|
25
|
+
name: string;
|
|
26
|
+
groups: string[];
|
|
27
|
+
iss: string;
|
|
28
|
+
/** Optional JWT audience, when configured for this deployment. */
|
|
29
|
+
aud?: string | string[];
|
|
30
|
+
iat: number;
|
|
31
|
+
exp: number;
|
|
32
|
+
act?: {
|
|
33
|
+
sub: string;
|
|
34
|
+
subjectType?: SubjectType;
|
|
35
|
+
};
|
|
36
|
+
customClaims?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
/** A short-lived access token paired with its longer-lived refresh token. */
|
|
39
|
+
interface AuthTokenPair {
|
|
40
|
+
accessToken: string;
|
|
41
|
+
refreshToken: string;
|
|
42
|
+
}
|
|
43
|
+
/** The credential method that completed authentication. Carried on a successful result. */
|
|
44
|
+
type AuthMethod = 'password' | 'refresh' | 'two-factor' | 'webauthn' | 'magic-link' | 'impersonation';
|
|
45
|
+
/** Why a sign-in is pending an additional step before tokens are issued. */
|
|
46
|
+
type PendingReason = 'two-factor' | 'webauthn' | 'email-verification' | 'magic-link';
|
|
47
|
+
/**
|
|
48
|
+
* The challenge a pending sign-in must satisfy before tokens are issued.
|
|
49
|
+
* `reason` tells the client which step to run; the single-use `continuationToken`
|
|
50
|
+
* is presented back to `auth.completePendingAuth` to finish the flow.
|
|
51
|
+
*/
|
|
52
|
+
interface AuthChallenge {
|
|
53
|
+
reason: PendingReason;
|
|
54
|
+
continuationToken: string;
|
|
55
|
+
}
|
|
56
|
+
/** Successful sign-in / refresh result — both tokens are issued. */
|
|
57
|
+
interface AuthSuccess {
|
|
58
|
+
status: 'success';
|
|
59
|
+
user: FortressUser;
|
|
60
|
+
method: AuthMethod;
|
|
61
|
+
accessToken: string;
|
|
62
|
+
refreshToken: string;
|
|
63
|
+
pluginData?: Record<string, unknown>;
|
|
64
|
+
}
|
|
65
|
+
/** Impersonation sign-in result — only an access token is issued (refresh is suppressed for safety). */
|
|
66
|
+
interface AuthImpersonation {
|
|
67
|
+
status: 'impersonation';
|
|
68
|
+
user: FortressUser;
|
|
69
|
+
accessToken: string;
|
|
70
|
+
refreshToken: null;
|
|
71
|
+
pluginData?: Record<string, unknown>;
|
|
72
|
+
}
|
|
73
|
+
/** Pending sign-in result — the user must complete an additional step before tokens are issued. */
|
|
74
|
+
interface AuthPending {
|
|
75
|
+
status: 'pending';
|
|
76
|
+
user: FortressUser;
|
|
77
|
+
pending: AuthChallenge;
|
|
78
|
+
pluginData?: Record<string, unknown>;
|
|
79
|
+
}
|
|
80
|
+
/** Discriminated union of every possible auth flow outcome. */
|
|
81
|
+
type AuthResult = AuthSuccess | AuthImpersonation | AuthPending;
|
|
82
|
+
/** Narrow an {@link AuthResult} to the successful (tokens-issued) variant. */
|
|
83
|
+
declare function isSuccess(result: AuthResult): result is AuthSuccess;
|
|
84
|
+
/** Narrow an {@link AuthResult} to the pending (additional-step-required) variant. */
|
|
85
|
+
declare function isPending(result: AuthResult): result is AuthPending;
|
|
86
|
+
/** Narrow an {@link AuthResult} to the impersonation variant. */
|
|
87
|
+
declare function isImpersonation(result: AuthResult): result is AuthImpersonation;
|
|
88
|
+
/**
|
|
89
|
+
* Assert an {@link AuthResult} is successful, throwing otherwise. Use when a
|
|
90
|
+
* caller has already ruled out the pending/impersonation variants and wants the
|
|
91
|
+
* token fields without a manual narrow.
|
|
92
|
+
*/
|
|
93
|
+
declare function assertSuccess(result: AuthResult): asserts result is AuthSuccess;
|
|
94
|
+
/** Per-request metadata fortress threads through hooks for audit logging, lockout, and trusted-device tracking. */
|
|
95
|
+
interface RequestMeta {
|
|
96
|
+
ipAddress?: string;
|
|
97
|
+
userAgent?: string;
|
|
98
|
+
deviceName?: string;
|
|
99
|
+
/** Raw server-issued trusted-device secret from the host-owned cookie. */
|
|
100
|
+
trustedDeviceToken?: string;
|
|
101
|
+
/** Explicitly request enrollment of this browser/device after a successful factor. */
|
|
102
|
+
rememberDevice?: boolean;
|
|
103
|
+
}
|
|
104
|
+
/** A persisted refresh-token session as exposed to session-management UIs. */
|
|
105
|
+
interface SessionInfo {
|
|
106
|
+
id: string;
|
|
107
|
+
ipAddress: string | null;
|
|
108
|
+
userAgent: string | null;
|
|
109
|
+
deviceName: string | null;
|
|
110
|
+
createdAt: Date;
|
|
111
|
+
lastActiveAt: Date | null;
|
|
112
|
+
}
|
|
113
|
+
/** Input shape accepted by `fortress.createUser` and the admin user-creation endpoint. */
|
|
114
|
+
interface CreateUserInput {
|
|
115
|
+
email: string;
|
|
116
|
+
name: string;
|
|
117
|
+
password?: string;
|
|
118
|
+
isActive?: boolean;
|
|
119
|
+
}
|
|
120
|
+
/** The kind of subject a role or permission can be bound to. */
|
|
121
|
+
type SubjectType = 'USER' | 'GROUP' | 'SERVICE_ACCOUNT';
|
|
122
|
+
/** A subject identity — a discriminated (type, id) pair used by IAM and the auth pipeline. */
|
|
123
|
+
interface Subject {
|
|
124
|
+
type: SubjectType;
|
|
125
|
+
id: string;
|
|
126
|
+
}
|
|
127
|
+
/** A persisted IAM permission — a (resource, action) pair with optional conditions and an allow/deny effect. */
|
|
128
|
+
interface Permission {
|
|
129
|
+
id: string;
|
|
130
|
+
resource: string;
|
|
131
|
+
action: string;
|
|
132
|
+
effect: 'ALLOW' | 'DENY';
|
|
133
|
+
conditions?: PermissionCondition[];
|
|
134
|
+
description?: string;
|
|
135
|
+
}
|
|
136
|
+
/** Input shape for creating a new {@link Permission}. */
|
|
137
|
+
interface PermissionInput {
|
|
138
|
+
resource: string;
|
|
139
|
+
action: string;
|
|
140
|
+
effect?: 'ALLOW' | 'DENY';
|
|
141
|
+
conditions?: PermissionCondition[];
|
|
142
|
+
}
|
|
143
|
+
/** Reference to a value resolved from the {@link PermissionContext} at evaluation time. */
|
|
144
|
+
interface ConditionRef {
|
|
145
|
+
ref: string;
|
|
146
|
+
}
|
|
147
|
+
/** A literal or {@link ConditionRef}-based value used by a {@link PermissionCondition}. */
|
|
148
|
+
type ConditionValue = string | string[] | ConditionRef | ConditionRef[];
|
|
149
|
+
/** A single ABAC-style condition guarding a {@link Permission}. */
|
|
150
|
+
interface PermissionCondition {
|
|
151
|
+
field: string;
|
|
152
|
+
operator: 'eq' | 'neq' | 'in' | 'startsWith';
|
|
153
|
+
value: ConditionValue;
|
|
154
|
+
}
|
|
155
|
+
/** Context object passed into the permission evaluator at check time. */
|
|
156
|
+
interface PermissionContext {
|
|
157
|
+
resource?: Record<string, unknown>;
|
|
158
|
+
request?: Record<string, unknown>;
|
|
159
|
+
user?: Record<string, unknown>;
|
|
160
|
+
tenantId?: string;
|
|
161
|
+
/**
|
|
162
|
+
* Optional narrowing scopes from the credential used for this request
|
|
163
|
+
* (for example an API key). `null`/`undefined` means unscoped/full
|
|
164
|
+
* subject permissions; an empty array means no permissions.
|
|
165
|
+
*/
|
|
166
|
+
credentialScopes?: string[] | null;
|
|
167
|
+
}
|
|
168
|
+
/** A persisted IAM role grouping a set of {@link Permission}s. */
|
|
169
|
+
interface Role {
|
|
170
|
+
id: string;
|
|
171
|
+
name: string;
|
|
172
|
+
description?: string | null;
|
|
173
|
+
isSystem?: boolean;
|
|
174
|
+
}
|
|
175
|
+
/** Binding of a {@link Role} to a subject (user, group, or service account), optionally scoped to a tenant. */
|
|
176
|
+
interface RoleBinding {
|
|
177
|
+
id: string;
|
|
178
|
+
roleId: string;
|
|
179
|
+
subjectType: SubjectType;
|
|
180
|
+
subjectId: string;
|
|
181
|
+
tenantId?: string | null;
|
|
182
|
+
}
|
|
183
|
+
/** The kind of identifier a user can sign in with. */
|
|
184
|
+
type LoginIdentifierType = 'email' | 'phone' | 'username';
|
|
185
|
+
/** A persisted login identifier (e.g. email, phone, username) attached to a user. */
|
|
186
|
+
interface LoginIdentifier {
|
|
187
|
+
id: string;
|
|
188
|
+
userId: string;
|
|
189
|
+
type: LoginIdentifierType;
|
|
190
|
+
value: string;
|
|
191
|
+
/** Reserved for future multi-tenant support. Not yet stored in the database. */
|
|
192
|
+
tenantId?: string | null;
|
|
193
|
+
}
|
|
194
|
+
/** A persisted IAM group used to grant permissions to many users at once. */
|
|
195
|
+
interface Group {
|
|
196
|
+
id: string;
|
|
197
|
+
name: string;
|
|
198
|
+
description?: string;
|
|
199
|
+
}
|
|
200
|
+
/** A persisted IAM service account — a non-human principal that can hold roles and direct permissions. */
|
|
201
|
+
interface ServiceAccount {
|
|
202
|
+
id: string;
|
|
203
|
+
name: string;
|
|
204
|
+
displayName: string | null;
|
|
205
|
+
description: string | null;
|
|
206
|
+
isActive: boolean;
|
|
207
|
+
createdAt: Date;
|
|
208
|
+
updatedAt: Date;
|
|
209
|
+
}
|
|
210
|
+
/** Input shape for creating a new {@link ServiceAccount}. `name` is the immutable machine identifier. */
|
|
211
|
+
interface CreateServiceAccountInput {
|
|
212
|
+
name: string;
|
|
213
|
+
displayName?: string;
|
|
214
|
+
description?: string;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export { type AuthResult as A, type CreateServiceAccountInput as C, type FortressUser as F, type Group as G, type LoginIdentifierType as L, type PermissionContext as P, type RequestMeta as R, type Subject as S, type TokenClaims as T, type SubjectType as a, type Permission as b, type PermissionInput as c, type Role as d, type RoleBinding as e, type ServiceAccount as f, type AuthMethod as g, type AuthTokenPair as h, type CreateUserInput as i, type SessionInfo as j, type LoginIdentifier as k, type PendingReason as l, type AuthChallenge as m, type AuthImpersonation as n, type AuthPending as o, type AuthSuccess as p, type ConditionRef as q, type ConditionValue as r, type PermissionCondition as s, assertSuccess as t, isImpersonation as u, isPending as v, isSuccess as w };
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Typed adapter helpers
|
|
2
|
+
|
|
3
|
+
Fortress's framework adapters (Hono, Express, SvelteKit) attach
|
|
4
|
+
request-scoped state (`fortressSubject`, `fortressUserId`,
|
|
5
|
+
`fortressClaims`, `fortressDb`, `fortressGetScopedDb`,
|
|
6
|
+
`fortressScopes`). This page covers how to read that state from your own
|
|
7
|
+
handlers with **no casts**, while keeping your own env/locals/request
|
|
8
|
+
augmentations intact.
|
|
9
|
+
|
|
10
|
+
The plugin-method surface (`fortress.plugins.<name>.method(...)`) is
|
|
11
|
+
already typed via `InferPlugins<T>` — the const generic plugin list
|
|
12
|
+
passed to `createFortress` carries each plugin's method-shape through to
|
|
13
|
+
`fortress.plugins`. Nothing extra to do.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Hono — `FortressEnv<TAppEnv>`
|
|
18
|
+
|
|
19
|
+
The Hono adapter exports a generic env type that composes your own
|
|
20
|
+
`Variables` and `Bindings` with Fortress's. Use the default
|
|
21
|
+
`FortressEnv` for Fortress-only apps; parameterize it with your env for
|
|
22
|
+
apps that already declare their own variables.
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { Hono } from 'hono';
|
|
26
|
+
import {
|
|
27
|
+
FortressEnv,
|
|
28
|
+
createHonoMiddleware,
|
|
29
|
+
getClaims,
|
|
30
|
+
getSubject,
|
|
31
|
+
getUserId,
|
|
32
|
+
} from '@bajustone/fortress/hono';
|
|
33
|
+
|
|
34
|
+
interface MyEnv {
|
|
35
|
+
Variables: { requestId: string };
|
|
36
|
+
Bindings: { DB: D1Database };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const app = new Hono<FortressEnv<MyEnv>>();
|
|
40
|
+
const { authMiddleware, errorHandler } = createHonoMiddleware(fortress);
|
|
41
|
+
app.use(authMiddleware);
|
|
42
|
+
app.onError(errorHandler);
|
|
43
|
+
|
|
44
|
+
app.get('/me', (c) => {
|
|
45
|
+
const requestId = c.get('requestId'); // string (host-defined)
|
|
46
|
+
const subject = getSubject(c); // Subject (fortress-defined)
|
|
47
|
+
const userId = getUserId(c); // string (USER-only)
|
|
48
|
+
return c.json({ requestId, subject, userId });
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Typed custom JWT claims
|
|
53
|
+
|
|
54
|
+
`getClaims<TCustomClaims>(c)` narrows the `customClaims` slot to your
|
|
55
|
+
deployment's plugin-augmented shape. The tenancy plugin, for example,
|
|
56
|
+
adds `tenantId` / `tenantCode`:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
interface MyClaims {
|
|
60
|
+
tenantId: string;
|
|
61
|
+
tenantCode: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
app.get('/iam/whoami', (c) => {
|
|
65
|
+
const claims = getClaims<MyClaims>(c);
|
|
66
|
+
const tenantCode = claims.customClaims?.tenantCode; // string | undefined
|
|
67
|
+
return c.json({ sub: claims.sub, tenantCode });
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Exports
|
|
72
|
+
|
|
73
|
+
| Export | Kind | Purpose |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| `FortressEnv<TAppEnv>` | type | Generic env composing your env with Fortress's variables |
|
|
76
|
+
| `FortressVariables` | type | The bare Fortress `Variables` slot — useful for `Hono<{ Variables: FortressVariables & MyVars }>` |
|
|
77
|
+
| `FortressContext<E>` | type | Sugar for `Context<E>` typed handlers |
|
|
78
|
+
| `getSubject<E>(c)` | function | Returns `Subject`; throws 401 if unauthenticated |
|
|
79
|
+
| `getUserId<E>(c)` | function | Returns `string`; throws 401 if subject is not a `USER` |
|
|
80
|
+
| `getClaims<T, E>(c)` | function | Returns `TokenClaims & { customClaims?: T }` |
|
|
81
|
+
| `getDb<E>(c)` | function | Per-request `DatabaseAdapter` with plugin `wrapAdapter` applied |
|
|
82
|
+
| `getScopedDb<E>(c, model)` | function | Adds `scopeRules` for `model` on top of `getDb` |
|
|
83
|
+
|
|
84
|
+
All helpers are generic in the env type so they accept any
|
|
85
|
+
`Context<E>` whose `Variables` include `FortressVariables`.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Express — `FortressExpressFields`
|
|
90
|
+
|
|
91
|
+
The Express adapter exports the Fortress-specific fields it attaches to
|
|
92
|
+
the `Request` object so you can **declaration-merge** them into express's
|
|
93
|
+
native `Request` type. Once merged, your route handlers see the typed
|
|
94
|
+
fields with no casts.
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
// src/types/express.d.ts
|
|
98
|
+
import type { FortressExpressFields } from '@bajustone/fortress/express';
|
|
99
|
+
|
|
100
|
+
declare module 'express-serve-static-core' {
|
|
101
|
+
interface Request extends FortressExpressFields {}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
// src/routes/me.ts
|
|
107
|
+
import { Router } from 'express';
|
|
108
|
+
import { createAuthMiddleware } from '@bajustone/fortress/express';
|
|
109
|
+
|
|
110
|
+
const router = Router();
|
|
111
|
+
router.use(createAuthMiddleware(fortress));
|
|
112
|
+
|
|
113
|
+
router.get('/me', (req, res) => {
|
|
114
|
+
const subject = req.fortressSubject; // Subject | undefined (typed)
|
|
115
|
+
const userId = req.fortressUserId; // string | undefined
|
|
116
|
+
const claims = req.fortressClaims; // TokenClaims | undefined
|
|
117
|
+
res.json({ subject, userId, claims });
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`@bajustone/fortress/express` also exports the helper functions
|
|
122
|
+
(`getSubject(req)`, `getUserId(req)`, `getClaims(req)`, `getDb(req)`,
|
|
123
|
+
`getScopedDb(req, model)`) — they're equivalent to reading the fields
|
|
124
|
+
directly, but throw a typed 401 when the field is missing instead of
|
|
125
|
+
returning `undefined`.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## SvelteKit — `FortressLocals`
|
|
130
|
+
|
|
131
|
+
The SvelteKit adapter exposes `event.locals.fortress` (subject, userId,
|
|
132
|
+
claims, scopes, db, getScopedDb). Augment SvelteKit's `App.Locals` once
|
|
133
|
+
and your `+server.ts` / `+page.server.ts` handlers see the typed shape
|
|
134
|
+
everywhere.
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
// src/app.d.ts
|
|
138
|
+
import type { FortressLocals } from '@bajustone/fortress/sveltekit';
|
|
139
|
+
|
|
140
|
+
declare global {
|
|
141
|
+
namespace App {
|
|
142
|
+
interface Locals extends FortressLocals {}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
// src/routes/api/me/+server.ts
|
|
149
|
+
import { json } from '@sveltejs/kit';
|
|
150
|
+
import { getSubject } from '@bajustone/fortress/sveltekit';
|
|
151
|
+
|
|
152
|
+
export const GET = ({ locals }) => {
|
|
153
|
+
const subject = locals.fortress?.subject; // Subject | undefined (typed)
|
|
154
|
+
return json({ subject });
|
|
155
|
+
};
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`FortressLocals` already extends Fortress's variables under the
|
|
159
|
+
`fortress` key, so multiple library augmentations of `App.Locals` (e.g.
|
|
160
|
+
your own session library + Fortress) coexist without colliding.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Typed plugin methods
|
|
165
|
+
|
|
166
|
+
`fortress.plugins.<name>` is automatically typed from the plugin's
|
|
167
|
+
`methods` return type, propagated through the const generic plugin
|
|
168
|
+
list:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import { createFortress } from '@bajustone/fortress';
|
|
172
|
+
import { tenancy } from '@bajustone/fortress/plugins/tenancy';
|
|
173
|
+
import { apiKey } from '@bajustone/fortress/plugins/api-key';
|
|
174
|
+
|
|
175
|
+
const fortress = createFortress({
|
|
176
|
+
database: db,
|
|
177
|
+
jwt: { key: secret },
|
|
178
|
+
plugins: [tenancy(), apiKey({ prefix: 'fortress' })] as const,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// All typed; no casts.
|
|
182
|
+
await fortress.plugins.tenancy.createTenant({ name: 'Acme', taxId: 'acme-001' });
|
|
183
|
+
await fortress.plugins['api-key'].createKey({
|
|
184
|
+
subject: { type: 'USER', id: userId },
|
|
185
|
+
name: 'CI',
|
|
186
|
+
scopes: ['*'],
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
For dynamic plugin lookup (e.g. a plugin loaded by name at runtime),
|
|
191
|
+
use `getPluginMethods<T>(fortress, name)` to attach a known interface
|
|
192
|
+
without casting.
|