@portaidentity/server 1.7.0
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 +11 -0
- package/LICENSE +21 -0
- package/README.md +33 -0
- package/dist/applications/cache.d.ts +64 -0
- package/dist/applications/cache.d.ts.map +1 -0
- package/dist/applications/cache.js +161 -0
- package/dist/applications/cache.js.map +1 -0
- package/dist/applications/errors.d.ts +25 -0
- package/dist/applications/errors.d.ts.map +1 -0
- package/dist/applications/errors.js +31 -0
- package/dist/applications/errors.js.map +1 -0
- package/dist/applications/index.d.ts +17 -0
- package/dist/applications/index.d.ts.map +1 -0
- package/dist/applications/index.js +19 -0
- package/dist/applications/index.js.map +1 -0
- package/dist/applications/repository.d.ts +184 -0
- package/dist/applications/repository.d.ts.map +1 -0
- package/dist/applications/repository.js +367 -0
- package/dist/applications/repository.js.map +1 -0
- package/dist/applications/service.d.ts +160 -0
- package/dist/applications/service.d.ts.map +1 -0
- package/dist/applications/service.js +388 -0
- package/dist/applications/service.js.map +1 -0
- package/dist/applications/slugs.d.ts +61 -0
- package/dist/applications/slugs.d.ts.map +1 -0
- package/dist/applications/slugs.js +115 -0
- package/dist/applications/slugs.js.map +1 -0
- package/dist/applications/types.d.ts +129 -0
- package/dist/applications/types.d.ts.map +1 -0
- package/dist/applications/types.js +62 -0
- package/dist/applications/types.js.map +1 -0
- package/dist/auth/csrf.d.ts +71 -0
- package/dist/auth/csrf.d.ts.map +1 -0
- package/dist/auth/csrf.js +112 -0
- package/dist/auth/csrf.js.map +1 -0
- package/dist/auth/email-renderer.d.ts +37 -0
- package/dist/auth/email-renderer.d.ts.map +1 -0
- package/dist/auth/email-renderer.js +116 -0
- package/dist/auth/email-renderer.js.map +1 -0
- package/dist/auth/email-service.d.ts +154 -0
- package/dist/auth/email-service.d.ts.map +1 -0
- package/dist/auth/email-service.js +415 -0
- package/dist/auth/email-service.js.map +1 -0
- package/dist/auth/email-transport.d.ts +65 -0
- package/dist/auth/email-transport.d.ts.map +1 -0
- package/dist/auth/email-transport.js +82 -0
- package/dist/auth/email-transport.js.map +1 -0
- package/dist/auth/i18n.d.ts +81 -0
- package/dist/auth/i18n.d.ts.map +1 -0
- package/dist/auth/i18n.js +395 -0
- package/dist/auth/i18n.js.map +1 -0
- package/dist/auth/index.d.ts +28 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +31 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/interaction-authority.d.ts +39 -0
- package/dist/auth/interaction-authority.d.ts.map +1 -0
- package/dist/auth/interaction-authority.js +30 -0
- package/dist/auth/interaction-authority.js.map +1 -0
- package/dist/auth/magic-link-session.d.ts +81 -0
- package/dist/auth/magic-link-session.d.ts.map +1 -0
- package/dist/auth/magic-link-session.js +183 -0
- package/dist/auth/magic-link-session.js.map +1 -0
- package/dist/auth/rate-limiter.d.ts +143 -0
- package/dist/auth/rate-limiter.d.ts.map +1 -0
- package/dist/auth/rate-limiter.js +220 -0
- package/dist/auth/rate-limiter.js.map +1 -0
- package/dist/auth/recovery-crypto.d.ts +34 -0
- package/dist/auth/recovery-crypto.d.ts.map +1 -0
- package/dist/auth/recovery-crypto.js +103 -0
- package/dist/auth/recovery-crypto.js.map +1 -0
- package/dist/auth/recovery-job-processor.d.ts +24 -0
- package/dist/auth/recovery-job-processor.d.ts.map +1 -0
- package/dist/auth/recovery-job-processor.js +123 -0
- package/dist/auth/recovery-job-processor.js.map +1 -0
- package/dist/auth/recovery-job-repository.d.ts +219 -0
- package/dist/auth/recovery-job-repository.d.ts.map +1 -0
- package/dist/auth/recovery-job-repository.js +340 -0
- package/dist/auth/recovery-job-repository.js.map +1 -0
- package/dist/auth/recovery-service.d.ts +27 -0
- package/dist/auth/recovery-service.d.ts.map +1 -0
- package/dist/auth/recovery-service.js +53 -0
- package/dist/auth/recovery-service.js.map +1 -0
- package/dist/auth/recovery-worker.d.ts +158 -0
- package/dist/auth/recovery-worker.d.ts.map +1 -0
- package/dist/auth/recovery-worker.js +306 -0
- package/dist/auth/recovery-worker.js.map +1 -0
- package/dist/auth/template-engine.d.ts +134 -0
- package/dist/auth/template-engine.d.ts.map +1 -0
- package/dist/auth/template-engine.js +225 -0
- package/dist/auth/template-engine.js.map +1 -0
- package/dist/auth/token-repository.d.ts +238 -0
- package/dist/auth/token-repository.d.ts.map +1 -0
- package/dist/auth/token-repository.js +475 -0
- package/dist/auth/token-repository.js.map +1 -0
- package/dist/auth/tokens.d.ts +45 -0
- package/dist/auth/tokens.d.ts.map +1 -0
- package/dist/auth/tokens.js +51 -0
- package/dist/auth/tokens.js.map +1 -0
- package/dist/cli/bootstrap.d.ts +55 -0
- package/dist/cli/bootstrap.d.ts.map +1 -0
- package/dist/cli/bootstrap.js +90 -0
- package/dist/cli/bootstrap.js.map +1 -0
- package/dist/cli/commands/health.d.ts +20 -0
- package/dist/cli/commands/health.d.ts.map +1 -0
- package/dist/cli/commands/health.js +80 -0
- package/dist/cli/commands/health.js.map +1 -0
- package/dist/cli/commands/init.d.ts +48 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +349 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/migrate.d.ts +19 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -0
- package/dist/cli/commands/migrate.js +88 -0
- package/dist/cli/commands/migrate.js.map +1 -0
- package/dist/cli/commands/seed.d.ts +19 -0
- package/dist/cli/commands/seed.d.ts.map +1 -0
- package/dist/cli/commands/seed.js +71 -0
- package/dist/cli/commands/seed.js.map +1 -0
- package/dist/cli/commands/user-2fa.d.ts +29 -0
- package/dist/cli/commands/user-2fa.d.ts.map +1 -0
- package/dist/cli/commands/user-2fa.js +113 -0
- package/dist/cli/commands/user-2fa.js.map +1 -0
- package/dist/cli/error-handler.d.ts +31 -0
- package/dist/cli/error-handler.d.ts.map +1 -0
- package/dist/cli/error-handler.js +65 -0
- package/dist/cli/error-handler.js.map +1 -0
- package/dist/cli/index.d.ts +48 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +99 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/output.d.ts +94 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +124 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/prompt.d.ts +48 -0
- package/dist/cli/prompt.d.ts.map +1 -0
- package/dist/cli/prompt.js +96 -0
- package/dist/cli/prompt.js.map +1 -0
- package/dist/clients/cache.d.ts +63 -0
- package/dist/clients/cache.d.ts.map +1 -0
- package/dist/clients/cache.js +160 -0
- package/dist/clients/cache.js.map +1 -0
- package/dist/clients/crypto.d.ts +69 -0
- package/dist/clients/crypto.d.ts.map +1 -0
- package/dist/clients/crypto.js +83 -0
- package/dist/clients/crypto.js.map +1 -0
- package/dist/clients/errors.d.ts +27 -0
- package/dist/clients/errors.d.ts.map +1 -0
- package/dist/clients/errors.js +33 -0
- package/dist/clients/errors.js.map +1 -0
- package/dist/clients/index.d.ts +19 -0
- package/dist/clients/index.d.ts.map +1 -0
- package/dist/clients/index.js +23 -0
- package/dist/clients/index.js.map +1 -0
- package/dist/clients/repository.d.ts +161 -0
- package/dist/clients/repository.d.ts.map +1 -0
- package/dist/clients/repository.js +318 -0
- package/dist/clients/repository.js.map +1 -0
- package/dist/clients/resolve-login-methods.d.ts +74 -0
- package/dist/clients/resolve-login-methods.d.ts.map +1 -0
- package/dist/clients/resolve-login-methods.js +68 -0
- package/dist/clients/resolve-login-methods.js.map +1 -0
- package/dist/clients/secret-repository.d.ts +109 -0
- package/dist/clients/secret-repository.d.ts.map +1 -0
- package/dist/clients/secret-repository.js +166 -0
- package/dist/clients/secret-repository.js.map +1 -0
- package/dist/clients/secret-service.d.ts +87 -0
- package/dist/clients/secret-service.d.ts.map +1 -0
- package/dist/clients/secret-service.js +204 -0
- package/dist/clients/secret-service.js.map +1 -0
- package/dist/clients/service.d.ts +158 -0
- package/dist/clients/service.d.ts.map +1 -0
- package/dist/clients/service.js +523 -0
- package/dist/clients/service.js.map +1 -0
- package/dist/clients/types.d.ts +214 -0
- package/dist/clients/types.d.ts.map +1 -0
- package/dist/clients/types.js +77 -0
- package/dist/clients/types.js.map +1 -0
- package/dist/clients/validators.d.ts +87 -0
- package/dist/clients/validators.d.ts.map +1 -0
- package/dist/clients/validators.js +164 -0
- package/dist/clients/validators.js.map +1 -0
- package/dist/config/index.d.ts +23 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +52 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.d.ts +35 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +167 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/custom-claims/cache.d.ts +48 -0
- package/dist/custom-claims/cache.d.ts.map +1 -0
- package/dist/custom-claims/cache.js +122 -0
- package/dist/custom-claims/cache.js.map +1 -0
- package/dist/custom-claims/errors.d.ts +25 -0
- package/dist/custom-claims/errors.d.ts.map +1 -0
- package/dist/custom-claims/errors.js +31 -0
- package/dist/custom-claims/errors.js.map +1 -0
- package/dist/custom-claims/index.d.ts +14 -0
- package/dist/custom-claims/index.d.ts.map +1 -0
- package/dist/custom-claims/index.js +17 -0
- package/dist/custom-claims/index.js.map +1 -0
- package/dist/custom-claims/repository.d.ts +147 -0
- package/dist/custom-claims/repository.d.ts.map +1 -0
- package/dist/custom-claims/repository.js +338 -0
- package/dist/custom-claims/repository.js.map +1 -0
- package/dist/custom-claims/service.d.ts +129 -0
- package/dist/custom-claims/service.d.ts.map +1 -0
- package/dist/custom-claims/service.js +296 -0
- package/dist/custom-claims/service.js.map +1 -0
- package/dist/custom-claims/types.d.ts +132 -0
- package/dist/custom-claims/types.d.ts.map +1 -0
- package/dist/custom-claims/types.js +61 -0
- package/dist/custom-claims/types.js.map +1 -0
- package/dist/custom-claims/validators.d.ts +61 -0
- package/dist/custom-claims/validators.d.ts.map +1 -0
- package/dist/custom-claims/validators.js +136 -0
- package/dist/custom-claims/validators.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +91 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/admin-permissions.d.ts +147 -0
- package/dist/lib/admin-permissions.d.ts.map +1 -0
- package/dist/lib/admin-permissions.js +261 -0
- package/dist/lib/admin-permissions.js.map +1 -0
- package/dist/lib/audit-log.d.ts +45 -0
- package/dist/lib/audit-log.d.ts.map +1 -0
- package/dist/lib/audit-log.js +79 -0
- package/dist/lib/audit-log.js.map +1 -0
- package/dist/lib/branding-assets.d.ts +47 -0
- package/dist/lib/branding-assets.d.ts.map +1 -0
- package/dist/lib/branding-assets.js +93 -0
- package/dist/lib/branding-assets.js.map +1 -0
- package/dist/lib/bulk-operations.d.ts +54 -0
- package/dist/lib/bulk-operations.d.ts.map +1 -0
- package/dist/lib/bulk-operations.js +201 -0
- package/dist/lib/bulk-operations.js.map +1 -0
- package/dist/lib/cursor.d.ts +110 -0
- package/dist/lib/cursor.d.ts.map +1 -0
- package/dist/lib/cursor.js +115 -0
- package/dist/lib/cursor.js.map +1 -0
- package/dist/lib/data-export.d.ts +67 -0
- package/dist/lib/data-export.d.ts.map +1 -0
- package/dist/lib/data-export.js +272 -0
- package/dist/lib/data-export.js.map +1 -0
- package/dist/lib/data-import-plan.d.ts +50 -0
- package/dist/lib/data-import-plan.d.ts.map +1 -0
- package/dist/lib/data-import-plan.js +235 -0
- package/dist/lib/data-import-plan.js.map +1 -0
- package/dist/lib/data-import.d.ts +276 -0
- package/dist/lib/data-import.d.ts.map +1 -0
- package/dist/lib/data-import.js +1694 -0
- package/dist/lib/data-import.js.map +1 -0
- package/dist/lib/database.d.ts +22 -0
- package/dist/lib/database.d.ts.map +1 -0
- package/dist/lib/database.js +103 -0
- package/dist/lib/database.js.map +1 -0
- package/dist/lib/entity-history.d.ts +52 -0
- package/dist/lib/entity-history.d.ts.map +1 -0
- package/dist/lib/entity-history.js +121 -0
- package/dist/lib/entity-history.js.map +1 -0
- package/dist/lib/etag.d.ts +92 -0
- package/dist/lib/etag.d.ts.map +1 -0
- package/dist/lib/etag.js +155 -0
- package/dist/lib/etag.js.map +1 -0
- package/dist/lib/image-validator.d.ts +44 -0
- package/dist/lib/image-validator.d.ts.map +1 -0
- package/dist/lib/image-validator.js +204 -0
- package/dist/lib/image-validator.js.map +1 -0
- package/dist/lib/logger.d.ts +15 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +84 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/migrator.d.ts +14 -0
- package/dist/lib/migrator.d.ts.map +1 -0
- package/dist/lib/migrator.js +34 -0
- package/dist/lib/migrator.js.map +1 -0
- package/dist/lib/redis.d.ts +5 -0
- package/dist/lib/redis.d.ts.map +1 -0
- package/dist/lib/redis.js +29 -0
- package/dist/lib/redis.js.map +1 -0
- package/dist/lib/runtime-paths.d.ts +38 -0
- package/dist/lib/runtime-paths.d.ts.map +1 -0
- package/dist/lib/runtime-paths.js +50 -0
- package/dist/lib/runtime-paths.js.map +1 -0
- package/dist/lib/session-tracking.d.ts +81 -0
- package/dist/lib/session-tracking.d.ts.map +1 -0
- package/dist/lib/session-tracking.js +145 -0
- package/dist/lib/session-tracking.js.map +1 -0
- package/dist/lib/signing-key-crypto.d.ts +53 -0
- package/dist/lib/signing-key-crypto.d.ts.map +1 -0
- package/dist/lib/signing-key-crypto.js +108 -0
- package/dist/lib/signing-key-crypto.js.map +1 -0
- package/dist/lib/signing-keys.d.ts +141 -0
- package/dist/lib/signing-keys.d.ts.map +1 -0
- package/dist/lib/signing-keys.js +206 -0
- package/dist/lib/signing-keys.js.map +1 -0
- package/dist/lib/stats.d.ts +83 -0
- package/dist/lib/stats.d.ts.map +1 -0
- package/dist/lib/stats.js +187 -0
- package/dist/lib/stats.js.map +1 -0
- package/dist/lib/super-admin-protection.d.ts +69 -0
- package/dist/lib/super-admin-protection.d.ts.map +1 -0
- package/dist/lib/super-admin-protection.js +102 -0
- package/dist/lib/super-admin-protection.js.map +1 -0
- package/dist/lib/system-config.d.ts +79 -0
- package/dist/lib/system-config.d.ts.map +1 -0
- package/dist/lib/system-config.js +159 -0
- package/dist/lib/system-config.js.map +1 -0
- package/dist/middleware/admin-auth.d.ts +100 -0
- package/dist/middleware/admin-auth.d.ts.map +1 -0
- package/dist/middleware/admin-auth.js +241 -0
- package/dist/middleware/admin-auth.js.map +1 -0
- package/dist/middleware/admin-cors.d.ts +41 -0
- package/dist/middleware/admin-cors.d.ts.map +1 -0
- package/dist/middleware/admin-cors.js +83 -0
- package/dist/middleware/admin-cors.js.map +1 -0
- package/dist/middleware/admin-mutation-audit.d.ts +9 -0
- package/dist/middleware/admin-mutation-audit.d.ts.map +1 -0
- package/dist/middleware/admin-mutation-audit.js +76 -0
- package/dist/middleware/admin-mutation-audit.js.map +1 -0
- package/dist/middleware/admin-rate-limiter.d.ts +56 -0
- package/dist/middleware/admin-rate-limiter.d.ts.map +1 -0
- package/dist/middleware/admin-rate-limiter.js +97 -0
- package/dist/middleware/admin-rate-limiter.js.map +1 -0
- package/dist/middleware/client-secret-hash.d.ts +34 -0
- package/dist/middleware/client-secret-hash.d.ts.map +1 -0
- package/dist/middleware/client-secret-hash.js +74 -0
- package/dist/middleware/client-secret-hash.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +3 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +93 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/health.d.ts +3 -0
- package/dist/middleware/health.d.ts.map +1 -0
- package/dist/middleware/health.js +35 -0
- package/dist/middleware/health.js.map +1 -0
- package/dist/middleware/metrics.d.ts +40 -0
- package/dist/middleware/metrics.d.ts.map +1 -0
- package/dist/middleware/metrics.js +75 -0
- package/dist/middleware/metrics.js.map +1 -0
- package/dist/middleware/oidc-client-tenant.d.ts +37 -0
- package/dist/middleware/oidc-client-tenant.d.ts.map +1 -0
- package/dist/middleware/oidc-client-tenant.js +156 -0
- package/dist/middleware/oidc-client-tenant.js.map +1 -0
- package/dist/middleware/oidc-cors.d.ts +33 -0
- package/dist/middleware/oidc-cors.d.ts.map +1 -0
- package/dist/middleware/oidc-cors.js +68 -0
- package/dist/middleware/oidc-cors.js.map +1 -0
- package/dist/middleware/oidc-preflight-cors.d.ts +54 -0
- package/dist/middleware/oidc-preflight-cors.d.ts.map +1 -0
- package/dist/middleware/oidc-preflight-cors.js +267 -0
- package/dist/middleware/oidc-preflight-cors.js.map +1 -0
- package/dist/middleware/prompt-login-reset.d.ts +103 -0
- package/dist/middleware/prompt-login-reset.d.ts.map +1 -0
- package/dist/middleware/prompt-login-reset.js +204 -0
- package/dist/middleware/prompt-login-reset.js.map +1 -0
- package/dist/middleware/ready.d.ts +23 -0
- package/dist/middleware/ready.d.ts.map +1 -0
- package/dist/middleware/ready.js +60 -0
- package/dist/middleware/ready.js.map +1 -0
- package/dist/middleware/request-logger.d.ts +27 -0
- package/dist/middleware/request-logger.d.ts.map +1 -0
- package/dist/middleware/request-logger.js +46 -0
- package/dist/middleware/request-logger.js.map +1 -0
- package/dist/middleware/require-permission.d.ts +28 -0
- package/dist/middleware/require-permission.d.ts.map +1 -0
- package/dist/middleware/require-permission.js +63 -0
- package/dist/middleware/require-permission.js.map +1 -0
- package/dist/middleware/require-user-organization.d.ts +18 -0
- package/dist/middleware/require-user-organization.d.ts.map +1 -0
- package/dist/middleware/require-user-organization.js +53 -0
- package/dist/middleware/require-user-organization.js.map +1 -0
- package/dist/middleware/root-page.d.ts +62 -0
- package/dist/middleware/root-page.d.ts.map +1 -0
- package/dist/middleware/root-page.js +134 -0
- package/dist/middleware/root-page.js.map +1 -0
- package/dist/middleware/security-headers.d.ts +98 -0
- package/dist/middleware/security-headers.d.ts.map +1 -0
- package/dist/middleware/security-headers.js +159 -0
- package/dist/middleware/security-headers.js.map +1 -0
- package/dist/middleware/tenant-resolver.d.ts +37 -0
- package/dist/middleware/tenant-resolver.d.ts.map +1 -0
- package/dist/middleware/tenant-resolver.js +71 -0
- package/dist/middleware/tenant-resolver.js.map +1 -0
- package/dist/middleware/token-rate-limiter.d.ts +89 -0
- package/dist/middleware/token-rate-limiter.d.ts.map +1 -0
- package/dist/middleware/token-rate-limiter.js +189 -0
- package/dist/middleware/token-rate-limiter.js.map +1 -0
- package/dist/oidc/account-finder.d.ts +52 -0
- package/dist/oidc/account-finder.d.ts.map +1 -0
- package/dist/oidc/account-finder.js +134 -0
- package/dist/oidc/account-finder.js.map +1 -0
- package/dist/oidc/adapter-factory.d.ts +90 -0
- package/dist/oidc/adapter-factory.d.ts.map +1 -0
- package/dist/oidc/adapter-factory.js +165 -0
- package/dist/oidc/adapter-factory.js.map +1 -0
- package/dist/oidc/configuration.d.ts +155 -0
- package/dist/oidc/configuration.d.ts.map +1 -0
- package/dist/oidc/configuration.js +531 -0
- package/dist/oidc/configuration.js.map +1 -0
- package/dist/oidc/postgres-adapter.d.ts +159 -0
- package/dist/oidc/postgres-adapter.d.ts.map +1 -0
- package/dist/oidc/postgres-adapter.js +222 -0
- package/dist/oidc/postgres-adapter.js.map +1 -0
- package/dist/oidc/protocol-security-observer.d.ts +38 -0
- package/dist/oidc/protocol-security-observer.d.ts.map +1 -0
- package/dist/oidc/protocol-security-observer.js +93 -0
- package/dist/oidc/protocol-security-observer.js.map +1 -0
- package/dist/oidc/provider.d.ts +37 -0
- package/dist/oidc/provider.d.ts.map +1 -0
- package/dist/oidc/provider.js +71 -0
- package/dist/oidc/provider.js.map +1 -0
- package/dist/oidc/redis-adapter.d.ts +140 -0
- package/dist/oidc/redis-adapter.d.ts.map +1 -0
- package/dist/oidc/redis-adapter.js +344 -0
- package/dist/oidc/redis-adapter.js.map +1 -0
- package/dist/organizations/cache.d.ts +60 -0
- package/dist/organizations/cache.d.ts.map +1 -0
- package/dist/organizations/cache.js +157 -0
- package/dist/organizations/cache.js.map +1 -0
- package/dist/organizations/errors.d.ts +25 -0
- package/dist/organizations/errors.d.ts.map +1 -0
- package/dist/organizations/errors.js +31 -0
- package/dist/organizations/errors.js.map +1 -0
- package/dist/organizations/index.d.ts +15 -0
- package/dist/organizations/index.d.ts.map +1 -0
- package/dist/organizations/index.js +15 -0
- package/dist/organizations/index.js.map +1 -0
- package/dist/organizations/repository.d.ts +183 -0
- package/dist/organizations/repository.d.ts.map +1 -0
- package/dist/organizations/repository.js +354 -0
- package/dist/organizations/repository.js.map +1 -0
- package/dist/organizations/service.d.ts +168 -0
- package/dist/organizations/service.d.ts.map +1 -0
- package/dist/organizations/service.js +511 -0
- package/dist/organizations/service.js.map +1 -0
- package/dist/organizations/slugs.d.ts +57 -0
- package/dist/organizations/slugs.d.ts.map +1 -0
- package/dist/organizations/slugs.js +130 -0
- package/dist/organizations/slugs.js.map +1 -0
- package/dist/organizations/types.d.ts +146 -0
- package/dist/organizations/types.d.ts.map +1 -0
- package/dist/organizations/types.js +49 -0
- package/dist/organizations/types.js.map +1 -0
- package/dist/rbac/cache.d.ts +110 -0
- package/dist/rbac/cache.d.ts.map +1 -0
- package/dist/rbac/cache.js +271 -0
- package/dist/rbac/cache.js.map +1 -0
- package/dist/rbac/errors.d.ts +34 -0
- package/dist/rbac/errors.d.ts.map +1 -0
- package/dist/rbac/errors.js +43 -0
- package/dist/rbac/errors.js.map +1 -0
- package/dist/rbac/index.d.ts +17 -0
- package/dist/rbac/index.d.ts.map +1 -0
- package/dist/rbac/index.js +21 -0
- package/dist/rbac/index.js.map +1 -0
- package/dist/rbac/mapping-repository.d.ts +121 -0
- package/dist/rbac/mapping-repository.d.ts.map +1 -0
- package/dist/rbac/mapping-repository.js +224 -0
- package/dist/rbac/mapping-repository.js.map +1 -0
- package/dist/rbac/permission-repository.d.ts +100 -0
- package/dist/rbac/permission-repository.d.ts.map +1 -0
- package/dist/rbac/permission-repository.js +190 -0
- package/dist/rbac/permission-repository.js.map +1 -0
- package/dist/rbac/permission-service.d.ts +85 -0
- package/dist/rbac/permission-service.d.ts.map +1 -0
- package/dist/rbac/permission-service.js +179 -0
- package/dist/rbac/permission-service.js.map +1 -0
- package/dist/rbac/role-repository.d.ts +104 -0
- package/dist/rbac/role-repository.d.ts.map +1 -0
- package/dist/rbac/role-repository.js +202 -0
- package/dist/rbac/role-repository.js.map +1 -0
- package/dist/rbac/role-service.d.ts +111 -0
- package/dist/rbac/role-service.d.ts.map +1 -0
- package/dist/rbac/role-service.js +256 -0
- package/dist/rbac/role-service.js.map +1 -0
- package/dist/rbac/slugs.d.ts +116 -0
- package/dist/rbac/slugs.d.ts.map +1 -0
- package/dist/rbac/slugs.js +178 -0
- package/dist/rbac/slugs.js.map +1 -0
- package/dist/rbac/types.d.ts +187 -0
- package/dist/rbac/types.d.ts.map +1 -0
- package/dist/rbac/types.js +75 -0
- package/dist/rbac/types.js.map +1 -0
- package/dist/rbac/user-role-service.d.ts +101 -0
- package/dist/rbac/user-role-service.d.ts.map +1 -0
- package/dist/rbac/user-role-service.js +166 -0
- package/dist/rbac/user-role-service.js.map +1 -0
- package/dist/routes/applications.d.ts +40 -0
- package/dist/routes/applications.d.ts.map +1 -0
- package/dist/routes/applications.js +284 -0
- package/dist/routes/applications.js.map +1 -0
- package/dist/routes/audit.d.ts +31 -0
- package/dist/routes/audit.d.ts.map +1 -0
- package/dist/routes/audit.js +140 -0
- package/dist/routes/audit.js.map +1 -0
- package/dist/routes/branding.d.ts +17 -0
- package/dist/routes/branding.d.ts.map +1 -0
- package/dist/routes/branding.js +104 -0
- package/dist/routes/branding.js.map +1 -0
- package/dist/routes/bulk.d.ts +37 -0
- package/dist/routes/bulk.d.ts.map +1 -0
- package/dist/routes/bulk.js +94 -0
- package/dist/routes/bulk.js.map +1 -0
- package/dist/routes/clients.d.ts +44 -0
- package/dist/routes/clients.d.ts.map +1 -0
- package/dist/routes/clients.js +353 -0
- package/dist/routes/clients.js.map +1 -0
- package/dist/routes/config.d.ts +35 -0
- package/dist/routes/config.d.ts.map +1 -0
- package/dist/routes/config.js +115 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/custom-claims.d.ts +38 -0
- package/dist/routes/custom-claims.d.ts.map +1 -0
- package/dist/routes/custom-claims.js +208 -0
- package/dist/routes/custom-claims.js.map +1 -0
- package/dist/routes/exports.d.ts +21 -0
- package/dist/routes/exports.d.ts.map +1 -0
- package/dist/routes/exports.js +98 -0
- package/dist/routes/exports.js.map +1 -0
- package/dist/routes/imports.d.ts +18 -0
- package/dist/routes/imports.d.ts.map +1 -0
- package/dist/routes/imports.js +72 -0
- package/dist/routes/imports.js.map +1 -0
- package/dist/routes/interactions.d.ts +111 -0
- package/dist/routes/interactions.d.ts.map +1 -0
- package/dist/routes/interactions.js +1074 -0
- package/dist/routes/interactions.js.map +1 -0
- package/dist/routes/invitation.d.ts +31 -0
- package/dist/routes/invitation.d.ts.map +1 -0
- package/dist/routes/invitation.js +342 -0
- package/dist/routes/invitation.js.map +1 -0
- package/dist/routes/keys.d.ts +28 -0
- package/dist/routes/keys.d.ts.map +1 -0
- package/dist/routes/keys.js +91 -0
- package/dist/routes/keys.js.map +1 -0
- package/dist/routes/magic-link.d.ts +43 -0
- package/dist/routes/magic-link.d.ts.map +1 -0
- package/dist/routes/magic-link.js +264 -0
- package/dist/routes/magic-link.js.map +1 -0
- package/dist/routes/organizations.d.ts +38 -0
- package/dist/routes/organizations.d.ts.map +1 -0
- package/dist/routes/organizations.js +347 -0
- package/dist/routes/organizations.js.map +1 -0
- package/dist/routes/password-reset.d.ts +65 -0
- package/dist/routes/password-reset.d.ts.map +1 -0
- package/dist/routes/password-reset.js +493 -0
- package/dist/routes/password-reset.js.map +1 -0
- package/dist/routes/permissions.d.ts +33 -0
- package/dist/routes/permissions.d.ts.map +1 -0
- package/dist/routes/permissions.js +166 -0
- package/dist/routes/permissions.js.map +1 -0
- package/dist/routes/roles.d.ts +37 -0
- package/dist/routes/roles.d.ts.map +1 -0
- package/dist/routes/roles.js +222 -0
- package/dist/routes/roles.js.map +1 -0
- package/dist/routes/sessions.d.ts +37 -0
- package/dist/routes/sessions.d.ts.map +1 -0
- package/dist/routes/sessions.js +121 -0
- package/dist/routes/sessions.js.map +1 -0
- package/dist/routes/stats.d.ts +28 -0
- package/dist/routes/stats.d.ts.map +1 -0
- package/dist/routes/stats.js +65 -0
- package/dist/routes/stats.js.map +1 -0
- package/dist/routes/two-factor-admin.d.ts +54 -0
- package/dist/routes/two-factor-admin.d.ts.map +1 -0
- package/dist/routes/two-factor-admin.js +344 -0
- package/dist/routes/two-factor-admin.js.map +1 -0
- package/dist/routes/two-factor.d.ts +31 -0
- package/dist/routes/two-factor.d.ts.map +1 -0
- package/dist/routes/two-factor.js +595 -0
- package/dist/routes/two-factor.js.map +1 -0
- package/dist/routes/user-roles.d.ts +33 -0
- package/dist/routes/user-roles.d.ts.map +1 -0
- package/dist/routes/user-roles.js +129 -0
- package/dist/routes/user-roles.js.map +1 -0
- package/dist/routes/users.d.ts +61 -0
- package/dist/routes/users.d.ts.map +1 -0
- package/dist/routes/users.js +868 -0
- package/dist/routes/users.js.map +1 -0
- package/dist/security/decision-context.d.ts +52 -0
- package/dist/security/decision-context.d.ts.map +1 -0
- package/dist/security/decision-context.js +150 -0
- package/dist/security/decision-context.js.map +1 -0
- package/dist/security/decision-event.d.ts +128 -0
- package/dist/security/decision-event.d.ts.map +1 -0
- package/dist/security/decision-event.js +225 -0
- package/dist/security/decision-event.js.map +1 -0
- package/dist/security/transport-decision.d.ts +6 -0
- package/dist/security/transport-decision.d.ts.map +1 -0
- package/dist/security/transport-decision.js +37 -0
- package/dist/security/transport-decision.js.map +1 -0
- package/dist/server.d.ts +50 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +491 -0
- package/dist/server.js.map +1 -0
- package/dist/two-factor/cache.d.ts +47 -0
- package/dist/two-factor/cache.d.ts.map +1 -0
- package/dist/two-factor/cache.js +122 -0
- package/dist/two-factor/cache.js.map +1 -0
- package/dist/two-factor/crypto.d.ts +41 -0
- package/dist/two-factor/crypto.d.ts.map +1 -0
- package/dist/two-factor/crypto.js +94 -0
- package/dist/two-factor/crypto.js.map +1 -0
- package/dist/two-factor/errors.d.ts +82 -0
- package/dist/two-factor/errors.d.ts.map +1 -0
- package/dist/two-factor/errors.js +112 -0
- package/dist/two-factor/errors.js.map +1 -0
- package/dist/two-factor/index.d.ts +18 -0
- package/dist/two-factor/index.d.ts.map +1 -0
- package/dist/two-factor/index.js +25 -0
- package/dist/two-factor/index.js.map +1 -0
- package/dist/two-factor/otp.d.ts +43 -0
- package/dist/two-factor/otp.d.ts.map +1 -0
- package/dist/two-factor/otp.js +57 -0
- package/dist/two-factor/otp.js.map +1 -0
- package/dist/two-factor/recovery.d.ts +45 -0
- package/dist/two-factor/recovery.d.ts.map +1 -0
- package/dist/two-factor/recovery.js +104 -0
- package/dist/two-factor/recovery.js.map +1 -0
- package/dist/two-factor/repository.d.ts +156 -0
- package/dist/two-factor/repository.d.ts.map +1 -0
- package/dist/two-factor/repository.js +251 -0
- package/dist/two-factor/repository.js.map +1 -0
- package/dist/two-factor/service.d.ts +232 -0
- package/dist/two-factor/service.d.ts.map +1 -0
- package/dist/two-factor/service.js +599 -0
- package/dist/two-factor/service.js.map +1 -0
- package/dist/two-factor/totp.d.ts +57 -0
- package/dist/two-factor/totp.d.ts.map +1 -0
- package/dist/two-factor/totp.js +96 -0
- package/dist/two-factor/totp.js.map +1 -0
- package/dist/two-factor/types.d.ts +152 -0
- package/dist/two-factor/types.d.ts.map +1 -0
- package/dist/two-factor/types.js +64 -0
- package/dist/two-factor/types.js.map +1 -0
- package/dist/users/cache.d.ts +49 -0
- package/dist/users/cache.d.ts.map +1 -0
- package/dist/users/cache.js +140 -0
- package/dist/users/cache.js.map +1 -0
- package/dist/users/claims.d.ts +67 -0
- package/dist/users/claims.d.ts.map +1 -0
- package/dist/users/claims.js +104 -0
- package/dist/users/claims.js.map +1 -0
- package/dist/users/errors.d.ts +25 -0
- package/dist/users/errors.d.ts.map +1 -0
- package/dist/users/errors.js +31 -0
- package/dist/users/errors.js.map +1 -0
- package/dist/users/gdpr.d.ts +110 -0
- package/dist/users/gdpr.d.ts.map +1 -0
- package/dist/users/gdpr.js +211 -0
- package/dist/users/gdpr.js.map +1 -0
- package/dist/users/index.d.ts +20 -0
- package/dist/users/index.d.ts.map +1 -0
- package/dist/users/index.js +19 -0
- package/dist/users/index.js.map +1 -0
- package/dist/users/password.d.ts +85 -0
- package/dist/users/password.d.ts.map +1 -0
- package/dist/users/password.js +138 -0
- package/dist/users/password.js.map +1 -0
- package/dist/users/repository.d.ts +263 -0
- package/dist/users/repository.d.ts.map +1 -0
- package/dist/users/repository.js +499 -0
- package/dist/users/repository.js.map +1 -0
- package/dist/users/service.d.ts +299 -0
- package/dist/users/service.d.ts.map +1 -0
- package/dist/users/service.js +698 -0
- package/dist/users/service.js.map +1 -0
- package/dist/users/types.d.ts +220 -0
- package/dist/users/types.d.ts.map +1 -0
- package/dist/users/types.js +77 -0
- package/dist/users/types.js.map +1 -0
- package/locales/default/en/common.json +10 -0
- package/locales/default/en/consent.json +13 -0
- package/locales/default/en/emails.json +17 -0
- package/locales/default/en/errors.json +21 -0
- package/locales/default/en/forgot-password.json +11 -0
- package/locales/default/en/invitation.json +18 -0
- package/locales/default/en/login.json +17 -0
- package/locales/default/en/logout.json +9 -0
- package/locales/default/en/magic-link.json +12 -0
- package/locales/default/en/reset-password.json +16 -0
- package/locales/default/en/two-factor.json +29 -0
- package/migrations/.gitkeep +0 -0
- package/migrations/001_extensions.sql +22 -0
- package/migrations/002_organizations.sql +43 -0
- package/migrations/003_applications.sql +50 -0
- package/migrations/004_clients.sql +74 -0
- package/migrations/005_users.sql +119 -0
- package/migrations/006_roles_permissions.sql +80 -0
- package/migrations/007_custom_claims.sql +55 -0
- package/migrations/008_config.sql +51 -0
- package/migrations/009_audit_log.sql +31 -0
- package/migrations/010_oidc_adapter.sql +34 -0
- package/migrations/011_seed.sql +53 -0
- package/migrations/012_two_factor.sql +88 -0
- package/migrations/013_client_secret_sha256.sql +22 -0
- package/migrations/014_login_methods.sql +34 -0
- package/migrations/015_signing_key_encryption.sql +20 -0
- package/migrations/016_failed_login_count.sql +27 -0
- package/migrations/017_audit_retention.sql +11 -0
- package/migrations/018_admin_api_enhancements.sql +78 -0
- package/migrations/019_invitation_details.sql +27 -0
- package/migrations/020_two_factor_admin_permission.sql +108 -0
- package/migrations/021_auth_recovery_jobs.sql +74 -0
- package/migrations/022_recovery_artifact_ownership.sql +25 -0
- package/migrations/023_magic_link_authority.sql +39 -0
- package/package.json +106 -0
- package/templates/default/emails/invitation.hbs +39 -0
- package/templates/default/emails/invitation.txt.hbs +18 -0
- package/templates/default/emails/magic-link.hbs +30 -0
- package/templates/default/emails/magic-link.txt.hbs +11 -0
- package/templates/default/emails/otp-code.hbs +31 -0
- package/templates/default/emails/otp-code.txt.hbs +15 -0
- package/templates/default/emails/password-changed.hbs +24 -0
- package/templates/default/emails/password-changed.txt.hbs +7 -0
- package/templates/default/emails/password-reset.hbs +30 -0
- package/templates/default/emails/password-reset.txt.hbs +11 -0
- package/templates/default/emails/welcome.hbs +25 -0
- package/templates/default/emails/welcome.txt.hbs +9 -0
- package/templates/default/layouts/main.hbs +49 -0
- package/templates/default/pages/accept-invite.hbs +25 -0
- package/templates/default/pages/consent.hbs +27 -0
- package/templates/default/pages/error.hbs +15 -0
- package/templates/default/pages/forgot-password.hbs +21 -0
- package/templates/default/pages/invite-expired.hbs +6 -0
- package/templates/default/pages/invite-success.hbs +9 -0
- package/templates/default/pages/login.hbs +108 -0
- package/templates/default/pages/logout-success.hbs +5 -0
- package/templates/default/pages/logout.hbs +24 -0
- package/templates/default/pages/magic-link-sent.hbs +10 -0
- package/templates/default/pages/magic-link-success.hbs +17 -0
- package/templates/default/pages/reset-password.hbs +20 -0
- package/templates/default/pages/reset-success.hbs +9 -0
- package/templates/default/pages/two-factor-setup.hbs +65 -0
- package/templates/default/pages/two-factor-verify.hbs +77 -0
- package/templates/default/partials/flash-messages.hbs +10 -0
- package/templates/default/partials/footer.hbs +4 -0
- package/templates/default/partials/header.hbs +8 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global HTTP security headers middleware.
|
|
3
|
+
*
|
|
4
|
+
* Adds defence-in-depth security headers to every response produced by
|
|
5
|
+
* the Porta application. The headers mitigate a broad class of browser-
|
|
6
|
+
* side attacks (clickjacking, MIME-sniffing, cross-origin information
|
|
7
|
+
* leakage, protocol downgrade) without depending on an upstream reverse
|
|
8
|
+
* proxy — if the proxy is a dumb passthrough, these headers still reach
|
|
9
|
+
* the browser.
|
|
10
|
+
*
|
|
11
|
+
* Headers set on every response:
|
|
12
|
+
* X-Content-Type-Options: nosniff — prevent MIME-sniffing
|
|
13
|
+
* X-Frame-Options: DENY — prevent clickjacking
|
|
14
|
+
* Referrer-Policy: strict-origin-when-cross-origin
|
|
15
|
+
* — limit referrer leakage
|
|
16
|
+
* X-XSS-Protection: 0 — disable legacy XSS filter
|
|
17
|
+
* (buggy; rely on CSP instead)
|
|
18
|
+
* Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
|
|
19
|
+
* — restrict browser APIs
|
|
20
|
+
* Content-Security-Policy — strict default, relaxed for
|
|
21
|
+
* HTML pages (see below)
|
|
22
|
+
*
|
|
23
|
+
* Conditional headers:
|
|
24
|
+
* Strict-Transport-Security — only emitted when the
|
|
25
|
+
* configured issuer URL uses https://
|
|
26
|
+
*
|
|
27
|
+
* Content-Security-Policy strategy:
|
|
28
|
+
* - Default (JSON / non-HTML responses):
|
|
29
|
+
* `default-src 'none'` — nothing to render, nothing to load.
|
|
30
|
+
* - HTML responses (login, consent, 2FA, password-reset, etc.):
|
|
31
|
+
* `default-src 'none'; style-src 'unsafe-inline'; frame-ancestors 'none'`
|
|
32
|
+
* The interaction templates use inline `<style>` blocks and may
|
|
33
|
+
* inject custom branding CSS via `{{{branding.customCss}}}`, hence
|
|
34
|
+
* `style-src 'unsafe-inline'` is required. Login pages must never
|
|
35
|
+
* be embedded in iframes (`frame-ancestors 'none'`).
|
|
36
|
+
*
|
|
37
|
+
* Note: `form-action` is intentionally omitted. Chrome enforces
|
|
38
|
+
* `form-action` on the entire redirect chain of a form submission,
|
|
39
|
+
* not just the immediate target. After login, the OIDC provider
|
|
40
|
+
* redirects through the authorization endpoint and then to the
|
|
41
|
+
* client's `redirect_uri` (a different origin). Chrome blocks
|
|
42
|
+
* this final redirect as a `form-action 'self'` violation, while
|
|
43
|
+
* Firefox only checks the immediate action URL. CSRF tokens are
|
|
44
|
+
* the primary defence against cross-site form attacks; removing
|
|
45
|
+
* `form-action` has negligible security impact given that
|
|
46
|
+
* `script-src 'unsafe-inline'` already allows fetch/XHR exfiltration.
|
|
47
|
+
*
|
|
48
|
+
* Placement:
|
|
49
|
+
* Mount early in the middleware stack — after `errorHandler()` and
|
|
50
|
+
* `requestLogger()` but before any route-specific middleware. This
|
|
51
|
+
* ensures headers are present even on error responses.
|
|
52
|
+
*/
|
|
53
|
+
import { config } from '../config/index.js';
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Exported constants — consumed by tests and by other middleware that may
|
|
56
|
+
// need to reference or override specific values.
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
/**
|
|
59
|
+
* Static security headers applied to every response regardless of
|
|
60
|
+
* content type or route.
|
|
61
|
+
*/
|
|
62
|
+
export const STATIC_SECURITY_HEADERS = Object.freeze({
|
|
63
|
+
'X-Content-Type-Options': 'nosniff',
|
|
64
|
+
'X-Frame-Options': 'DENY',
|
|
65
|
+
'Referrer-Policy': 'strict-origin-when-cross-origin',
|
|
66
|
+
'X-XSS-Protection': '0',
|
|
67
|
+
'Permissions-Policy': 'camera=(), microphone=(), geolocation=(), payment=()',
|
|
68
|
+
});
|
|
69
|
+
/**
|
|
70
|
+
* Strict Content-Security-Policy for non-HTML (JSON, plain-text) responses.
|
|
71
|
+
* Disallows all resource loading — there is nothing for a browser to render
|
|
72
|
+
* in a JSON API response.
|
|
73
|
+
*/
|
|
74
|
+
export const DEFAULT_CSP = "default-src 'none'";
|
|
75
|
+
/**
|
|
76
|
+
* Relaxed Content-Security-Policy for HTML pages (login, consent, 2FA,
|
|
77
|
+
* password reset, invitation, magic-link confirmation, etc.).
|
|
78
|
+
*
|
|
79
|
+
* - `style-src 'unsafe-inline'` — templates use inline `<style>` blocks,
|
|
80
|
+
* inline style attributes, and custom
|
|
81
|
+
* branding CSS injection.
|
|
82
|
+
* - `script-src 'unsafe-inline'` — login.hbs uses an inline `<script>` for
|
|
83
|
+
* dual-method email copying, and
|
|
84
|
+
* two-factor-verify.hbs uses one for
|
|
85
|
+
* OTP/recovery mode toggling.
|
|
86
|
+
* - `frame-ancestors 'none'` — prevents embedding in iframes
|
|
87
|
+
* (modern replacement for X-Frame-Options).
|
|
88
|
+
*
|
|
89
|
+
* `form-action` is intentionally omitted — see module-level doc comment.
|
|
90
|
+
*/
|
|
91
|
+
export const HTML_CSP = "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; frame-ancestors 'none'";
|
|
92
|
+
/**
|
|
93
|
+
* HSTS header value — one year, include subdomains.
|
|
94
|
+
* Only emitted when the configured issuer URL uses https.
|
|
95
|
+
*/
|
|
96
|
+
export const HSTS_VALUE = 'max-age=31536000; includeSubDomains';
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Middleware factory
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
/**
|
|
101
|
+
* Determine whether HSTS should be emitted based on the configured
|
|
102
|
+
* issuer URL. Returns `true` when the issuer starts with `https://`.
|
|
103
|
+
*
|
|
104
|
+
* Evaluated once at middleware creation time (issuer doesn't change
|
|
105
|
+
* at runtime).
|
|
106
|
+
*/
|
|
107
|
+
function shouldEmitHsts() {
|
|
108
|
+
try {
|
|
109
|
+
return config.issuerBaseUrl.startsWith('https://');
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Config may not be available in test environments — safe to skip.
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Create the global security-headers Koa middleware.
|
|
118
|
+
*
|
|
119
|
+
* Sets static security headers on every response **before** downstream
|
|
120
|
+
* handlers run, then adjusts the Content-Security-Policy after the
|
|
121
|
+
* response body is produced — if the response is HTML, the CSP is
|
|
122
|
+
* relaxed to allow inline styles and form submissions.
|
|
123
|
+
*
|
|
124
|
+
* @returns Koa middleware function
|
|
125
|
+
*/
|
|
126
|
+
export function securityHeaders() {
|
|
127
|
+
const emitHsts = shouldEmitHsts();
|
|
128
|
+
return async function securityHeadersMiddleware(ctx, next) {
|
|
129
|
+
// -----------------------------------------------------------------------
|
|
130
|
+
// Phase 1 — set all static headers before downstream processing.
|
|
131
|
+
// This ensures headers are present even when a downstream handler
|
|
132
|
+
// throws and the error handler generates the final response.
|
|
133
|
+
// -----------------------------------------------------------------------
|
|
134
|
+
for (const [name, value] of Object.entries(STATIC_SECURITY_HEADERS)) {
|
|
135
|
+
ctx.set(name, value);
|
|
136
|
+
}
|
|
137
|
+
// Default strict CSP — overridden post-response for HTML pages.
|
|
138
|
+
ctx.set('Content-Security-Policy', DEFAULT_CSP);
|
|
139
|
+
// HSTS — only when the deployment uses HTTPS.
|
|
140
|
+
if (emitHsts) {
|
|
141
|
+
ctx.set('Strict-Transport-Security', HSTS_VALUE);
|
|
142
|
+
}
|
|
143
|
+
// -----------------------------------------------------------------------
|
|
144
|
+
// Phase 2 — run downstream middleware / route handlers.
|
|
145
|
+
// -----------------------------------------------------------------------
|
|
146
|
+
await next();
|
|
147
|
+
// -----------------------------------------------------------------------
|
|
148
|
+
// Phase 3 — adjust CSP for HTML responses.
|
|
149
|
+
// After the response body and content-type have been set by the route
|
|
150
|
+
// handler, check if the response is HTML and apply the relaxed CSP
|
|
151
|
+
// that allows inline styles and form submissions.
|
|
152
|
+
// -----------------------------------------------------------------------
|
|
153
|
+
const contentType = ctx.response.get('Content-Type') || '';
|
|
154
|
+
if (contentType.includes('text/html')) {
|
|
155
|
+
ctx.set('Content-Security-Policy', HTML_CSP);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=security-headers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-headers.js","sourceRoot":"","sources":["../../src/middleware/security-headers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,8EAA8E;AAC9E,0EAA0E;AAC1E,iDAAiD;AACjD,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAqC,MAAM,CAAC,MAAM,CAAC;IACrF,wBAAwB,EAAE,SAAS;IACnC,iBAAiB,EAAE,MAAM;IACzB,iBAAiB,EAAE,iCAAiC;IACpD,kBAAkB,EAAE,GAAG;IACvB,oBAAoB,EAAE,sDAAsD;CAC7E,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,oBAAoB,CAAC;AAEhD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,QAAQ,GACnB,mGAAmG,CAAC;AAEtG;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,qCAAqC,CAAC;AAEhE,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAE9E;;;;;;GAMG;AACH,SAAS,cAAc;IACrB,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,mEAAmE;QACnE,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;IAElC,OAAO,KAAK,UAAU,yBAAyB,CAAC,GAAG,EAAE,IAAI;QACvD,0EAA0E;QAC1E,iEAAiE;QACjE,kEAAkE;QAClE,6DAA6D;QAC7D,0EAA0E;QAC1E,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,EAAE,CAAC;YACpE,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvB,CAAC;QAED,gEAAgE;QAChE,GAAG,CAAC,GAAG,CAAC,yBAAyB,EAAE,WAAW,CAAC,CAAC;QAEhD,8CAA8C;QAC9C,IAAI,QAAQ,EAAE,CAAC;YACb,GAAG,CAAC,GAAG,CAAC,2BAA2B,EAAE,UAAU,CAAC,CAAC;QACnD,CAAC;QAED,0EAA0E;QAC1E,wDAAwD;QACxD,0EAA0E;QAC1E,MAAM,IAAI,EAAE,CAAC;QAEb,0EAA0E;QAC1E,2CAA2C;QAC3C,sEAAsE;QACtE,mEAAmE;QACnE,kDAAkD;QAClD,0EAA0E;QAC1E,MAAM,WAAW,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC3D,IAAI,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YACtC,GAAG,CAAC,GAAG,CAAC,yBAAyB,EAAE,QAAQ,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-tenant resolver middleware.
|
|
3
|
+
*
|
|
4
|
+
* Extracts the organization slug from the URL path, resolves the
|
|
5
|
+
* organization using Redis cache (with DB fallback), validates the
|
|
6
|
+
* organization status, and sets tenant context on ctx.state.
|
|
7
|
+
*
|
|
8
|
+
* This middleware is applied to routes under /:orgSlug/* — it does NOT
|
|
9
|
+
* apply to root-level routes like /health.
|
|
10
|
+
*
|
|
11
|
+
* Resolution flow:
|
|
12
|
+
* 1. Extract orgSlug from route params
|
|
13
|
+
* 2. Check Redis cache: getCachedOrganizationBySlug(orgSlug)
|
|
14
|
+
* 3. If cache miss, query DB: findOrganizationBySlug(orgSlug)
|
|
15
|
+
* 4. If found from DB, cache it: cacheOrganization(org)
|
|
16
|
+
* 5. Validate status:
|
|
17
|
+
* - archived → 404 "Organization not found"
|
|
18
|
+
* - suspended → 403 "Organization is suspended"
|
|
19
|
+
* - active → continue
|
|
20
|
+
* 6. Set ctx.state.organization (full Organization object)
|
|
21
|
+
* 7. Set ctx.state.issuer
|
|
22
|
+
*
|
|
23
|
+
* Returns:
|
|
24
|
+
* - 404 if organization not found or archived
|
|
25
|
+
* - 403 if organization is suspended
|
|
26
|
+
*/
|
|
27
|
+
import type { Middleware } from 'koa';
|
|
28
|
+
/**
|
|
29
|
+
* Create the tenant resolver middleware.
|
|
30
|
+
*
|
|
31
|
+
* Expects the route to have an `:orgSlug` parameter (e.g., `/:orgSlug/*`).
|
|
32
|
+
* Resolves the organization via cache-first strategy and validates status.
|
|
33
|
+
*
|
|
34
|
+
* @returns Koa middleware function
|
|
35
|
+
*/
|
|
36
|
+
export declare function tenantResolver(): Middleware;
|
|
37
|
+
//# sourceMappingURL=tenant-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-resolver.d.ts","sourceRoot":"","sources":["../../src/middleware/tenant-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAStC;;;;;;;GAOG;AACH,wBAAgB,cAAc,IAAI,UAAU,CAyC3C"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-tenant resolver middleware.
|
|
3
|
+
*
|
|
4
|
+
* Extracts the organization slug from the URL path, resolves the
|
|
5
|
+
* organization using Redis cache (with DB fallback), validates the
|
|
6
|
+
* organization status, and sets tenant context on ctx.state.
|
|
7
|
+
*
|
|
8
|
+
* This middleware is applied to routes under /:orgSlug/* — it does NOT
|
|
9
|
+
* apply to root-level routes like /health.
|
|
10
|
+
*
|
|
11
|
+
* Resolution flow:
|
|
12
|
+
* 1. Extract orgSlug from route params
|
|
13
|
+
* 2. Check Redis cache: getCachedOrganizationBySlug(orgSlug)
|
|
14
|
+
* 3. If cache miss, query DB: findOrganizationBySlug(orgSlug)
|
|
15
|
+
* 4. If found from DB, cache it: cacheOrganization(org)
|
|
16
|
+
* 5. Validate status:
|
|
17
|
+
* - archived → 404 "Organization not found"
|
|
18
|
+
* - suspended → 403 "Organization is suspended"
|
|
19
|
+
* - active → continue
|
|
20
|
+
* 6. Set ctx.state.organization (full Organization object)
|
|
21
|
+
* 7. Set ctx.state.issuer
|
|
22
|
+
*
|
|
23
|
+
* Returns:
|
|
24
|
+
* - 404 if organization not found or archived
|
|
25
|
+
* - 403 if organization is suspended
|
|
26
|
+
*/
|
|
27
|
+
import { config } from '../config/index.js';
|
|
28
|
+
import { getCachedOrganizationBySlug, cacheOrganization, } from '../organizations/cache.js';
|
|
29
|
+
import { findOrganizationBySlug } from '../organizations/repository.js';
|
|
30
|
+
/**
|
|
31
|
+
* Create the tenant resolver middleware.
|
|
32
|
+
*
|
|
33
|
+
* Expects the route to have an `:orgSlug` parameter (e.g., `/:orgSlug/*`).
|
|
34
|
+
* Resolves the organization via cache-first strategy and validates status.
|
|
35
|
+
*
|
|
36
|
+
* @returns Koa middleware function
|
|
37
|
+
*/
|
|
38
|
+
export function tenantResolver() {
|
|
39
|
+
return async (ctx, next) => {
|
|
40
|
+
const orgSlug = ctx.params?.orgSlug;
|
|
41
|
+
if (!orgSlug) {
|
|
42
|
+
ctx.throw(404, 'Organization not found');
|
|
43
|
+
}
|
|
44
|
+
// 1. Try Redis cache first
|
|
45
|
+
let org = await getCachedOrganizationBySlug(orgSlug);
|
|
46
|
+
// 2. Cache miss → query database (returns all statuses)
|
|
47
|
+
if (!org) {
|
|
48
|
+
org = await findOrganizationBySlug(orgSlug);
|
|
49
|
+
// 3. Cache the result for subsequent requests
|
|
50
|
+
if (org) {
|
|
51
|
+
await cacheOrganization(org);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// 4. Not found at all
|
|
55
|
+
if (!org) {
|
|
56
|
+
return ctx.throw(404, 'Organization not found');
|
|
57
|
+
}
|
|
58
|
+
// 5. Status-dependent responses
|
|
59
|
+
if (org.status === 'archived') {
|
|
60
|
+
return ctx.throw(404, 'Organization not found');
|
|
61
|
+
}
|
|
62
|
+
if (org.status === 'suspended') {
|
|
63
|
+
return ctx.throw(403, 'Organization is suspended');
|
|
64
|
+
}
|
|
65
|
+
// 6. Set tenant context for downstream middleware and OIDC provider
|
|
66
|
+
ctx.state.organization = org;
|
|
67
|
+
ctx.state.issuer = `${config.issuerBaseUrl}/${org.slug}`;
|
|
68
|
+
await next();
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=tenant-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-resolver.js","sourceRoot":"","sources":["../../src/middleware/tenant-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EACL,2BAA2B,EAC3B,iBAAiB,GAClB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAGxE;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;QAEpC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC;QAC3C,CAAC;QAED,2BAA2B;QAC3B,IAAI,GAAG,GAAwB,MAAM,2BAA2B,CAAC,OAAO,CAAC,CAAC;QAE1E,wDAAwD;QACxD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,GAAG,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC;YAE5C,8CAA8C;YAC9C,IAAI,GAAG,EAAE,CAAC;gBACR,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;QAED,sBAAsB;QACtB,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC;QAClD,CAAC;QAED,gCAAgC;QAChC,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAC9B,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC;QAClD,CAAC;QAED,IAAI,GAAG,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAC/B,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,2BAA2B,CAAC,CAAC;QACrD,CAAC;QAED,oEAAoE;QACpE,GAAG,CAAC,KAAK,CAAC,YAAY,GAAG,GAAG,CAAC;QAC7B,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,MAAM,CAAC,aAAa,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QAEzD,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token and introspection endpoint rate limiter middleware.
|
|
3
|
+
*
|
|
4
|
+
* Protects the OAuth token endpoint (`POST /:orgSlug/oidc/token`) and
|
|
5
|
+
* token introspection endpoint (`POST /:orgSlug/oidc/token/introspection`)
|
|
6
|
+
* against abuse: client-credentials flooding, authorization-code
|
|
7
|
+
* brute-forcing, token enumeration, and general endpoint spam.
|
|
8
|
+
*
|
|
9
|
+
* Rate limiting is per-IP + per-client_id composite key. If `client_id`
|
|
10
|
+
* is not present in the request body (e.g. `client_secret_basic` auth
|
|
11
|
+
* sends credentials in the `Authorization` header), the key falls back
|
|
12
|
+
* to `unknown` — all such requests from the same IP share one counter.
|
|
13
|
+
*
|
|
14
|
+
* Reuses the existing `checkRateLimit()` infrastructure from
|
|
15
|
+
* `src/auth/rate-limiter.ts` which provides Redis INCR+EXPIRE sliding
|
|
16
|
+
* window and graceful degradation on Redis failure.
|
|
17
|
+
*
|
|
18
|
+
* Placement: mount as global middleware **before** the OIDC provider
|
|
19
|
+
* router in `src/server.ts`. The path regex ensures only matching
|
|
20
|
+
* requests are rate-limited — all other paths pass through untouched.
|
|
21
|
+
*
|
|
22
|
+
* @module middleware/token-rate-limiter
|
|
23
|
+
*/
|
|
24
|
+
import type { Middleware } from 'koa';
|
|
25
|
+
import type { RateLimitConfig } from '../auth/rate-limiter.js';
|
|
26
|
+
/**
|
|
27
|
+
* Regex matching the OIDC token endpoint path.
|
|
28
|
+
*
|
|
29
|
+
* Pattern: `/<orgSlug>/oidc/token` where orgSlug starts with a
|
|
30
|
+
* lowercase alphanumeric character followed by zero or more lowercase
|
|
31
|
+
* alphanumeric or hyphen characters.
|
|
32
|
+
*
|
|
33
|
+
* Matches: `/acme/oidc/token`, `/my-org/oidc/token`
|
|
34
|
+
* Rejects: `/api/admin/something`, `/acme/oidc/token/extra`
|
|
35
|
+
*/
|
|
36
|
+
export declare const TOKEN_PATH_REGEX: RegExp;
|
|
37
|
+
/**
|
|
38
|
+
* Rate limit configuration for the token endpoint.
|
|
39
|
+
*
|
|
40
|
+
* 30 requests per 5-minute window — generous enough for legitimate
|
|
41
|
+
* applications (SPAs refreshing tokens, server-side token exchanges)
|
|
42
|
+
* while catching automated abuse.
|
|
43
|
+
*/
|
|
44
|
+
export declare const TOKEN_RATE_LIMIT: RateLimitConfig;
|
|
45
|
+
/**
|
|
46
|
+
* Create the token endpoint rate limiter middleware.
|
|
47
|
+
*
|
|
48
|
+
* Only intercepts `POST` requests to paths matching `TOKEN_PATH_REGEX`.
|
|
49
|
+
* Sets informational `X-RateLimit-*` headers on every token response
|
|
50
|
+
* and returns a `429` with an OAuth-format error body when the limit
|
|
51
|
+
* is exceeded.
|
|
52
|
+
*
|
|
53
|
+
* @returns Koa middleware that rate-limits the OIDC token endpoint
|
|
54
|
+
*/
|
|
55
|
+
export declare function tokenRateLimiter(): Middleware;
|
|
56
|
+
/**
|
|
57
|
+
* Regex matching the OIDC token introspection endpoint path.
|
|
58
|
+
*
|
|
59
|
+
* Pattern: `/<orgSlug>/oidc/token/introspection` where orgSlug uses
|
|
60
|
+
* the same slug format as TOKEN_PATH_REGEX.
|
|
61
|
+
*
|
|
62
|
+
* Matches: `/acme/oidc/token/introspection`, `/my-org/oidc/token/introspection`
|
|
63
|
+
* Rejects: `/acme/oidc/token`, `/api/admin/introspection`
|
|
64
|
+
*/
|
|
65
|
+
export declare const INTROSPECTION_PATH_REGEX: RegExp;
|
|
66
|
+
/**
|
|
67
|
+
* Rate limit configuration for the introspection endpoint.
|
|
68
|
+
*
|
|
69
|
+
* 100 requests per 60-second window — higher than the token endpoint
|
|
70
|
+
* because introspection is used by resource servers to validate tokens
|
|
71
|
+
* on every API call. The higher limit accommodates legitimate traffic
|
|
72
|
+
* while still preventing token enumeration attacks.
|
|
73
|
+
*/
|
|
74
|
+
export declare const INTROSPECTION_RATE_LIMIT: RateLimitConfig;
|
|
75
|
+
/**
|
|
76
|
+
* Create the introspection endpoint rate limiter middleware.
|
|
77
|
+
*
|
|
78
|
+
* Only intercepts `POST` requests to paths matching
|
|
79
|
+
* `INTROSPECTION_PATH_REGEX`. Sets informational `X-RateLimit-*`
|
|
80
|
+
* headers and returns a `429` with an OAuth-format error body when
|
|
81
|
+
* the limit is exceeded.
|
|
82
|
+
*
|
|
83
|
+
* Key format: `ratelimit:introspect:{ip}:{clientId}` — composite key
|
|
84
|
+
* per-IP + per-client_id for granular rate limiting.
|
|
85
|
+
*
|
|
86
|
+
* @returns Koa middleware that rate-limits the OIDC introspection endpoint
|
|
87
|
+
*/
|
|
88
|
+
export declare function introspectionRateLimiter(): Middleware;
|
|
89
|
+
//# sourceMappingURL=token-rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-rate-limiter.d.ts","sourceRoot":"","sources":["../../src/middleware/token-rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAEtC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAO/D;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,QAAwC,CAAC;AAEtE;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAG9B,CAAC;AAMF;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAwD7C;AAMD;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,QAAuD,CAAC;AAE7F;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,EAAE,eAGtC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,IAAI,UAAU,CAqDrD"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token and introspection endpoint rate limiter middleware.
|
|
3
|
+
*
|
|
4
|
+
* Protects the OAuth token endpoint (`POST /:orgSlug/oidc/token`) and
|
|
5
|
+
* token introspection endpoint (`POST /:orgSlug/oidc/token/introspection`)
|
|
6
|
+
* against abuse: client-credentials flooding, authorization-code
|
|
7
|
+
* brute-forcing, token enumeration, and general endpoint spam.
|
|
8
|
+
*
|
|
9
|
+
* Rate limiting is per-IP + per-client_id composite key. If `client_id`
|
|
10
|
+
* is not present in the request body (e.g. `client_secret_basic` auth
|
|
11
|
+
* sends credentials in the `Authorization` header), the key falls back
|
|
12
|
+
* to `unknown` — all such requests from the same IP share one counter.
|
|
13
|
+
*
|
|
14
|
+
* Reuses the existing `checkRateLimit()` infrastructure from
|
|
15
|
+
* `src/auth/rate-limiter.ts` which provides Redis INCR+EXPIRE sliding
|
|
16
|
+
* window and graceful degradation on Redis failure.
|
|
17
|
+
*
|
|
18
|
+
* Placement: mount as global middleware **before** the OIDC provider
|
|
19
|
+
* router in `src/server.ts`. The path regex ensures only matching
|
|
20
|
+
* requests are rate-limited — all other paths pass through untouched.
|
|
21
|
+
*
|
|
22
|
+
* @module middleware/token-rate-limiter
|
|
23
|
+
*/
|
|
24
|
+
import { checkRateLimit } from '../auth/rate-limiter.js';
|
|
25
|
+
import { logger } from '../lib/logger.js';
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Constants — exported for test assertions and documentation
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
/**
|
|
30
|
+
* Regex matching the OIDC token endpoint path.
|
|
31
|
+
*
|
|
32
|
+
* Pattern: `/<orgSlug>/oidc/token` where orgSlug starts with a
|
|
33
|
+
* lowercase alphanumeric character followed by zero or more lowercase
|
|
34
|
+
* alphanumeric or hyphen characters.
|
|
35
|
+
*
|
|
36
|
+
* Matches: `/acme/oidc/token`, `/my-org/oidc/token`
|
|
37
|
+
* Rejects: `/api/admin/something`, `/acme/oidc/token/extra`
|
|
38
|
+
*/
|
|
39
|
+
export const TOKEN_PATH_REGEX = /^\/[a-z0-9][a-z0-9-]*\/oidc\/token$/;
|
|
40
|
+
/**
|
|
41
|
+
* Rate limit configuration for the token endpoint.
|
|
42
|
+
*
|
|
43
|
+
* 30 requests per 5-minute window — generous enough for legitimate
|
|
44
|
+
* applications (SPAs refreshing tokens, server-side token exchanges)
|
|
45
|
+
* while catching automated abuse.
|
|
46
|
+
*/
|
|
47
|
+
export const TOKEN_RATE_LIMIT = {
|
|
48
|
+
max: 30,
|
|
49
|
+
windowSeconds: 300,
|
|
50
|
+
};
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Middleware factory
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
/**
|
|
55
|
+
* Create the token endpoint rate limiter middleware.
|
|
56
|
+
*
|
|
57
|
+
* Only intercepts `POST` requests to paths matching `TOKEN_PATH_REGEX`.
|
|
58
|
+
* Sets informational `X-RateLimit-*` headers on every token response
|
|
59
|
+
* and returns a `429` with an OAuth-format error body when the limit
|
|
60
|
+
* is exceeded.
|
|
61
|
+
*
|
|
62
|
+
* @returns Koa middleware that rate-limits the OIDC token endpoint
|
|
63
|
+
*/
|
|
64
|
+
export function tokenRateLimiter() {
|
|
65
|
+
return async function tokenRateLimiterMiddleware(ctx, next) {
|
|
66
|
+
// Only rate-limit POST to the token endpoint — GET, OPTIONS, etc.
|
|
67
|
+
// and all non-token paths pass through immediately.
|
|
68
|
+
if (ctx.method !== 'POST' || !TOKEN_PATH_REGEX.test(ctx.path)) {
|
|
69
|
+
return next();
|
|
70
|
+
}
|
|
71
|
+
// Extract client_id from the parsed request body if available.
|
|
72
|
+
// The selective bodyParser in server.ts excludes OIDC routes, so
|
|
73
|
+
// ctx.request.body may be empty when this middleware runs.
|
|
74
|
+
// Fall back to 'unknown' — all unidentified clients from the same
|
|
75
|
+
// IP share a single rate limit counter, which is acceptable.
|
|
76
|
+
const body = ctx.request.body;
|
|
77
|
+
const clientId = body?.client_id;
|
|
78
|
+
const clientKey = typeof clientId === 'string' && clientId.length > 0
|
|
79
|
+
? clientId
|
|
80
|
+
: 'unknown';
|
|
81
|
+
// Composite key: per-IP + per-client_id for granular rate limiting
|
|
82
|
+
const key = `ratelimit:token:${ctx.ip}:${clientKey}`;
|
|
83
|
+
const result = await checkRateLimit(key, TOKEN_RATE_LIMIT);
|
|
84
|
+
// Informational headers on all responses — helps legitimate clients
|
|
85
|
+
// monitor their usage and implement backoff strategies.
|
|
86
|
+
ctx.set('X-RateLimit-Limit', String(TOKEN_RATE_LIMIT.max));
|
|
87
|
+
ctx.set('X-RateLimit-Remaining', String(result.remaining));
|
|
88
|
+
if (!result.allowed) {
|
|
89
|
+
logger.warn({
|
|
90
|
+
action: 'token_rate_limit_exceeded',
|
|
91
|
+
ip: ctx.ip,
|
|
92
|
+
clientId: clientKey,
|
|
93
|
+
path: ctx.path,
|
|
94
|
+
}, 'Token endpoint rate limit exceeded');
|
|
95
|
+
ctx.status = 429;
|
|
96
|
+
ctx.set('Retry-After', String(result.retryAfter));
|
|
97
|
+
// OAuth 2.0 error format — clients that parse token endpoint
|
|
98
|
+
// errors can handle this structured response.
|
|
99
|
+
ctx.body = {
|
|
100
|
+
error: 'rate_limit_exceeded',
|
|
101
|
+
error_description: 'Too many token requests. Please try again later.',
|
|
102
|
+
retry_after: result.retryAfter,
|
|
103
|
+
};
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
return next();
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Introspection endpoint rate limiter
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
/**
|
|
113
|
+
* Regex matching the OIDC token introspection endpoint path.
|
|
114
|
+
*
|
|
115
|
+
* Pattern: `/<orgSlug>/oidc/token/introspection` where orgSlug uses
|
|
116
|
+
* the same slug format as TOKEN_PATH_REGEX.
|
|
117
|
+
*
|
|
118
|
+
* Matches: `/acme/oidc/token/introspection`, `/my-org/oidc/token/introspection`
|
|
119
|
+
* Rejects: `/acme/oidc/token`, `/api/admin/introspection`
|
|
120
|
+
*/
|
|
121
|
+
export const INTROSPECTION_PATH_REGEX = /^\/[a-z0-9][a-z0-9-]*\/oidc\/token\/introspection$/;
|
|
122
|
+
/**
|
|
123
|
+
* Rate limit configuration for the introspection endpoint.
|
|
124
|
+
*
|
|
125
|
+
* 100 requests per 60-second window — higher than the token endpoint
|
|
126
|
+
* because introspection is used by resource servers to validate tokens
|
|
127
|
+
* on every API call. The higher limit accommodates legitimate traffic
|
|
128
|
+
* while still preventing token enumeration attacks.
|
|
129
|
+
*/
|
|
130
|
+
export const INTROSPECTION_RATE_LIMIT = {
|
|
131
|
+
max: 100,
|
|
132
|
+
windowSeconds: 60,
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Create the introspection endpoint rate limiter middleware.
|
|
136
|
+
*
|
|
137
|
+
* Only intercepts `POST` requests to paths matching
|
|
138
|
+
* `INTROSPECTION_PATH_REGEX`. Sets informational `X-RateLimit-*`
|
|
139
|
+
* headers and returns a `429` with an OAuth-format error body when
|
|
140
|
+
* the limit is exceeded.
|
|
141
|
+
*
|
|
142
|
+
* Key format: `ratelimit:introspect:{ip}:{clientId}` — composite key
|
|
143
|
+
* per-IP + per-client_id for granular rate limiting.
|
|
144
|
+
*
|
|
145
|
+
* @returns Koa middleware that rate-limits the OIDC introspection endpoint
|
|
146
|
+
*/
|
|
147
|
+
export function introspectionRateLimiter() {
|
|
148
|
+
return async function introspectionRateLimiterMiddleware(ctx, next) {
|
|
149
|
+
// Only rate-limit POST to the introspection endpoint.
|
|
150
|
+
if (ctx.method !== 'POST' || !INTROSPECTION_PATH_REGEX.test(ctx.path)) {
|
|
151
|
+
return next();
|
|
152
|
+
}
|
|
153
|
+
// Extract client_id from the parsed request body if available.
|
|
154
|
+
// Introspection requests carry client credentials — client_id may
|
|
155
|
+
// be in the body (client_secret_post) or absent (client_secret_basic).
|
|
156
|
+
const body = ctx.request.body;
|
|
157
|
+
const clientId = body?.client_id;
|
|
158
|
+
const clientKey = typeof clientId === 'string' && clientId.length > 0
|
|
159
|
+
? clientId
|
|
160
|
+
: 'unknown';
|
|
161
|
+
// Composite key: per-IP + per-client_id — separate namespace from
|
|
162
|
+
// the token endpoint to keep counters independent.
|
|
163
|
+
const key = `ratelimit:introspect:${ctx.ip}:${clientKey}`;
|
|
164
|
+
const result = await checkRateLimit(key, INTROSPECTION_RATE_LIMIT);
|
|
165
|
+
// Informational headers on all matched responses.
|
|
166
|
+
ctx.set('X-RateLimit-Limit', String(INTROSPECTION_RATE_LIMIT.max));
|
|
167
|
+
ctx.set('X-RateLimit-Remaining', String(result.remaining));
|
|
168
|
+
if (!result.allowed) {
|
|
169
|
+
logger.warn({
|
|
170
|
+
action: 'introspection_rate_limit_exceeded',
|
|
171
|
+
ip: ctx.ip,
|
|
172
|
+
clientId: clientKey,
|
|
173
|
+
path: ctx.path,
|
|
174
|
+
}, 'Introspection endpoint rate limit exceeded');
|
|
175
|
+
ctx.status = 429;
|
|
176
|
+
ctx.set('Retry-After', String(result.retryAfter));
|
|
177
|
+
// OAuth 2.0 error format — resource servers parsing introspection
|
|
178
|
+
// errors can handle this structured response.
|
|
179
|
+
ctx.body = {
|
|
180
|
+
error: 'rate_limit_exceeded',
|
|
181
|
+
error_description: 'Too many introspection requests. Please try again later.',
|
|
182
|
+
retry_after: result.retryAfter,
|
|
183
|
+
};
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
return next();
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=token-rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-rate-limiter.js","sourceRoot":"","sources":["../../src/middleware/token-rate-limiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account finder for node-oidc-provider.
|
|
3
|
+
*
|
|
4
|
+
* Maps user records from the database to OIDC account objects with a
|
|
5
|
+
* claims() method. The provider calls findAccount when it needs user
|
|
6
|
+
* information for ID tokens, userinfo endpoint responses, etc.
|
|
7
|
+
*
|
|
8
|
+
* This implementation delegates to the user service for lookup and
|
|
9
|
+
* the claims builder for scope-based claims mapping. The user must
|
|
10
|
+
* be active to be returned — non-active users get undefined (no account).
|
|
11
|
+
*
|
|
12
|
+
* Claims flow:
|
|
13
|
+
* 1. buildUserClaims(user, scopes) → standard OIDC claims (sub, name, email...)
|
|
14
|
+
* 2. buildRoleClaims(userId) → role slugs array (cache-first)
|
|
15
|
+
* 3. buildPermissionClaims(userId) → permission slugs array (cache-first)
|
|
16
|
+
* 4. buildCustomClaims(userId, appId, tokenType) → custom per-app claims
|
|
17
|
+
*
|
|
18
|
+
* The applicationId for custom claims is resolved from the OIDC client
|
|
19
|
+
* context. If unavailable (e.g., no client context), custom claims are
|
|
20
|
+
* skipped gracefully.
|
|
21
|
+
*
|
|
22
|
+
* @see users/service.ts — User lookup for OIDC
|
|
23
|
+
* @see users/claims.ts — Standard OIDC claims builder
|
|
24
|
+
* @see rbac/user-role-service.ts — RBAC claims builders
|
|
25
|
+
* @see custom-claims/service.ts — Custom claims builder
|
|
26
|
+
*/
|
|
27
|
+
/** OIDC Account object — returned by findAccount, used by the provider */
|
|
28
|
+
export interface OidcAccount {
|
|
29
|
+
/** The subject identifier (user UUID) */
|
|
30
|
+
accountId: string;
|
|
31
|
+
/**
|
|
32
|
+
* Return OIDC claims for the user.
|
|
33
|
+
* @param use - Token type ('id_token' or 'userinfo')
|
|
34
|
+
* @param scope - Granted scopes (space-separated)
|
|
35
|
+
* @returns Object with OIDC Standard Claims + RBAC + custom claims
|
|
36
|
+
*/
|
|
37
|
+
claims: (use: string, scope: string) => Promise<Record<string, unknown>>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Find an OIDC account by subject identifier (user ID).
|
|
41
|
+
*
|
|
42
|
+
* Delegates to the user service's findUserForOidc which returns
|
|
43
|
+
* active users only. Returns an account object with a claims() method
|
|
44
|
+
* that builds scope-filtered OIDC Standard Claims merged with RBAC
|
|
45
|
+
* role/permission claims and per-application custom claims.
|
|
46
|
+
*
|
|
47
|
+
* @param ctx - Koa context with OIDC extensions (used to resolve client/app context)
|
|
48
|
+
* @param sub - The subject identifier (user UUID)
|
|
49
|
+
* @returns Account object with claims() method, or undefined if not found
|
|
50
|
+
*/
|
|
51
|
+
export declare function findAccount(ctx: unknown, sub: string): Promise<OidcAccount | undefined>;
|
|
52
|
+
//# sourceMappingURL=account-finder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-finder.d.ts","sourceRoot":"","sources":["../../src/oidc/account-finder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AASH,0EAA0E;AAC1E,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC1E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAmDlC"}
|