@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,558 @@
|
|
|
1
|
+
import type { EndpointDefinition } from '../endpoint';
|
|
2
|
+
import type { FortressSchema } from '../json-schema';
|
|
3
|
+
import type { AuthMethod, PendingReason } from '../types';
|
|
4
|
+
import { arr, bool, defineComponents, email, endpoint, enums, id, int, nullable, nullType, obj, oneOf, record, str, strFormat } from '../schema-builder';
|
|
5
|
+
|
|
6
|
+
// Sentinel for "no body / query / params" that matches EndpointDefinition's
|
|
7
|
+
// defaults so the intersection-based InferEndpointCallInput collapses cleanly.
|
|
8
|
+
|
|
9
|
+
interface EmptyInput {}
|
|
10
|
+
|
|
11
|
+
// ── Wire-format shapes (what endpoint handlers serialize to JSON) ──────
|
|
12
|
+
//
|
|
13
|
+
// These mirror the domain types in `src/core/types.ts` but use `string`
|
|
14
|
+
// everywhere the domain uses `Date` (ISO 8601 on the wire). Declared here
|
|
15
|
+
// explicitly so JSR's fast-check doesn't walk through the deeply-inferred
|
|
16
|
+
// types produced by `obj(...)` / `oneOf(...)`.
|
|
17
|
+
|
|
18
|
+
/** Wire shape of a fortress user — domain `Date` fields are ISO strings on the wire. */
|
|
19
|
+
export interface UserWire {
|
|
20
|
+
id: string;
|
|
21
|
+
email: string;
|
|
22
|
+
name: string;
|
|
23
|
+
isActive: boolean;
|
|
24
|
+
emailVerified?: boolean;
|
|
25
|
+
createdAt: string;
|
|
26
|
+
updatedAt: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Wire shape of a pending post-auth challenge. */
|
|
30
|
+
export interface AuthChallengeWire {
|
|
31
|
+
reason: PendingReason;
|
|
32
|
+
continuationToken: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Wire shape of a successful sign-in. */
|
|
36
|
+
export interface AuthSuccessWire {
|
|
37
|
+
status: 'success';
|
|
38
|
+
user: UserWire;
|
|
39
|
+
method: AuthMethod;
|
|
40
|
+
accessToken: string;
|
|
41
|
+
refreshToken: string;
|
|
42
|
+
pluginData?: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Wire shape of a pending sign-in (2FA, email verification, etc.). */
|
|
46
|
+
export interface AuthPendingWire {
|
|
47
|
+
status: 'pending';
|
|
48
|
+
user: UserWire;
|
|
49
|
+
pending: AuthChallengeWire;
|
|
50
|
+
pluginData?: Record<string, unknown>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Wire shape of an impersonation sign-in (no refresh token). */
|
|
54
|
+
export interface AuthImpersonationWire {
|
|
55
|
+
status: 'impersonation';
|
|
56
|
+
user: UserWire;
|
|
57
|
+
accessToken: string;
|
|
58
|
+
refreshToken: null;
|
|
59
|
+
pluginData?: Record<string, unknown>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Discriminated union of every auth-flow wire outcome. */
|
|
63
|
+
export type AuthResultWire
|
|
64
|
+
= | AuthSuccessWire
|
|
65
|
+
| AuthPendingWire
|
|
66
|
+
| AuthImpersonationWire;
|
|
67
|
+
|
|
68
|
+
/** Wire shape of a refreshed access/refresh token pair. */
|
|
69
|
+
export interface AuthTokenPairWire {
|
|
70
|
+
accessToken: string;
|
|
71
|
+
refreshToken: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Wire shape of a persisted refresh-token session. */
|
|
75
|
+
export interface SessionInfoWire {
|
|
76
|
+
id: string;
|
|
77
|
+
ipAddress?: string | null;
|
|
78
|
+
userAgent?: string | null;
|
|
79
|
+
deviceName?: string | null;
|
|
80
|
+
createdAt: string;
|
|
81
|
+
lastActiveAt?: string | null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Wire shape of the create-user input body. */
|
|
85
|
+
export interface CreateUserInputWire {
|
|
86
|
+
email: string;
|
|
87
|
+
name: string;
|
|
88
|
+
password?: string;
|
|
89
|
+
isActive?: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Wire shape of a structured error response. */
|
|
93
|
+
export interface ErrorResponseWire {
|
|
94
|
+
code: string;
|
|
95
|
+
message: string;
|
|
96
|
+
statusCode: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Wire shape of a login identifier. */
|
|
100
|
+
export interface LoginIdentifierWire {
|
|
101
|
+
id: string;
|
|
102
|
+
userId: string;
|
|
103
|
+
type: 'email' | 'phone' | 'username';
|
|
104
|
+
value: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Empty `{ ok: boolean }` ack. */
|
|
108
|
+
export interface OkResponseWire {
|
|
109
|
+
ok: boolean;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ── Component schemas (typed registry) ──────────────────────────────────
|
|
113
|
+
|
|
114
|
+
const User: FortressSchema<UserWire> = obj(
|
|
115
|
+
{
|
|
116
|
+
id: id('User ID'),
|
|
117
|
+
email: strFormat('email', 'User email'),
|
|
118
|
+
name: str('Display name'),
|
|
119
|
+
isActive: bool('Account active status'),
|
|
120
|
+
emailVerified: bool('Email verification status'),
|
|
121
|
+
createdAt: strFormat('date-time', 'Creation timestamp'),
|
|
122
|
+
updatedAt: strFormat('date-time', 'Last update timestamp'),
|
|
123
|
+
},
|
|
124
|
+
'id',
|
|
125
|
+
'email',
|
|
126
|
+
'name',
|
|
127
|
+
'isActive',
|
|
128
|
+
'createdAt',
|
|
129
|
+
'updatedAt',
|
|
130
|
+
) as FortressSchema<UserWire>;
|
|
131
|
+
|
|
132
|
+
const AuthChallenge: FortressSchema<AuthChallengeWire> = obj(
|
|
133
|
+
{
|
|
134
|
+
reason: enums('two-factor', 'webauthn', 'email-verification', 'magic-link'),
|
|
135
|
+
continuationToken: str('Single-use post-auth continuation token'),
|
|
136
|
+
},
|
|
137
|
+
'reason',
|
|
138
|
+
'continuationToken',
|
|
139
|
+
) as FortressSchema<AuthChallengeWire>;
|
|
140
|
+
|
|
141
|
+
const AuthResult: FortressSchema<AuthResultWire> = oneOf(
|
|
142
|
+
obj(
|
|
143
|
+
{
|
|
144
|
+
status: enums('success'),
|
|
145
|
+
user: User,
|
|
146
|
+
method: enums('password', 'refresh', 'two-factor', 'webauthn', 'magic-link', 'impersonation'),
|
|
147
|
+
accessToken: str('JWT access token'),
|
|
148
|
+
refreshToken: str('Refresh token for rotation'),
|
|
149
|
+
pluginData: record(),
|
|
150
|
+
},
|
|
151
|
+
'status',
|
|
152
|
+
'user',
|
|
153
|
+
'method',
|
|
154
|
+
'accessToken',
|
|
155
|
+
'refreshToken',
|
|
156
|
+
),
|
|
157
|
+
obj(
|
|
158
|
+
{
|
|
159
|
+
status: enums('pending'),
|
|
160
|
+
user: User,
|
|
161
|
+
pending: AuthChallenge,
|
|
162
|
+
pluginData: record(),
|
|
163
|
+
},
|
|
164
|
+
'status',
|
|
165
|
+
'user',
|
|
166
|
+
'pending',
|
|
167
|
+
),
|
|
168
|
+
obj(
|
|
169
|
+
{
|
|
170
|
+
status: enums('impersonation'),
|
|
171
|
+
user: User,
|
|
172
|
+
accessToken: str('JWT access token'),
|
|
173
|
+
refreshToken: nullType(),
|
|
174
|
+
pluginData: record(),
|
|
175
|
+
},
|
|
176
|
+
'status',
|
|
177
|
+
'user',
|
|
178
|
+
'accessToken',
|
|
179
|
+
'refreshToken',
|
|
180
|
+
),
|
|
181
|
+
) as FortressSchema<AuthResultWire>;
|
|
182
|
+
|
|
183
|
+
const AuthTokenPair: FortressSchema<AuthTokenPairWire> = obj(
|
|
184
|
+
{
|
|
185
|
+
accessToken: str('JWT access token'),
|
|
186
|
+
refreshToken: str('New refresh token'),
|
|
187
|
+
},
|
|
188
|
+
'accessToken',
|
|
189
|
+
'refreshToken',
|
|
190
|
+
) as FortressSchema<AuthTokenPairWire>;
|
|
191
|
+
|
|
192
|
+
const SessionInfo: FortressSchema<SessionInfoWire> = obj(
|
|
193
|
+
{
|
|
194
|
+
id: id('Token ID'),
|
|
195
|
+
ipAddress: nullable(str('Client IP address')),
|
|
196
|
+
userAgent: nullable(str('Client user agent')),
|
|
197
|
+
deviceName: nullable(str('Device name')),
|
|
198
|
+
createdAt: strFormat('date-time', 'Session creation time'),
|
|
199
|
+
lastActiveAt: nullable(strFormat('date-time', 'Last activity time')),
|
|
200
|
+
},
|
|
201
|
+
'id',
|
|
202
|
+
'createdAt',
|
|
203
|
+
) as FortressSchema<SessionInfoWire>;
|
|
204
|
+
|
|
205
|
+
const CreateUserInput: FortressSchema<CreateUserInputWire> = obj(
|
|
206
|
+
{
|
|
207
|
+
email: email('User email'), // enforced format (ReDoS-safe pattern), not annotation-only
|
|
208
|
+
name: str('Display name'),
|
|
209
|
+
password: str('User password'),
|
|
210
|
+
isActive: bool('Set active status (default true)'),
|
|
211
|
+
},
|
|
212
|
+
'email',
|
|
213
|
+
'name',
|
|
214
|
+
) as FortressSchema<CreateUserInputWire>;
|
|
215
|
+
|
|
216
|
+
const ErrorResponse: FortressSchema<ErrorResponseWire> = obj(
|
|
217
|
+
{
|
|
218
|
+
code: str('Error code (e.g. UNAUTHORIZED)'),
|
|
219
|
+
message: str('Human-readable message'),
|
|
220
|
+
statusCode: int('HTTP status code'),
|
|
221
|
+
},
|
|
222
|
+
'code',
|
|
223
|
+
'message',
|
|
224
|
+
'statusCode',
|
|
225
|
+
) as FortressSchema<ErrorResponseWire>;
|
|
226
|
+
|
|
227
|
+
const LoginIdentifier: FortressSchema<LoginIdentifierWire> = obj(
|
|
228
|
+
{
|
|
229
|
+
id: id('Identifier ID'),
|
|
230
|
+
userId: id('User ID'),
|
|
231
|
+
type: enums('email', 'phone', 'username'),
|
|
232
|
+
value: str('Identifier value'),
|
|
233
|
+
},
|
|
234
|
+
'id',
|
|
235
|
+
'userId',
|
|
236
|
+
'type',
|
|
237
|
+
'value',
|
|
238
|
+
) as FortressSchema<LoginIdentifierWire>;
|
|
239
|
+
|
|
240
|
+
/** Explicit registry type so JSR's fast-check doesn't walk into the deeply-inferred `defineComponents` return. */
|
|
241
|
+
interface AuthComponents {
|
|
242
|
+
readonly components: {
|
|
243
|
+
readonly User: FortressSchema<UserWire>;
|
|
244
|
+
readonly AuthChallenge: FortressSchema<AuthChallengeWire>;
|
|
245
|
+
readonly AuthResult: FortressSchema<AuthResultWire>;
|
|
246
|
+
readonly AuthTokenPair: FortressSchema<AuthTokenPairWire>;
|
|
247
|
+
readonly SessionInfo: FortressSchema<SessionInfoWire>;
|
|
248
|
+
readonly CreateUserInput: FortressSchema<CreateUserInputWire>;
|
|
249
|
+
readonly ErrorResponse: FortressSchema<ErrorResponseWire>;
|
|
250
|
+
readonly LoginIdentifier: FortressSchema<LoginIdentifierWire>;
|
|
251
|
+
};
|
|
252
|
+
readonly ref: <K extends keyof AuthComponents['components']>(
|
|
253
|
+
name: K,
|
|
254
|
+
) => FortressSchema<AuthComponents['components'][K] extends FortressSchema<infer U> ? U : never>;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const authComponents: AuthComponents = defineComponents({
|
|
258
|
+
User,
|
|
259
|
+
AuthChallenge,
|
|
260
|
+
AuthResult,
|
|
261
|
+
AuthTokenPair,
|
|
262
|
+
SessionInfo,
|
|
263
|
+
CreateUserInput,
|
|
264
|
+
ErrorResponse,
|
|
265
|
+
LoginIdentifier,
|
|
266
|
+
}) as AuthComponents;
|
|
267
|
+
|
|
268
|
+
/** Reusable OpenAPI component schemas referenced by the core auth endpoints. */
|
|
269
|
+
export const authComponentSchemas: AuthComponents['components'] = authComponents.components;
|
|
270
|
+
|
|
271
|
+
/** Typed `$ref` helper bound to {@link authComponentSchemas}. */
|
|
272
|
+
export const authRef: AuthComponents['ref'] = authComponents.ref;
|
|
273
|
+
|
|
274
|
+
// ── Auth endpoint definitions (keyed by handler name) ───────────────────
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Typed record of every core auth endpoint. Declared explicitly (not
|
|
278
|
+
* inferred from the builder) so JSR's fast-check passes without
|
|
279
|
+
* `--allow-slow-types`, while `InferEndpointCallInput<typeof authEndpoints.login>`
|
|
280
|
+
* and friends still resolve to the precise per-endpoint shapes.
|
|
281
|
+
*/
|
|
282
|
+
export interface AuthEndpointsMap {
|
|
283
|
+
login: EndpointDefinition<
|
|
284
|
+
{ identifier: string; password: string; trustedDeviceToken?: string },
|
|
285
|
+
EmptyInput,
|
|
286
|
+
EmptyInput,
|
|
287
|
+
{ 200: AuthResultWire; 401: ErrorResponseWire }
|
|
288
|
+
>;
|
|
289
|
+
verifyTwoFactor: EndpointDefinition<
|
|
290
|
+
{ continuationToken: string; code: string; rememberDevice?: boolean },
|
|
291
|
+
EmptyInput,
|
|
292
|
+
EmptyInput,
|
|
293
|
+
{ 200: AuthResultWire; 400: ErrorResponseWire; 401: ErrorResponseWire }
|
|
294
|
+
>;
|
|
295
|
+
verifyMagicLink: EndpointDefinition<
|
|
296
|
+
{ token: string },
|
|
297
|
+
EmptyInput,
|
|
298
|
+
EmptyInput,
|
|
299
|
+
{ 200: AuthResultWire; 400: ErrorResponseWire; 404: ErrorResponseWire }
|
|
300
|
+
>;
|
|
301
|
+
createUser: EndpointDefinition<
|
|
302
|
+
CreateUserInputWire,
|
|
303
|
+
EmptyInput,
|
|
304
|
+
EmptyInput,
|
|
305
|
+
{ 201: UserWire; 409: ErrorResponseWire }
|
|
306
|
+
>;
|
|
307
|
+
refresh: EndpointDefinition<
|
|
308
|
+
{ refreshToken: string },
|
|
309
|
+
EmptyInput,
|
|
310
|
+
EmptyInput,
|
|
311
|
+
{ 200: AuthTokenPairWire; 401: ErrorResponseWire }
|
|
312
|
+
>;
|
|
313
|
+
logout: EndpointDefinition<
|
|
314
|
+
{ refreshToken: string },
|
|
315
|
+
EmptyInput,
|
|
316
|
+
EmptyInput,
|
|
317
|
+
{ 200: OkResponseWire }
|
|
318
|
+
>;
|
|
319
|
+
me: EndpointDefinition<
|
|
320
|
+
EmptyInput,
|
|
321
|
+
EmptyInput,
|
|
322
|
+
EmptyInput,
|
|
323
|
+
{ 200: UserWire; 401: ErrorResponseWire }
|
|
324
|
+
>;
|
|
325
|
+
listSessions: EndpointDefinition<
|
|
326
|
+
EmptyInput,
|
|
327
|
+
EmptyInput,
|
|
328
|
+
EmptyInput,
|
|
329
|
+
{ 200: SessionInfoWire[]; 401: ErrorResponseWire }
|
|
330
|
+
>;
|
|
331
|
+
revokeSession: EndpointDefinition<
|
|
332
|
+
EmptyInput,
|
|
333
|
+
EmptyInput,
|
|
334
|
+
{ id: string },
|
|
335
|
+
{ 200: OkResponseWire; 401: ErrorResponseWire }
|
|
336
|
+
>;
|
|
337
|
+
revokeAllOtherSessions: EndpointDefinition<
|
|
338
|
+
{ currentTokenId: string },
|
|
339
|
+
EmptyInput,
|
|
340
|
+
EmptyInput,
|
|
341
|
+
{ 200: OkResponseWire; 401: ErrorResponseWire }
|
|
342
|
+
>;
|
|
343
|
+
addLoginIdentifier: EndpointDefinition<
|
|
344
|
+
{ type: 'email' | 'phone' | 'username'; value: string },
|
|
345
|
+
EmptyInput,
|
|
346
|
+
EmptyInput,
|
|
347
|
+
{ 200: OkResponseWire; 401: ErrorResponseWire }
|
|
348
|
+
>;
|
|
349
|
+
removeLoginIdentifier: EndpointDefinition<
|
|
350
|
+
{ type: 'email' | 'phone' | 'username'; value: string },
|
|
351
|
+
EmptyInput,
|
|
352
|
+
EmptyInput,
|
|
353
|
+
{ 200: OkResponseWire; 401: ErrorResponseWire }
|
|
354
|
+
>;
|
|
355
|
+
getLoginIdentifiers: EndpointDefinition<
|
|
356
|
+
EmptyInput,
|
|
357
|
+
EmptyInput,
|
|
358
|
+
EmptyInput,
|
|
359
|
+
{ 200: LoginIdentifierWire[]; 401: ErrorResponseWire }
|
|
360
|
+
>;
|
|
361
|
+
impersonate: EndpointDefinition<
|
|
362
|
+
{ targetUserId: string; reason?: string; expirySeconds?: number },
|
|
363
|
+
EmptyInput,
|
|
364
|
+
EmptyInput,
|
|
365
|
+
{ 200: AuthResultWire; 401: ErrorResponseWire; 404: ErrorResponseWire }
|
|
366
|
+
>;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Declarative endpoint definitions for fortress's built-in auth routes
|
|
371
|
+
* (sign in, refresh, sessions, impersonation).
|
|
372
|
+
*
|
|
373
|
+
* The explicit `AuthEndpointsMap` annotation is what keeps JSR fast-check
|
|
374
|
+
* happy — each entry's `EndpointDefinition<TBody, TQuery, TParams, TResponses>`
|
|
375
|
+
* generics are stated declaratively, not inferred from the builder chain.
|
|
376
|
+
* `fortress.call.*` type inference still resolves because `typeof
|
|
377
|
+
* authEndpoints.login` picks up the exact generics declared on the map.
|
|
378
|
+
*/
|
|
379
|
+
export const authEndpoints: AuthEndpointsMap = {
|
|
380
|
+
login: endpoint('POST', '/auth/login')
|
|
381
|
+
.summary('Login with credentials')
|
|
382
|
+
.tags('Auth')
|
|
383
|
+
.security('none')
|
|
384
|
+
.body(obj(
|
|
385
|
+
{
|
|
386
|
+
identifier: str('Email, username, or phone'),
|
|
387
|
+
password: str('User password'),
|
|
388
|
+
trustedDeviceToken: str('Opaque trusted-device token previously returned after two-factor verification'),
|
|
389
|
+
},
|
|
390
|
+
'identifier',
|
|
391
|
+
'password',
|
|
392
|
+
))
|
|
393
|
+
.response(200, 'Login successful', authRef('AuthResult'))
|
|
394
|
+
.response(401, 'Invalid credentials', authRef('ErrorResponse'))
|
|
395
|
+
.handler('login')
|
|
396
|
+
.build() as AuthEndpointsMap['login'],
|
|
397
|
+
|
|
398
|
+
verifyTwoFactor: endpoint('POST', '/auth/2fa/verify')
|
|
399
|
+
.summary('Complete a pending two-factor challenge')
|
|
400
|
+
.tags('Auth', 'Two-Factor')
|
|
401
|
+
.security('none')
|
|
402
|
+
.body(obj(
|
|
403
|
+
{
|
|
404
|
+
continuationToken: str('Single-use auth continuation token'),
|
|
405
|
+
code: str('TOTP or backup code'),
|
|
406
|
+
rememberDevice: bool('Issue an opaque trusted-device token after successful verification'),
|
|
407
|
+
},
|
|
408
|
+
'continuationToken',
|
|
409
|
+
'code',
|
|
410
|
+
))
|
|
411
|
+
.response(200, 'Authentication result', authRef('AuthResult'))
|
|
412
|
+
.response(400, 'Two-factor plugin unavailable', authRef('ErrorResponse'))
|
|
413
|
+
.response(401, 'Invalid continuation or verification code', authRef('ErrorResponse'))
|
|
414
|
+
.handler('verifyTwoFactor')
|
|
415
|
+
.build() as AuthEndpointsMap['verifyTwoFactor'],
|
|
416
|
+
|
|
417
|
+
verifyMagicLink: endpoint('POST', '/auth/magic-link/verify')
|
|
418
|
+
.summary('Verify a magic-link token')
|
|
419
|
+
.tags('Auth', 'Magic Link')
|
|
420
|
+
.security('none')
|
|
421
|
+
.body(obj({ token: str('Raw magic-link token') }, 'token'))
|
|
422
|
+
.response(200, 'Authentication result', authRef('AuthResult'))
|
|
423
|
+
.response(400, 'Magic-link plugin unavailable', authRef('ErrorResponse'))
|
|
424
|
+
.response(404, 'Invalid or expired magic link', authRef('ErrorResponse'))
|
|
425
|
+
.handler('verifyMagicLink')
|
|
426
|
+
.build() as AuthEndpointsMap['verifyMagicLink'],
|
|
427
|
+
|
|
428
|
+
createUser: endpoint('POST', '/auth/register')
|
|
429
|
+
.summary('Create a new user')
|
|
430
|
+
.tags('Auth')
|
|
431
|
+
.security('none')
|
|
432
|
+
.body(CreateUserInput)
|
|
433
|
+
.response(201, 'User created', authRef('User'))
|
|
434
|
+
.response(409, 'Email already exists', authRef('ErrorResponse'))
|
|
435
|
+
.handler('createUser')
|
|
436
|
+
.build() as AuthEndpointsMap['createUser'],
|
|
437
|
+
|
|
438
|
+
refresh: endpoint('POST', '/auth/refresh')
|
|
439
|
+
.summary('Refresh access token')
|
|
440
|
+
.tags('Auth')
|
|
441
|
+
.security('none')
|
|
442
|
+
.body(obj({ refreshToken: str('Current refresh token') }, 'refreshToken'))
|
|
443
|
+
.response(200, 'Tokens refreshed', authRef('AuthTokenPair'))
|
|
444
|
+
.response(401, 'Invalid or expired refresh token', authRef('ErrorResponse'))
|
|
445
|
+
.handler('refresh')
|
|
446
|
+
.build() as AuthEndpointsMap['refresh'],
|
|
447
|
+
|
|
448
|
+
logout: endpoint('POST', '/auth/logout')
|
|
449
|
+
.summary('Logout and revoke refresh token')
|
|
450
|
+
.tags('Auth')
|
|
451
|
+
.security('none')
|
|
452
|
+
.body(obj({ refreshToken: str('Refresh token to revoke') }, 'refreshToken'))
|
|
453
|
+
.response(200, 'Logged out', obj({ ok: bool() }, 'ok'))
|
|
454
|
+
.handler('logout')
|
|
455
|
+
.build() as AuthEndpointsMap['logout'],
|
|
456
|
+
|
|
457
|
+
me: endpoint('GET', '/auth/me')
|
|
458
|
+
.summary('Get current user profile')
|
|
459
|
+
.tags('Auth')
|
|
460
|
+
.security('bearer')
|
|
461
|
+
.response(200, 'Current user', authRef('User'))
|
|
462
|
+
.response(401, 'Not authenticated', authRef('ErrorResponse'))
|
|
463
|
+
.handler('me')
|
|
464
|
+
.build() as AuthEndpointsMap['me'],
|
|
465
|
+
|
|
466
|
+
listSessions: endpoint('GET', '/auth/sessions')
|
|
467
|
+
.summary('List active sessions')
|
|
468
|
+
.tags('Auth')
|
|
469
|
+
.security('bearer')
|
|
470
|
+
.response(200, 'Active sessions', arr(authRef('SessionInfo')))
|
|
471
|
+
.response(401, 'Not authenticated', authRef('ErrorResponse'))
|
|
472
|
+
.handler('listSessions')
|
|
473
|
+
.build() as AuthEndpointsMap['listSessions'],
|
|
474
|
+
|
|
475
|
+
revokeSession: endpoint('DELETE', '/auth/sessions/:id')
|
|
476
|
+
.summary('Revoke a specific session')
|
|
477
|
+
.tags('Auth')
|
|
478
|
+
.security('bearer')
|
|
479
|
+
.params(obj({ id: id('Session/token ID') }, 'id'))
|
|
480
|
+
.response(200, 'Session revoked', obj({ ok: bool() }, 'ok'))
|
|
481
|
+
.response(401, 'Not authenticated', authRef('ErrorResponse'))
|
|
482
|
+
.handler('revokeSession')
|
|
483
|
+
.build() as AuthEndpointsMap['revokeSession'],
|
|
484
|
+
|
|
485
|
+
revokeAllOtherSessions: endpoint('DELETE', '/auth/sessions')
|
|
486
|
+
.summary('Revoke all other sessions')
|
|
487
|
+
.description('Revokes all active sessions except the current one. Requires the current token ID.')
|
|
488
|
+
.tags('Auth')
|
|
489
|
+
.security('bearer')
|
|
490
|
+
.body(obj({ currentTokenId: id('Current token ID to keep') }, 'currentTokenId'))
|
|
491
|
+
.response(200, 'Other sessions revoked', obj({ ok: bool() }, 'ok'))
|
|
492
|
+
.response(401, 'Not authenticated', authRef('ErrorResponse'))
|
|
493
|
+
.handler('revokeAllOtherSessions')
|
|
494
|
+
.build() as AuthEndpointsMap['revokeAllOtherSessions'],
|
|
495
|
+
|
|
496
|
+
addLoginIdentifier: endpoint('POST', '/auth/identifiers')
|
|
497
|
+
.summary('Add a login identifier')
|
|
498
|
+
.tags('Auth')
|
|
499
|
+
.security('bearer')
|
|
500
|
+
.body(obj(
|
|
501
|
+
{
|
|
502
|
+
type: enums('email', 'phone', 'username'),
|
|
503
|
+
value: str('Identifier value'),
|
|
504
|
+
},
|
|
505
|
+
'type',
|
|
506
|
+
'value',
|
|
507
|
+
))
|
|
508
|
+
.response(200, 'Identifier added', obj({ ok: bool() }, 'ok'))
|
|
509
|
+
.response(401, 'Not authenticated', authRef('ErrorResponse'))
|
|
510
|
+
.handler('addLoginIdentifier')
|
|
511
|
+
.build() as AuthEndpointsMap['addLoginIdentifier'],
|
|
512
|
+
|
|
513
|
+
removeLoginIdentifier: endpoint('DELETE', '/auth/identifiers')
|
|
514
|
+
.summary('Remove a login identifier')
|
|
515
|
+
.tags('Auth')
|
|
516
|
+
.security('bearer')
|
|
517
|
+
.body(obj(
|
|
518
|
+
{
|
|
519
|
+
type: enums('email', 'phone', 'username'),
|
|
520
|
+
value: str('Identifier value'),
|
|
521
|
+
},
|
|
522
|
+
'type',
|
|
523
|
+
'value',
|
|
524
|
+
))
|
|
525
|
+
.response(200, 'Identifier removed', obj({ ok: bool() }, 'ok'))
|
|
526
|
+
.response(401, 'Not authenticated', authRef('ErrorResponse'))
|
|
527
|
+
.handler('removeLoginIdentifier')
|
|
528
|
+
.build() as AuthEndpointsMap['removeLoginIdentifier'],
|
|
529
|
+
|
|
530
|
+
getLoginIdentifiers: endpoint('GET', '/auth/identifiers')
|
|
531
|
+
.summary('List login identifiers')
|
|
532
|
+
.tags('Auth')
|
|
533
|
+
.security('bearer')
|
|
534
|
+
.response(200, 'Login identifiers', arr(authRef('LoginIdentifier')))
|
|
535
|
+
.response(401, 'Not authenticated', authRef('ErrorResponse'))
|
|
536
|
+
.handler('getLoginIdentifiers')
|
|
537
|
+
.build() as AuthEndpointsMap['getLoginIdentifiers'],
|
|
538
|
+
|
|
539
|
+
impersonate: endpoint('POST', '/auth/impersonate')
|
|
540
|
+
.summary('Impersonate a user')
|
|
541
|
+
.description('Issue a short-lived, non-renewable token to act as another user. Requires fortress:impersonate permission.')
|
|
542
|
+
.tags('Auth')
|
|
543
|
+
.security('bearer')
|
|
544
|
+
.permission('fortress', 'impersonate')
|
|
545
|
+
.body(obj(
|
|
546
|
+
{
|
|
547
|
+
targetUserId: id('User to impersonate'),
|
|
548
|
+
reason: str('Reason for impersonation'),
|
|
549
|
+
expirySeconds: int('Token expiry in seconds (default 3600)'),
|
|
550
|
+
},
|
|
551
|
+
'targetUserId',
|
|
552
|
+
))
|
|
553
|
+
.response(200, 'Impersonation token issued', authRef('AuthResult'))
|
|
554
|
+
.response(401, 'Not authenticated', authRef('ErrorResponse'))
|
|
555
|
+
.response(404, 'Target user not found', authRef('ErrorResponse'))
|
|
556
|
+
.handler('impersonate')
|
|
557
|
+
.build() as AuthEndpointsMap['impersonate'],
|
|
558
|
+
};
|