@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,402 @@
|
|
|
1
|
+
# Admin Plugin
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The `admin` plugin provides a complete set of endpoints for managing Fortress's own resources: users, roles, groups, permissions, role bindings, permission bindings, and resource sync. The first-admin bootstrap endpoint is opt-in and protected by a one-time secret.
|
|
6
|
+
|
|
7
|
+
All admin endpoints are protected by `fortress:*` permissions enforced through the RBAC middleware. Until a user has been bootstrapped with the `fortress-admin` role, no one can access these routes. There is no ambient superadmin bypass.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Import the `admin` factory and pass it in the `plugins` array when creating a Fortress instance:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createFortress } from '@bajustone/fortress';
|
|
15
|
+
import { admin } from '@bajustone/fortress/plugins/admin';
|
|
16
|
+
|
|
17
|
+
const fortress = createFortress({
|
|
18
|
+
jwt: { key: 'your-secret-at-least-32-bytes!!' },
|
|
19
|
+
database: adapter,
|
|
20
|
+
plugins: [
|
|
21
|
+
admin({ bootstrap: { enabled: true, secret: process.env.FORTRESS_ADMIN_BOOTSTRAP_SECRET } }),
|
|
22
|
+
],
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Configuration
|
|
27
|
+
|
|
28
|
+
All fields on `AdminPluginOptions` are optional:
|
|
29
|
+
|
|
30
|
+
| Option | Type | Default | Description |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| `resource` | `string` | `'fortress'` | Resource name used in permission declarations. Change this if `fortress` conflicts with your application's resource naming. |
|
|
33
|
+
| `bootstrap` | `{ enabled: boolean; secret?: string }` | disabled | Mount the one-time first-admin bootstrap route. `secret` defaults to `FORTRESS_ADMIN_BOOTSTRAP_SECRET`. |
|
|
34
|
+
| `apiKeyRoutes` | `boolean` | `false` | Mount the admin-side api-key management endpoints under `/admin/users/:userId/api-keys/*` and `/admin/service-accounts/:id/api-keys/*`. Requires the `api-key` plugin to also be registered. See [API Key Management](#api-key-management) below. |
|
|
35
|
+
|
|
36
|
+
## Bootstrap
|
|
37
|
+
|
|
38
|
+
Before any admin endpoints can be used, you must bootstrap the first admin user. The bootstrap route is **not mounted by default**; enable it only during setup. It creates the `fortress-admin` role, registers all required permissions (auto-discovered from endpoint definitions across all plugins), and binds them to the specified user.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
// Via the plugin method or HTTP, while bootstrap.enabled is true.
|
|
42
|
+
await fortress.plugins.admin.bootstrap({
|
|
43
|
+
userId: '1',
|
|
44
|
+
secret: process.env.FORTRESS_ADMIN_BOOTSTRAP_SECRET!,
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Or via HTTP:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
POST /iam/admin/bootstrap
|
|
52
|
+
Authorization: Bearer <access-token>
|
|
53
|
+
Content-Type: application/json
|
|
54
|
+
|
|
55
|
+
{ "userId": "1", "secret": "<one-time-secret>" }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Bootstrap succeeds only while zero `fortress-admin` bindings exist and the supplied secret matches the configured one-time secret. It cannot be used to re-bootstrap.
|
|
59
|
+
|
|
60
|
+
### What bootstrap does
|
|
61
|
+
|
|
62
|
+
1. Verifies the target user exists.
|
|
63
|
+
2. Scans all registered endpoint definitions (core auth, core IAM, and all plugins) to discover every declared `permission`.
|
|
64
|
+
3. Creates the corresponding `resource` and `permission` records if they do not already exist.
|
|
65
|
+
4. Creates the `fortress-admin` role (system role, cannot be updated or deleted via normal role endpoints).
|
|
66
|
+
5. Links every discovered permission to the `fortress-admin` role.
|
|
67
|
+
6. Binds the role to the specified user.
|
|
68
|
+
|
|
69
|
+
## Usage
|
|
70
|
+
|
|
71
|
+
### User management
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
// List users with pagination and search
|
|
75
|
+
const { users, total } = await fortress.plugins.admin.listUsers({
|
|
76
|
+
limit: 20,
|
|
77
|
+
offset: 0,
|
|
78
|
+
search: 'alice',
|
|
79
|
+
sortBy: 'id',
|
|
80
|
+
sortDirection: 'desc',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Get a single user
|
|
84
|
+
const user = await fortress.plugins.admin.getUserById({ id: '42' });
|
|
85
|
+
|
|
86
|
+
// Update a user
|
|
87
|
+
await fortress.plugins.admin.updateUser({ id: '42', name: 'New Name', isActive: false });
|
|
88
|
+
|
|
89
|
+
// Delete a user
|
|
90
|
+
await fortress.plugins.admin.deleteUser({ id: '42' });
|
|
91
|
+
|
|
92
|
+
// Create a user (admin-initiated, requires fortress:manageUsers)
|
|
93
|
+
const newUser = await fortress.plugins.admin.createUser({
|
|
94
|
+
email: 'alice@example.com',
|
|
95
|
+
name: 'Alice',
|
|
96
|
+
password: 'SecurePass123!',
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Role management
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
// List all roles
|
|
104
|
+
const roles = await fortress.plugins.admin.getRoles();
|
|
105
|
+
|
|
106
|
+
// Get role with its permissions
|
|
107
|
+
const role = await fortress.plugins.admin.getRole({ id: '1' });
|
|
108
|
+
// role.permissions => Permission[]
|
|
109
|
+
|
|
110
|
+
// Create a role
|
|
111
|
+
const newRole = await fortress.plugins.admin.createRole({
|
|
112
|
+
name: 'editor',
|
|
113
|
+
permissions: [{ resource: 'post', action: 'publish' }],
|
|
114
|
+
description: 'Content editors',
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Update role name/description
|
|
118
|
+
await fortress.plugins.admin.updateRole({ id: '1', name: 'editor', description: 'Content editors' });
|
|
119
|
+
|
|
120
|
+
// Delete a role
|
|
121
|
+
await fortress.plugins.admin.deleteRole({ id: '1' });
|
|
122
|
+
|
|
123
|
+
// Add a permission to a role
|
|
124
|
+
await fortress.plugins.admin.addPermissionToRole({ id: '1', resource: 'post', action: 'publish' });
|
|
125
|
+
|
|
126
|
+
// Bind a role to a user
|
|
127
|
+
await fortress.plugins.admin.bindRoleToUser({ id: '1', userId: '42' });
|
|
128
|
+
|
|
129
|
+
// Bind a role to a group
|
|
130
|
+
await fortress.plugins.admin.bindRoleToGroup({ id: '1', groupId: '5' });
|
|
131
|
+
|
|
132
|
+
// Unbind a role
|
|
133
|
+
await fortress.plugins.admin.unbindRole({ id: '1', subjectType: 'USER', subjectId: '42' });
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
System roles (like `fortress-admin`) cannot be updated or deleted through the admin endpoints.
|
|
137
|
+
|
|
138
|
+
### Group management
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
// List groups
|
|
142
|
+
const { groups, total } = await fortress.plugins.admin.listGroups({ limit: 20 });
|
|
143
|
+
|
|
144
|
+
// Create a group
|
|
145
|
+
const group = await fortress.plugins.admin.createGroup({ name: 'devs', description: 'Developer team' });
|
|
146
|
+
|
|
147
|
+
// Get group with members
|
|
148
|
+
const detail = await fortress.plugins.admin.getGroup({ id: '5' });
|
|
149
|
+
// detail.users => FortressUser[]
|
|
150
|
+
|
|
151
|
+
// Update group
|
|
152
|
+
await fortress.plugins.admin.updateGroup({ id: '5', name: 'devs', description: 'Developer team' });
|
|
153
|
+
|
|
154
|
+
// Delete group
|
|
155
|
+
await fortress.plugins.admin.deleteGroup({ id: '5' });
|
|
156
|
+
|
|
157
|
+
// List group members separately
|
|
158
|
+
const members = await fortress.plugins.admin.getGroupUsers({ id: '5' });
|
|
159
|
+
|
|
160
|
+
// Add/remove users from groups
|
|
161
|
+
await fortress.plugins.admin.addUserToGroup({ id: '5', userId: '42' });
|
|
162
|
+
await fortress.plugins.admin.removeUserFromGroup({ id: '5', userId: '42' });
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Permission management
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
// List all permissions, optionally filtered by resource
|
|
169
|
+
const perms = await fortress.plugins.admin.listPermissions({ resource: 'post' });
|
|
170
|
+
|
|
171
|
+
// Create a new permission
|
|
172
|
+
const perm = await fortress.plugins.admin.createPermission({
|
|
173
|
+
resource: 'invoice',
|
|
174
|
+
action: 'create',
|
|
175
|
+
effect: 'ALLOW',
|
|
176
|
+
description: 'Allow creating invoices',
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Delete a permission
|
|
180
|
+
await fortress.plugins.admin.deletePermission({ id: '99' });
|
|
181
|
+
|
|
182
|
+
// Get user permissions
|
|
183
|
+
const userPerms = await fortress.plugins.admin.getUserPermissions({ id: '42' });
|
|
184
|
+
|
|
185
|
+
// Check a specific permission
|
|
186
|
+
const { allowed } = await fortress.plugins.admin.checkPermission({
|
|
187
|
+
userId: '42', resource: 'post', action: 'publish',
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Bind/unbind permissions directly to users or groups
|
|
191
|
+
await fortress.plugins.admin.bindPermissionToUser({ userId: '42', permission: { resource: 'post', action: 'publish' } });
|
|
192
|
+
await fortress.plugins.admin.bindPermissionToGroup({ groupId: '5', permission: { resource: 'post', action: 'publish' } });
|
|
193
|
+
await fortress.plugins.admin.unbindPermissionFromUser({ userId: '42', permissionId: '99' });
|
|
194
|
+
await fortress.plugins.admin.unbindPermissionFromGroup({ groupId: '5', permissionId: '99' });
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Resource sync
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
// Pull resources from database into file
|
|
201
|
+
await fortress.plugins.admin.syncResources({ direction: 'pull' });
|
|
202
|
+
|
|
203
|
+
// Push resources from file into database
|
|
204
|
+
await fortress.plugins.admin.syncResources({ direction: 'push', filePath: './custom-resources.json' });
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Service account management
|
|
208
|
+
|
|
209
|
+
Service accounts are non-human IAM principals used for CI/CD, M2M, devices, etc. The admin plugin proxies the core `/iam/service-accounts/*` endpoints (CRUD + role bindings + direct permission bindings) so they run through the admin plugin's permission gating when the admin plugin is registered alongside core fortress.
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
// Create a service account
|
|
213
|
+
const sa = await fortress.plugins.admin.createServiceAccount({
|
|
214
|
+
name: 'ci-deploy',
|
|
215
|
+
displayName: 'CI Deploy',
|
|
216
|
+
description: 'Runs production deploys from GitHub Actions',
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// List / get / update / delete
|
|
220
|
+
const { serviceAccounts, total } = await fortress.plugins.admin.listServiceAccounts({ limit: 20 });
|
|
221
|
+
const found = await fortress.plugins.admin.getServiceAccount({ id: sa.id });
|
|
222
|
+
await fortress.plugins.admin.updateServiceAccount({ id: sa.id, isActive: false });
|
|
223
|
+
await fortress.plugins.admin.deleteServiceAccount({ id: sa.id });
|
|
224
|
+
|
|
225
|
+
// Bind / unbind roles and direct permissions (same shape as the user variants)
|
|
226
|
+
await fortress.plugins.admin.bindRoleToServiceAccount({ serviceAccountId: sa.id, id: roleId });
|
|
227
|
+
await fortress.plugins.admin.bindPermissionToServiceAccount({
|
|
228
|
+
serviceAccountId: sa.id,
|
|
229
|
+
permission: { resource: 'deploy', action: 'run' },
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
See the [IAM service account docs](../../README.md#use-service-accounts) for the conceptual overview and `checkPermission({ type: 'SERVICE_ACCOUNT', id }, ...)` examples.
|
|
234
|
+
|
|
235
|
+
### API key management
|
|
236
|
+
|
|
237
|
+
When `apiKeyRoutes: true` is passed to the admin plugin (and the `api-key` plugin is also registered), six admin HTTP routes are mounted under `/admin/users/:userId/api-keys/*` and `/admin/service-accounts/:id/api-keys/*`. All are guarded by the `apiKey:manage` permission, which bootstrap auto-registers into the `fortress-admin` role when the flag is on.
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { createFortress } from '@bajustone/fortress';
|
|
241
|
+
import { apiKey } from '@bajustone/fortress/plugins/api-key';
|
|
242
|
+
import { admin } from '@bajustone/fortress/plugins/admin';
|
|
243
|
+
|
|
244
|
+
const fortress = createFortress({
|
|
245
|
+
jwt: { key: '...' },
|
|
246
|
+
database: adapter,
|
|
247
|
+
plugins: [
|
|
248
|
+
apiKey({ prefix: 'myapp' }),
|
|
249
|
+
admin({ apiKeyRoutes: true }),
|
|
250
|
+
],
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Bootstrapping a service account's first credential.** A fresh service account has no login path — it can't self-mint. The `POST /admin/service-accounts/:id/api-keys` endpoint is the only supported way to issue its initial key:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
curl -X POST http://localhost:3000/admin/service-accounts/42/api-keys \
|
|
258
|
+
-H "Authorization: Bearer $ADMIN_JWT" \
|
|
259
|
+
-H "Content-Type: application/json" \
|
|
260
|
+
-d '{"name":"ci-deploy-github-actions"}'
|
|
261
|
+
# Returns { "key": "myapp_sk_...", "id": 7 } — store the raw key immediately.
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
From then on, the service account authenticates incoming requests with `Authorization: ApiKey myapp_sk_...` or `X-API-Key: myapp_sk_...` — the api-key plugin's `resolvePrincipal` hook turns the header into a subject principal and RBAC flows through as usual.
|
|
265
|
+
|
|
266
|
+
Admin-minted keys respect the same configured knobs (`prefix`, `maxKeysPerSubject`, `defaultExpirySeconds`) as self-service keys — the admin plugin re-enters the api-key plugin's `methods(ctx)` factory to mint, so there's no duplicate config.
|
|
267
|
+
|
|
268
|
+
## Endpoints
|
|
269
|
+
|
|
270
|
+
All endpoints require bearer authentication and the corresponding `fortress:*` permission. The bootstrap endpoint only requires authentication (no permission check).
|
|
271
|
+
|
|
272
|
+
### Bootstrap
|
|
273
|
+
|
|
274
|
+
| Method | Path | Permission |
|
|
275
|
+
|---|---|---|
|
|
276
|
+
| POST | `/iam/admin/bootstrap` | *(authenticated only)* |
|
|
277
|
+
|
|
278
|
+
### User endpoints
|
|
279
|
+
|
|
280
|
+
| Method | Path | Permission |
|
|
281
|
+
|---|---|---|
|
|
282
|
+
| GET | `/auth/users` | `fortress:viewUsers` |
|
|
283
|
+
| GET | `/auth/users/:id` | `fortress:viewUsers` |
|
|
284
|
+
| POST | `/auth/users` | `fortress:manageUsers` |
|
|
285
|
+
| PUT | `/auth/users/:id` | `fortress:manageUsers` |
|
|
286
|
+
| DELETE | `/auth/users/:id` | `fortress:manageUsers` |
|
|
287
|
+
|
|
288
|
+
### Role endpoints
|
|
289
|
+
|
|
290
|
+
| Method | Path | Permission |
|
|
291
|
+
|---|---|---|
|
|
292
|
+
| GET | `/iam/roles` | `fortress:viewRoles` |
|
|
293
|
+
| GET | `/iam/roles/:id` | `fortress:viewRoles` |
|
|
294
|
+
| POST | `/iam/roles` | `fortress:createRole` |
|
|
295
|
+
| PUT | `/iam/roles/:id` | `fortress:manageRoles` |
|
|
296
|
+
| DELETE | `/iam/roles/:id` | `fortress:deleteRole` |
|
|
297
|
+
| POST | `/iam/roles/:id/permissions` | `fortress:manageRoles` |
|
|
298
|
+
| POST | `/iam/roles/:id/bind/user` | `fortress:bindRole` |
|
|
299
|
+
| POST | `/iam/roles/:id/bind/group` | `fortress:bindRole` |
|
|
300
|
+
| DELETE | `/iam/roles/:id/bind` | `fortress:unbindRole` |
|
|
301
|
+
|
|
302
|
+
### Group endpoints
|
|
303
|
+
|
|
304
|
+
| Method | Path | Permission |
|
|
305
|
+
|---|---|---|
|
|
306
|
+
| GET | `/iam/groups` | `fortress:viewGroups` |
|
|
307
|
+
| GET | `/iam/groups/:id` | `fortress:viewGroups` |
|
|
308
|
+
| POST | `/iam/groups` | `fortress:createGroup` |
|
|
309
|
+
| PUT | `/iam/groups/:id` | `fortress:manageGroup` |
|
|
310
|
+
| DELETE | `/iam/groups/:id` | `fortress:manageGroup` |
|
|
311
|
+
| GET | `/iam/groups/:id/users` | `fortress:viewGroups` |
|
|
312
|
+
| POST | `/iam/groups/:id/users` | `fortress:manageGroup` |
|
|
313
|
+
| DELETE | `/iam/groups/:id/users/:userId` | `fortress:manageGroup` |
|
|
314
|
+
|
|
315
|
+
### Permission endpoints
|
|
316
|
+
|
|
317
|
+
| Method | Path | Permission |
|
|
318
|
+
|---|---|---|
|
|
319
|
+
| GET | `/iam/permissions` | `fortress:viewPermissions` |
|
|
320
|
+
| POST | `/iam/permissions` | `fortress:managePermissions` |
|
|
321
|
+
| DELETE | `/iam/permissions/:id` | `fortress:managePermissions` |
|
|
322
|
+
| GET | `/iam/users/:id/permissions` | `fortress:viewPermissions` |
|
|
323
|
+
| POST | `/iam/check` | `fortress:viewPermissions` |
|
|
324
|
+
| POST | `/iam/permissions/bind/user` | `fortress:managePermissions` |
|
|
325
|
+
| POST | `/iam/permissions/bind/group` | `fortress:managePermissions` |
|
|
326
|
+
| DELETE | `/iam/permissions/bind/user` | `fortress:managePermissions` |
|
|
327
|
+
| DELETE | `/iam/permissions/bind/group` | `fortress:managePermissions` |
|
|
328
|
+
|
|
329
|
+
### Service account endpoints (core IAM, proxied through admin)
|
|
330
|
+
|
|
331
|
+
These come from core IAM — the admin plugin re-registers them on its own `routes` array so they dispatch through the admin plugin's permission gating.
|
|
332
|
+
|
|
333
|
+
| Method | Path | Permission |
|
|
334
|
+
|---|---|---|
|
|
335
|
+
| POST | `/iam/service-accounts` | `fortress:createServiceAccount` |
|
|
336
|
+
| GET | `/iam/service-accounts` | `fortress:viewServiceAccounts` |
|
|
337
|
+
| GET | `/iam/service-accounts/:id` | `fortress:viewServiceAccounts` |
|
|
338
|
+
| PATCH | `/iam/service-accounts/:id` | `fortress:manageServiceAccount` |
|
|
339
|
+
| DELETE | `/iam/service-accounts/:id` | `fortress:manageServiceAccount` |
|
|
340
|
+
| GET | `/iam/service-accounts/:id/permissions` | `fortress:viewPermissions` |
|
|
341
|
+
| POST | `/iam/roles/:id/bind/service-account` | `fortress:bindRole` |
|
|
342
|
+
| DELETE | `/iam/roles/:id/bind/service-account` | `fortress:unbindRole` |
|
|
343
|
+
| POST | `/iam/permissions/bind/service-account` | `fortress:managePermissions` |
|
|
344
|
+
| DELETE | `/iam/permissions/bind/service-account` | `fortress:managePermissions` |
|
|
345
|
+
|
|
346
|
+
### API key admin endpoints (opt-in via `apiKeyRoutes: true`)
|
|
347
|
+
|
|
348
|
+
Mounted only when `admin({ apiKeyRoutes: true })`. All six require the `apiKey:manage` permission, which bootstrap auto-registers. Requires the `api-key` plugin to also be registered.
|
|
349
|
+
|
|
350
|
+
| Method | Path | Purpose |
|
|
351
|
+
|---|---|---|
|
|
352
|
+
| POST | `/admin/users/:userId/api-keys` | Mint a key on behalf of any user. |
|
|
353
|
+
| GET | `/admin/users/:userId/api-keys` | List any user's non-revoked keys. |
|
|
354
|
+
| DELETE | `/admin/users/:userId/api-keys/:id` | Revoke any user's key (ownership is not enforced). |
|
|
355
|
+
| POST | `/admin/service-accounts/:id/api-keys` | Mint a key on behalf of a service account. **This is the only HTTP path for bootstrapping a fresh service account's first credential.** |
|
|
356
|
+
| GET | `/admin/service-accounts/:id/api-keys` | List a service account's non-revoked keys. |
|
|
357
|
+
| DELETE | `/admin/service-accounts/:id/api-keys/:keyId` | Revoke a service account's key. |
|
|
358
|
+
|
|
359
|
+
### Resource endpoints
|
|
360
|
+
|
|
361
|
+
| Method | Path | Permission |
|
|
362
|
+
|---|---|---|
|
|
363
|
+
| GET | `/iam/resources` | `fortress:viewResources` |
|
|
364
|
+
| POST | `/iam/sync` | `fortress:managePermissions` |
|
|
365
|
+
|
|
366
|
+
## Bootstrap Hardening
|
|
367
|
+
|
|
368
|
+
The bootstrap endpoint is intentionally not mounted unless `bootstrap.enabled` is true. A request must be authenticated and must include the configured one-time secret. The secret is compared in constant time, and a successful bootstrap permanently closes the path by creating the first `fortress-admin` binding.
|
|
369
|
+
|
|
370
|
+
For emergency access after bootstrap, grant or repair IAM role bindings out-of-band using a trusted migration/seed process with direct database access; the HTTP bootstrap route will not re-open.
|
|
371
|
+
|
|
372
|
+
## Permissions Reference
|
|
373
|
+
|
|
374
|
+
The admin plugin uses all fortress permissions declared across admin and core IAM endpoints:
|
|
375
|
+
|
|
376
|
+
| Permission | Used by |
|
|
377
|
+
|---|---|
|
|
378
|
+
| `fortress:viewUsers` | List users, get user by ID |
|
|
379
|
+
| `fortress:manageUsers` | Create user, update user, delete user |
|
|
380
|
+
| `fortress:viewRoles` | List roles, get role with permissions |
|
|
381
|
+
| `fortress:createRole` | Create role |
|
|
382
|
+
| `fortress:deleteRole` | Delete role |
|
|
383
|
+
| `fortress:manageRoles` | Update role, add permission to role |
|
|
384
|
+
| `fortress:bindRole` | Bind role to user, group, or service account |
|
|
385
|
+
| `fortress:unbindRole` | Unbind role |
|
|
386
|
+
| `fortress:viewGroups` | List groups, get group, list group members |
|
|
387
|
+
| `fortress:createGroup` | Create group |
|
|
388
|
+
| `fortress:manageGroup` | Update group, delete group, add/remove group members |
|
|
389
|
+
| `fortress:viewPermissions` | List permissions, get user/service-account permissions, check permission |
|
|
390
|
+
| `fortress:managePermissions` | Create/delete permissions, bind/unbind permissions, resource sync |
|
|
391
|
+
| `fortress:viewResources` | List available resources |
|
|
392
|
+
| `fortress:createServiceAccount` | Create a service account |
|
|
393
|
+
| `fortress:viewServiceAccounts` | List and get service accounts |
|
|
394
|
+
| `fortress:manageServiceAccount` | Update / delete a service account |
|
|
395
|
+
| `apiKey:manage` | *(only when `apiKeyRoutes: true`)* Admin mint/list/revoke of any user's or service account's api keys |
|
|
396
|
+
|
|
397
|
+
## How It Works
|
|
398
|
+
|
|
399
|
+
1. **Default deny** -- All admin endpoints declare a `permission` in their endpoint metadata. The RBAC middleware enforces these permissions automatically. Without the `fortress-admin` role, every request is denied.
|
|
400
|
+
2. **Auto-discovery** -- The bootstrap method scans all registered endpoint definitions (core + plugins) to discover every `{ resource, action }` pair. This means adding new plugins with permission-protected endpoints automatically includes those permissions in the bootstrap role.
|
|
401
|
+
3. **Delegation** -- Admin methods delegate to the core `auth` and `iam` services. The plugin does not duplicate business logic; it provides the permission-gated route layer on top of existing service methods.
|
|
402
|
+
4. **Bootstrap is one-time** -- The bootstrap route is opt-in, secret-gated, and refuses to run after any `fortress-admin` role binding exists.
|