@portaidentity/server 1.7.3 → 1.8.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 +5 -0
- package/dist/applications/index.d.ts +2 -2
- package/dist/applications/index.d.ts.map +1 -1
- package/dist/applications/index.js +2 -2
- package/dist/applications/index.js.map +1 -1
- package/dist/applications/repository.d.ts +49 -4
- package/dist/applications/repository.d.ts.map +1 -1
- package/dist/applications/repository.js +148 -10
- package/dist/applications/repository.js.map +1 -1
- package/dist/applications/service.d.ts +35 -17
- package/dist/applications/service.d.ts.map +1 -1
- package/dist/applications/service.js +144 -35
- package/dist/applications/service.js.map +1 -1
- package/dist/applications/types.d.ts +1 -1
- package/dist/applications/types.d.ts.map +1 -1
- package/dist/applications/validators.d.ts +14 -0
- package/dist/applications/validators.d.ts.map +1 -0
- package/dist/applications/validators.js +14 -0
- package/dist/applications/validators.js.map +1 -0
- package/dist/auth/effective-branding.d.ts +35 -0
- package/dist/auth/effective-branding.d.ts.map +1 -0
- package/dist/auth/effective-branding.js +81 -0
- package/dist/auth/effective-branding.js.map +1 -0
- package/dist/auth/email-service.d.ts +3 -8
- package/dist/auth/email-service.d.ts.map +1 -1
- package/dist/auth/email-service.js +22 -30
- package/dist/auth/email-service.js.map +1 -1
- package/dist/auth/i18n.d.ts +5 -0
- package/dist/auth/i18n.d.ts.map +1 -1
- package/dist/auth/i18n.js +6 -5
- package/dist/auth/i18n.js.map +1 -1
- package/dist/auth/rate-limiter.d.ts +6 -10
- package/dist/auth/rate-limiter.d.ts.map +1 -1
- package/dist/auth/rate-limiter.js +12 -16
- package/dist/auth/rate-limiter.js.map +1 -1
- package/dist/auth/recovery-job-processor.d.ts.map +1 -1
- package/dist/auth/recovery-job-processor.js +1 -1
- package/dist/auth/recovery-job-processor.js.map +1 -1
- package/dist/auth/template-engine.d.ts +3 -1
- package/dist/auth/template-engine.d.ts.map +1 -1
- package/dist/auth/template-engine.js +1 -1
- package/dist/auth/template-engine.js.map +1 -1
- package/dist/cli/commands/init.d.ts +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +12 -12
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/error-handler.d.ts +3 -2
- package/dist/cli/error-handler.d.ts.map +1 -1
- package/dist/cli/error-handler.js +9 -2
- package/dist/cli/error-handler.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/clients/index.d.ts +1 -1
- package/dist/clients/index.js +1 -1
- package/dist/clients/repository.d.ts +18 -0
- package/dist/clients/repository.d.ts.map +1 -1
- package/dist/clients/repository.js +36 -0
- package/dist/clients/repository.js.map +1 -1
- package/dist/clients/secret-repository.d.ts +35 -8
- package/dist/clients/secret-repository.d.ts.map +1 -1
- package/dist/clients/secret-repository.js +69 -16
- package/dist/clients/secret-repository.js.map +1 -1
- package/dist/clients/secret-service.d.ts +5 -9
- package/dist/clients/secret-service.d.ts.map +1 -1
- package/dist/clients/secret-service.js +13 -22
- package/dist/clients/secret-service.js.map +1 -1
- package/dist/clients/service.d.ts +10 -13
- package/dist/clients/service.d.ts.map +1 -1
- package/dist/clients/service.js +112 -40
- package/dist/clients/service.js.map +1 -1
- package/dist/clients/types.d.ts +1 -1
- package/dist/clients/types.d.ts.map +1 -1
- package/dist/clients/validators.d.ts +83 -1
- package/dist/clients/validators.d.ts.map +1 -1
- package/dist/clients/validators.js +110 -0
- package/dist/clients/validators.js.map +1 -1
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -0
- package/dist/config/index.js.map +1 -1
- package/dist/config/schema.d.ts +1 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +23 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/custom-claims/repository.d.ts +14 -9
- package/dist/custom-claims/repository.d.ts.map +1 -1
- package/dist/custom-claims/repository.js +38 -11
- package/dist/custom-claims/repository.js.map +1 -1
- package/dist/custom-claims/service.d.ts +11 -6
- package/dist/custom-claims/service.d.ts.map +1 -1
- package/dist/custom-claims/service.js +43 -20
- package/dist/custom-claims/service.js.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/admin-permissions.d.ts +12 -7
- package/dist/lib/admin-permissions.d.ts.map +1 -1
- package/dist/lib/admin-permissions.js +26 -16
- package/dist/lib/admin-permissions.js.map +1 -1
- package/dist/lib/audit-log.d.ts +2 -2
- package/dist/lib/audit-log.d.ts.map +1 -1
- package/dist/lib/audit-log.js +1 -1
- package/dist/lib/audit-log.js.map +1 -1
- package/dist/lib/authority-revocation.d.ts +18 -0
- package/dist/lib/authority-revocation.d.ts.map +1 -0
- package/dist/lib/authority-revocation.js +37 -0
- package/dist/lib/authority-revocation.js.map +1 -0
- package/dist/lib/branding-assets.d.ts +35 -5
- package/dist/lib/branding-assets.d.ts.map +1 -1
- package/dist/lib/branding-assets.js +33 -26
- package/dist/lib/branding-assets.js.map +1 -1
- package/dist/lib/bulk-operations.d.ts +1 -1
- package/dist/lib/bulk-operations.d.ts.map +1 -1
- package/dist/lib/bulk-operations.js +1 -13
- package/dist/lib/bulk-operations.js.map +1 -1
- package/dist/lib/cursor.d.ts +2 -0
- package/dist/lib/cursor.d.ts.map +1 -1
- package/dist/lib/cursor.js +2 -7
- package/dist/lib/cursor.js.map +1 -1
- package/dist/lib/database.d.ts.map +1 -1
- package/dist/lib/database.js +5 -0
- package/dist/lib/database.js.map +1 -1
- package/dist/lib/deletion-cleanup.d.ts +43 -0
- package/dist/lib/deletion-cleanup.d.ts.map +1 -0
- package/dist/lib/deletion-cleanup.js +207 -0
- package/dist/lib/deletion-cleanup.js.map +1 -0
- package/dist/lib/image-validator.d.ts +3 -1
- package/dist/lib/image-validator.d.ts.map +1 -1
- package/dist/lib/image-validator.js +32 -19
- package/dist/lib/image-validator.js.map +1 -1
- package/dist/lib/log-format.d.ts +20 -0
- package/dist/lib/log-format.d.ts.map +1 -0
- package/dist/lib/log-format.js +22 -0
- package/dist/lib/log-format.js.map +1 -0
- package/dist/lib/logger.d.ts +3 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +21 -4
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/redis.d.ts.map +1 -1
- package/dist/lib/redis.js +3 -0
- package/dist/lib/redis.js.map +1 -1
- package/dist/lib/session-tracking.d.ts +73 -11
- package/dist/lib/session-tracking.d.ts.map +1 -1
- package/dist/lib/session-tracking.js +76 -32
- package/dist/lib/session-tracking.js.map +1 -1
- package/dist/lib/signing-key-crypto.d.ts.map +1 -1
- package/dist/lib/signing-key-crypto.js +4 -5
- package/dist/lib/signing-key-crypto.js.map +1 -1
- package/dist/lib/signing-keys.d.ts +3 -4
- package/dist/lib/signing-keys.d.ts.map +1 -1
- package/dist/lib/signing-keys.js +66 -41
- package/dist/lib/signing-keys.js.map +1 -1
- package/dist/lib/super-admin-protection.d.ts +5 -5
- package/dist/lib/super-admin-protection.d.ts.map +1 -1
- package/dist/lib/super-admin-protection.js +6 -9
- package/dist/lib/super-admin-protection.js.map +1 -1
- package/dist/lib/system-config-catalog.d.ts +268 -0
- package/dist/lib/system-config-catalog.d.ts.map +1 -0
- package/dist/lib/system-config-catalog.js +90 -0
- package/dist/lib/system-config-catalog.js.map +1 -0
- package/dist/lib/system-config.d.ts +30 -33
- package/dist/lib/system-config.d.ts.map +1 -1
- package/dist/lib/system-config.js +78 -93
- package/dist/lib/system-config.js.map +1 -1
- package/dist/lib/with-timeout.d.ts +32 -0
- package/dist/lib/with-timeout.d.ts.map +1 -0
- package/dist/lib/with-timeout.js +45 -0
- package/dist/lib/with-timeout.js.map +1 -0
- package/dist/middleware/admin-auth.d.ts +6 -5
- package/dist/middleware/admin-auth.d.ts.map +1 -1
- package/dist/middleware/admin-auth.js +30 -20
- package/dist/middleware/admin-auth.js.map +1 -1
- package/dist/middleware/admin-cors.d.ts +3 -3
- package/dist/middleware/admin-cors.js +3 -3
- package/dist/middleware/admin-mutation-audit.d.ts +3 -2
- package/dist/middleware/admin-mutation-audit.d.ts.map +1 -1
- package/dist/middleware/admin-mutation-audit.js +12 -5
- package/dist/middleware/admin-mutation-audit.js.map +1 -1
- package/dist/middleware/admin-rate-limiter.d.ts +7 -8
- package/dist/middleware/admin-rate-limiter.d.ts.map +1 -1
- package/dist/middleware/admin-rate-limiter.js +7 -8
- package/dist/middleware/admin-rate-limiter.js.map +1 -1
- package/dist/middleware/client-secret-hash.d.ts +10 -29
- package/dist/middleware/client-secret-hash.d.ts.map +1 -1
- package/dist/middleware/client-secret-hash.js +161 -59
- package/dist/middleware/client-secret-hash.js.map +1 -1
- package/dist/middleware/health.d.ts +17 -0
- package/dist/middleware/health.d.ts.map +1 -1
- package/dist/middleware/health.js +25 -21
- package/dist/middleware/health.js.map +1 -1
- package/dist/middleware/ready.d.ts.map +1 -1
- package/dist/middleware/ready.js +1 -12
- package/dist/middleware/ready.js.map +1 -1
- package/dist/middleware/require-existing-organization.d.ts +18 -0
- package/dist/middleware/require-existing-organization.d.ts.map +1 -0
- package/dist/middleware/require-existing-organization.js +38 -0
- package/dist/middleware/require-existing-organization.js.map +1 -0
- package/dist/middleware/require-permission.d.ts.map +1 -1
- package/dist/middleware/require-permission.js +35 -1
- package/dist/middleware/require-permission.js.map +1 -1
- package/dist/middleware/require-user-organization.d.ts.map +1 -1
- package/dist/middleware/require-user-organization.js +4 -2
- package/dist/middleware/require-user-organization.js.map +1 -1
- package/dist/middleware/security-headers.d.ts +18 -0
- package/dist/middleware/security-headers.d.ts.map +1 -1
- package/dist/middleware/security-headers.js +32 -1
- package/dist/middleware/security-headers.js.map +1 -1
- package/dist/middleware/tenant-resolver.d.ts +1 -2
- package/dist/middleware/tenant-resolver.d.ts.map +1 -1
- package/dist/middleware/tenant-resolver.js +1 -5
- package/dist/middleware/tenant-resolver.js.map +1 -1
- package/dist/middleware/token-rate-limiter.d.ts +37 -16
- package/dist/middleware/token-rate-limiter.d.ts.map +1 -1
- package/dist/middleware/token-rate-limiter.js +126 -54
- package/dist/middleware/token-rate-limiter.js.map +1 -1
- package/dist/oidc/account-finder.d.ts +5 -5
- package/dist/oidc/account-finder.d.ts.map +1 -1
- package/dist/oidc/account-finder.js +31 -23
- package/dist/oidc/account-finder.js.map +1 -1
- package/dist/oidc/adapter-factory.d.ts.map +1 -1
- package/dist/oidc/adapter-factory.js +83 -3
- package/dist/oidc/adapter-factory.js.map +1 -1
- package/dist/oidc/configuration.d.ts +3 -8
- package/dist/oidc/configuration.d.ts.map +1 -1
- package/dist/oidc/configuration.js +64 -50
- package/dist/oidc/configuration.js.map +1 -1
- package/dist/oidc/provider.d.ts.map +1 -1
- package/dist/oidc/provider.js +7 -0
- package/dist/oidc/provider.js.map +1 -1
- package/dist/oidc/redis-adapter.d.ts +2 -3
- package/dist/oidc/redis-adapter.d.ts.map +1 -1
- package/dist/oidc/redis-adapter.js +54 -46
- package/dist/oidc/redis-adapter.js.map +1 -1
- package/dist/organizations/branding-url.d.ts +17 -0
- package/dist/organizations/branding-url.d.ts.map +1 -0
- package/dist/organizations/branding-url.js +38 -0
- package/dist/organizations/branding-url.js.map +1 -0
- package/dist/organizations/index.d.ts +2 -2
- package/dist/organizations/index.d.ts.map +1 -1
- package/dist/organizations/index.js +1 -1
- package/dist/organizations/index.js.map +1 -1
- package/dist/organizations/repository.d.ts +22 -32
- package/dist/organizations/repository.d.ts.map +1 -1
- package/dist/organizations/repository.js +64 -45
- package/dist/organizations/repository.js.map +1 -1
- package/dist/organizations/service.d.ts +14 -46
- package/dist/organizations/service.d.ts.map +1 -1
- package/dist/organizations/service.js +60 -112
- package/dist/organizations/service.js.map +1 -1
- package/dist/organizations/types.d.ts +1 -15
- package/dist/organizations/types.d.ts.map +1 -1
- package/dist/organizations/validators.d.ts +41 -0
- package/dist/organizations/validators.d.ts.map +1 -0
- package/dist/organizations/validators.js +48 -0
- package/dist/organizations/validators.js.map +1 -0
- package/dist/portability/apply.d.ts +15 -0
- package/dist/portability/apply.d.ts.map +1 -0
- package/dist/portability/apply.js +274 -0
- package/dist/portability/apply.js.map +1 -0
- package/dist/portability/authorization.d.ts +30 -0
- package/dist/portability/authorization.d.ts.map +1 -0
- package/dist/portability/authorization.js +130 -0
- package/dist/portability/authorization.js.map +1 -0
- package/dist/portability/cleanup.d.ts +29 -0
- package/dist/portability/cleanup.d.ts.map +1 -0
- package/dist/portability/cleanup.js +167 -0
- package/dist/portability/cleanup.js.map +1 -0
- package/dist/portability/export.d.ts +18 -0
- package/dist/portability/export.d.ts.map +1 -0
- package/dist/portability/export.js +125 -0
- package/dist/portability/export.js.map +1 -0
- package/dist/portability/import-repository.d.ts +204 -0
- package/dist/portability/import-repository.d.ts.map +1 -0
- package/dist/portability/import-repository.js +347 -0
- package/dist/portability/import-repository.js.map +1 -0
- package/dist/portability/import-user-client-writers.d.ts +40 -0
- package/dist/portability/import-user-client-writers.d.ts.map +1 -0
- package/dist/portability/import-user-client-writers.js +168 -0
- package/dist/portability/import-user-client-writers.js.map +1 -0
- package/dist/portability/index.d.ts +8 -0
- package/dist/portability/index.d.ts.map +1 -0
- package/dist/portability/index.js +8 -0
- package/dist/portability/index.js.map +1 -0
- package/dist/portability/plan-support.d.ts +137 -0
- package/dist/portability/plan-support.d.ts.map +1 -0
- package/dist/portability/plan-support.js +336 -0
- package/dist/portability/plan-support.js.map +1 -0
- package/dist/portability/plan.d.ts +34 -0
- package/dist/portability/plan.d.ts.map +1 -0
- package/dist/portability/plan.js +450 -0
- package/dist/portability/plan.js.map +1 -0
- package/dist/portability/repository.d.ts +95 -0
- package/dist/portability/repository.d.ts.map +1 -0
- package/dist/portability/repository.js +388 -0
- package/dist/portability/repository.js.map +1 -0
- package/dist/portability/schema.d.ts +589 -0
- package/dist/portability/schema.d.ts.map +1 -0
- package/dist/portability/schema.js +458 -0
- package/dist/portability/schema.js.map +1 -0
- package/dist/portability/types.d.ts +381 -0
- package/dist/portability/types.d.ts.map +1 -0
- package/dist/portability/types.js +19 -0
- package/dist/portability/types.js.map +1 -0
- package/dist/rbac/errors.d.ts +4 -0
- package/dist/rbac/errors.d.ts.map +1 -1
- package/dist/rbac/errors.js +7 -0
- package/dist/rbac/errors.js.map +1 -1
- package/dist/rbac/index.d.ts +3 -3
- package/dist/rbac/index.d.ts.map +1 -1
- package/dist/rbac/index.js +3 -3
- package/dist/rbac/index.js.map +1 -1
- package/dist/rbac/mapping-repository.d.ts +84 -9
- package/dist/rbac/mapping-repository.d.ts.map +1 -1
- package/dist/rbac/mapping-repository.js +222 -55
- package/dist/rbac/mapping-repository.js.map +1 -1
- package/dist/rbac/permission-repository.d.ts +36 -19
- package/dist/rbac/permission-repository.d.ts.map +1 -1
- package/dist/rbac/permission-repository.js +93 -35
- package/dist/rbac/permission-repository.js.map +1 -1
- package/dist/rbac/permission-service.d.ts +18 -19
- package/dist/rbac/permission-service.d.ts.map +1 -1
- package/dist/rbac/permission-service.js +100 -57
- package/dist/rbac/permission-service.js.map +1 -1
- package/dist/rbac/role-repository.d.ts +29 -24
- package/dist/rbac/role-repository.d.ts.map +1 -1
- package/dist/rbac/role-repository.js +66 -43
- package/dist/rbac/role-repository.js.map +1 -1
- package/dist/rbac/role-service.d.ts +35 -32
- package/dist/rbac/role-service.d.ts.map +1 -1
- package/dist/rbac/role-service.js +210 -96
- package/dist/rbac/role-service.js.map +1 -1
- package/dist/rbac/slugs.d.ts +16 -27
- package/dist/rbac/slugs.d.ts.map +1 -1
- package/dist/rbac/slugs.js +36 -59
- package/dist/rbac/slugs.js.map +1 -1
- package/dist/rbac/types.d.ts +2 -4
- package/dist/rbac/types.d.ts.map +1 -1
- package/dist/rbac/types.js.map +1 -1
- package/dist/rbac/user-role-service.d.ts +23 -24
- package/dist/rbac/user-role-service.d.ts.map +1 -1
- package/dist/rbac/user-role-service.js +117 -61
- package/dist/rbac/user-role-service.js.map +1 -1
- package/dist/routes/applications.d.ts +4 -2
- package/dist/routes/applications.d.ts.map +1 -1
- package/dist/routes/applications.js +57 -19
- package/dist/routes/applications.js.map +1 -1
- package/dist/routes/audit.js +1 -1
- package/dist/routes/audit.js.map +1 -1
- package/dist/routes/branding.d.ts +3 -3
- package/dist/routes/branding.d.ts.map +1 -1
- package/dist/routes/branding.js +44 -16
- package/dist/routes/branding.js.map +1 -1
- package/dist/routes/bulk.d.ts +0 -5
- package/dist/routes/bulk.d.ts.map +1 -1
- package/dist/routes/bulk.js +3 -5
- package/dist/routes/bulk.js.map +1 -1
- package/dist/routes/clients.d.ts +4 -4
- package/dist/routes/clients.d.ts.map +1 -1
- package/dist/routes/clients.js +111 -52
- package/dist/routes/clients.js.map +1 -1
- package/dist/routes/config.d.ts +17 -12
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +165 -64
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/custom-claims.d.ts.map +1 -1
- package/dist/routes/custom-claims.js +8 -2
- package/dist/routes/custom-claims.js.map +1 -1
- package/dist/routes/exports.d.ts +2 -1
- package/dist/routes/exports.d.ts.map +1 -1
- package/dist/routes/exports.js +57 -1
- package/dist/routes/exports.js.map +1 -1
- package/dist/routes/imports.d.ts +2 -2
- package/dist/routes/imports.d.ts.map +1 -1
- package/dist/routes/imports.js +95 -40
- package/dist/routes/imports.js.map +1 -1
- package/dist/routes/interactions.d.ts.map +1 -1
- package/dist/routes/interactions.js +16 -34
- package/dist/routes/interactions.js.map +1 -1
- package/dist/routes/invitation.d.ts.map +1 -1
- package/dist/routes/invitation.js +10 -23
- package/dist/routes/invitation.js.map +1 -1
- package/dist/routes/keys.d.ts.map +1 -1
- package/dist/routes/keys.js +26 -13
- package/dist/routes/keys.js.map +1 -1
- package/dist/routes/magic-link.d.ts.map +1 -1
- package/dist/routes/magic-link.js +5 -17
- package/dist/routes/magic-link.js.map +1 -1
- package/dist/routes/organizations.d.ts +1 -2
- package/dist/routes/organizations.d.ts.map +1 -1
- package/dist/routes/organizations.js +43 -104
- package/dist/routes/organizations.js.map +1 -1
- package/dist/routes/password-reset.d.ts.map +1 -1
- package/dist/routes/password-reset.js +13 -35
- package/dist/routes/password-reset.js.map +1 -1
- package/dist/routes/permissions.d.ts +1 -1
- package/dist/routes/permissions.d.ts.map +1 -1
- package/dist/routes/permissions.js +58 -17
- package/dist/routes/permissions.js.map +1 -1
- package/dist/routes/public-branding.d.ts +16 -0
- package/dist/routes/public-branding.d.ts.map +1 -0
- package/dist/routes/public-branding.js +52 -0
- package/dist/routes/public-branding.js.map +1 -0
- package/dist/routes/roles.d.ts +1 -1
- package/dist/routes/roles.d.ts.map +1 -1
- package/dist/routes/roles.js +63 -21
- package/dist/routes/roles.js.map +1 -1
- package/dist/routes/sessions.d.ts.map +1 -1
- package/dist/routes/sessions.js +23 -12
- package/dist/routes/sessions.js.map +1 -1
- package/dist/routes/two-factor.d.ts.map +1 -1
- package/dist/routes/two-factor.js +91 -38
- package/dist/routes/two-factor.js.map +1 -1
- package/dist/routes/user-roles.d.ts.map +1 -1
- package/dist/routes/user-roles.js +56 -9
- package/dist/routes/user-roles.js.map +1 -1
- package/dist/routes/users.d.ts +4 -8
- package/dist/routes/users.d.ts.map +1 -1
- package/dist/routes/users.js +108 -265
- package/dist/routes/users.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +53 -37
- package/dist/server.js.map +1 -1
- package/dist/two-factor/errors.d.ts +9 -0
- package/dist/two-factor/errors.d.ts.map +1 -1
- package/dist/two-factor/errors.js +12 -0
- package/dist/two-factor/errors.js.map +1 -1
- package/dist/two-factor/index.d.ts +5 -4
- package/dist/two-factor/index.d.ts.map +1 -1
- package/dist/two-factor/index.js +4 -4
- package/dist/two-factor/index.js.map +1 -1
- package/dist/two-factor/repository.d.ts +17 -6
- package/dist/two-factor/repository.d.ts.map +1 -1
- package/dist/two-factor/repository.js +37 -9
- package/dist/two-factor/repository.js.map +1 -1
- package/dist/two-factor/service.d.ts.map +1 -1
- package/dist/two-factor/service.js +26 -19
- package/dist/two-factor/service.js.map +1 -1
- package/dist/two-factor/totp.d.ts +13 -2
- package/dist/two-factor/totp.d.ts.map +1 -1
- package/dist/two-factor/totp.js +20 -7
- package/dist/two-factor/totp.js.map +1 -1
- package/dist/two-factor/types.d.ts +3 -0
- package/dist/two-factor/types.d.ts.map +1 -1
- package/dist/two-factor/types.js +5 -0
- package/dist/two-factor/types.js.map +1 -1
- package/dist/users/gdpr.d.ts +2 -40
- package/dist/users/gdpr.d.ts.map +1 -1
- package/dist/users/gdpr.js +3 -110
- package/dist/users/gdpr.js.map +1 -1
- package/dist/users/index.d.ts +5 -4
- package/dist/users/index.d.ts.map +1 -1
- package/dist/users/index.js +5 -4
- package/dist/users/index.js.map +1 -1
- package/dist/users/repository.d.ts +49 -3
- package/dist/users/repository.d.ts.map +1 -1
- package/dist/users/repository.js +169 -18
- package/dist/users/repository.js.map +1 -1
- package/dist/users/service.d.ts +19 -51
- package/dist/users/service.d.ts.map +1 -1
- package/dist/users/service.js +72 -122
- package/dist/users/service.js.map +1 -1
- package/dist/users/types.d.ts +4 -5
- package/dist/users/types.d.ts.map +1 -1
- package/dist/users/types.js.map +1 -1
- package/dist/users/validators.d.ts +28 -0
- package/dist/users/validators.d.ts.map +1 -0
- package/dist/users/validators.js +25 -0
- package/dist/users/validators.js.map +1 -0
- package/locales/default/en/errors.json +2 -1
- package/migrations/024_application_client_correction.sql +39 -0
- package/migrations/025_record_deletion_lifecycle.sql +26 -0
- package/migrations/026_simplify_user_lifecycle.sql +33 -0
- package/migrations/027_branding_asset_size_limits.sql +19 -0
- package/migrations/028_totp_replay_protection.sql +17 -0
- package/migrations/029_public_session_identifiers.sql +16 -0
- package/migrations/030_global_configuration_catalog.sql +39 -0
- package/migrations/031_monotonic_updated_at.sql +28 -0
- package/package.json +2 -2
- package/dist/lib/data-import-plan.d.ts +0 -50
- package/dist/lib/data-import-plan.d.ts.map +0 -1
- package/dist/lib/data-import-plan.js +0 -235
- package/dist/lib/data-import-plan.js.map +0 -1
- package/dist/lib/data-import.d.ts +0 -276
- package/dist/lib/data-import.d.ts.map +0 -1
- package/dist/lib/data-import.js +0 -1694
- package/dist/lib/data-import.js.map +0 -1
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Token and introspection endpoint rate limiter middleware.
|
|
3
3
|
*
|
|
4
|
-
* Protects the OAuth token endpoint (`POST /:orgSlug/
|
|
5
|
-
* token introspection endpoint (`POST /:orgSlug/
|
|
4
|
+
* Protects the OAuth token endpoint (`POST /:orgSlug/token`) and
|
|
5
|
+
* token introspection endpoint (`POST /:orgSlug/token/introspection`)
|
|
6
6
|
* against abuse: client-credentials flooding, authorization-code
|
|
7
7
|
* brute-forcing, token enumeration, and general endpoint spam.
|
|
8
8
|
*
|
|
9
|
-
* Rate limiting is per-IP + per-client_id composite key.
|
|
10
|
-
* is
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* Rate limiting is per-IP + per-client_id composite key. The client
|
|
10
|
+
* identifier is read from the parsed body or from HTTP Basic credentials;
|
|
11
|
+
* when neither is present (unauthenticated spam) the key falls back to
|
|
12
|
+
* `unknown`. Because that identifier is unauthenticated and caller-controlled,
|
|
13
|
+
* an aggregate per-IP counter runs alongside it, so one address cannot flood
|
|
14
|
+
* the endpoint by varying the client identifier.
|
|
13
15
|
*
|
|
14
16
|
* Reuses the existing `checkRateLimit()` infrastructure from
|
|
15
17
|
* `src/auth/rate-limiter.ts` which provides Redis INCR+EXPIRE sliding
|
|
16
18
|
* window and graceful degradation on Redis failure.
|
|
17
19
|
*
|
|
18
|
-
* Placement:
|
|
19
|
-
*
|
|
20
|
-
*
|
|
20
|
+
* Placement: mounted on the OIDC router **after** the request body parser so
|
|
21
|
+
* the client identifier is available, and before the provider callback. The
|
|
22
|
+
* path regex ensures only the token and introspection endpoints are limited —
|
|
23
|
+
* every other path passes through untouched.
|
|
21
24
|
*
|
|
22
25
|
* @module middleware/token-rate-limiter
|
|
23
26
|
*/
|
|
@@ -29,16 +32,52 @@ import { logger } from '../lib/logger.js';
|
|
|
29
32
|
/**
|
|
30
33
|
* Regex matching the OIDC token endpoint path.
|
|
31
34
|
*
|
|
32
|
-
* Pattern: `/<orgSlug>/
|
|
35
|
+
* Pattern: `/<orgSlug>/token` where orgSlug starts with a
|
|
33
36
|
* lowercase alphanumeric character followed by zero or more lowercase
|
|
34
37
|
* alphanumeric or hyphen characters.
|
|
35
38
|
*
|
|
36
|
-
* Matches: `/acme/
|
|
37
|
-
* Rejects: `/api/admin/something`, `/acme/
|
|
39
|
+
* Matches: `/acme/token`, `/my-org/token`
|
|
40
|
+
* Rejects: `/api/admin/something`, `/acme/token/extra`
|
|
38
41
|
*/
|
|
39
|
-
export const TOKEN_PATH_REGEX = /^\/[a-z0-9][a-z0-9-]*\/
|
|
42
|
+
export const TOKEN_PATH_REGEX = /^\/[a-z0-9][a-z0-9-]*\/token$/;
|
|
43
|
+
/** Maximum length of a client identifier accepted for rate-limit bucketing. */
|
|
44
|
+
const MAX_CLIENT_KEY_LENGTH = 255;
|
|
40
45
|
/**
|
|
41
|
-
*
|
|
46
|
+
* Extract the presented OAuth client identifier for rate-limit bucketing.
|
|
47
|
+
*
|
|
48
|
+
* The identifier is read from the parsed form body (`client_secret_post`) or
|
|
49
|
+
* from HTTP Basic credentials (`client_secret_basic`). It is a public value
|
|
50
|
+
* used only to give each client its own counter, so reading it before the
|
|
51
|
+
* provider authenticates the client is safe. Values longer than
|
|
52
|
+
* `MAX_CLIENT_KEY_LENGTH` are treated as absent. When neither form is present
|
|
53
|
+
* the key falls back to `unknown`, so unauthenticated spam from one address
|
|
54
|
+
* shares a single budget.
|
|
55
|
+
*
|
|
56
|
+
* @param ctx - Koa context whose parsed body and Authorization header are read
|
|
57
|
+
* @returns The presented client identifier, or `unknown` when none is present
|
|
58
|
+
*/
|
|
59
|
+
function presentedClientId(ctx) {
|
|
60
|
+
const body = ctx.request.body;
|
|
61
|
+
const bodyClientId = body?.['client_id'];
|
|
62
|
+
if (typeof bodyClientId === 'string' &&
|
|
63
|
+
bodyClientId.length > 0 &&
|
|
64
|
+
bodyClientId.length <= MAX_CLIENT_KEY_LENGTH) {
|
|
65
|
+
return bodyClientId;
|
|
66
|
+
}
|
|
67
|
+
const authorization = ctx.headers.authorization;
|
|
68
|
+
if (authorization !== undefined &&
|
|
69
|
+
authorization.length > 6 &&
|
|
70
|
+
authorization.slice(0, 6).toLowerCase() === 'basic ') {
|
|
71
|
+
const decoded = Buffer.from(authorization.slice(6), 'base64').toString('utf8');
|
|
72
|
+
const separator = decoded.indexOf(':');
|
|
73
|
+
const clientId = separator > 0 ? decoded.slice(0, separator) : '';
|
|
74
|
+
if (clientId.length > 0 && clientId.length <= MAX_CLIENT_KEY_LENGTH)
|
|
75
|
+
return clientId;
|
|
76
|
+
}
|
|
77
|
+
return 'unknown';
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Rate limit configuration for the token endpoint, per client.
|
|
42
81
|
*
|
|
43
82
|
* 30 requests per 5-minute window — generous enough for legitimate
|
|
44
83
|
* applications (SPAs refreshing tokens, server-side token exchanges)
|
|
@@ -48,6 +87,44 @@ export const TOKEN_RATE_LIMIT = {
|
|
|
48
87
|
max: 30,
|
|
49
88
|
windowSeconds: 300,
|
|
50
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* Aggregate token limit for one client address.
|
|
92
|
+
*
|
|
93
|
+
* The per-client key comes from an unauthenticated value, so a caller can vary
|
|
94
|
+
* the client identifier to open a fresh counter for every request. This
|
|
95
|
+
* aggregate bounds the total token traffic from one address, so the endpoint
|
|
96
|
+
* cannot be flooded with junk requests, while many legitimate clients behind
|
|
97
|
+
* one address still receive their own per-client budgets.
|
|
98
|
+
*/
|
|
99
|
+
export const TOKEN_IP_RATE_LIMIT = {
|
|
100
|
+
max: 300,
|
|
101
|
+
windowSeconds: 300,
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Emit the OAuth-format 429 response for a limiter denial.
|
|
105
|
+
*
|
|
106
|
+
* @param ctx - Koa context to mutate
|
|
107
|
+
* @param retryAfter - Seconds until the caller may retry
|
|
108
|
+
* @param description - Client-safe error description
|
|
109
|
+
*/
|
|
110
|
+
function denyRateLimited(ctx, retryAfter, description) {
|
|
111
|
+
// The limiter runs before the OIDC CORS middleware so it can reject before
|
|
112
|
+
// any client lookup. Re-emit the request origin here so a browser client can
|
|
113
|
+
// read the 429 and its Retry-After. A 429 exposes no data, so reflecting the
|
|
114
|
+
// origin is safe; credentialed access is not granted.
|
|
115
|
+
const origin = ctx.headers.origin;
|
|
116
|
+
if (origin !== undefined) {
|
|
117
|
+
ctx.set('Access-Control-Allow-Origin', origin);
|
|
118
|
+
ctx.set('Vary', 'Origin');
|
|
119
|
+
}
|
|
120
|
+
ctx.status = 429;
|
|
121
|
+
ctx.set('Retry-After', String(retryAfter));
|
|
122
|
+
ctx.body = {
|
|
123
|
+
error: 'rate_limit_exceeded',
|
|
124
|
+
error_description: description,
|
|
125
|
+
retry_after: retryAfter,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
51
128
|
// ---------------------------------------------------------------------------
|
|
52
129
|
// Middleware factory
|
|
53
130
|
// ---------------------------------------------------------------------------
|
|
@@ -68,16 +145,15 @@ export function tokenRateLimiter() {
|
|
|
68
145
|
if (ctx.method !== 'POST' || !TOKEN_PATH_REGEX.test(ctx.path)) {
|
|
69
146
|
return next();
|
|
70
147
|
}
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
: 'unknown';
|
|
148
|
+
// An unauthenticated client identifier can be varied per request, so an
|
|
149
|
+
// aggregate per-IP counter bounds total token traffic independently of it.
|
|
150
|
+
const aggregate = await checkRateLimit(`ratelimit:token:ip:${ctx.ip}`, TOKEN_IP_RATE_LIMIT);
|
|
151
|
+
if (!aggregate.allowed) {
|
|
152
|
+
logger.warn({ action: 'token_ip_rate_limit_exceeded', ip: ctx.ip, path: ctx.path }, 'Token endpoint per-IP rate limit exceeded');
|
|
153
|
+
denyRateLimited(ctx, aggregate.retryAfter, 'Too many token requests. Please try again later.');
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const clientKey = presentedClientId(ctx);
|
|
81
157
|
// Composite key: per-IP + per-client_id for granular rate limiting
|
|
82
158
|
const key = `ratelimit:token:${ctx.ip}:${clientKey}`;
|
|
83
159
|
const result = await checkRateLimit(key, TOKEN_RATE_LIMIT);
|
|
@@ -92,15 +168,7 @@ export function tokenRateLimiter() {
|
|
|
92
168
|
clientId: clientKey,
|
|
93
169
|
path: ctx.path,
|
|
94
170
|
}, 'Token endpoint rate limit exceeded');
|
|
95
|
-
ctx.
|
|
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
|
-
};
|
|
171
|
+
denyRateLimited(ctx, result.retryAfter, 'Too many token requests. Please try again later.');
|
|
104
172
|
return;
|
|
105
173
|
}
|
|
106
174
|
return next();
|
|
@@ -112,13 +180,13 @@ export function tokenRateLimiter() {
|
|
|
112
180
|
/**
|
|
113
181
|
* Regex matching the OIDC token introspection endpoint path.
|
|
114
182
|
*
|
|
115
|
-
* Pattern: `/<orgSlug>/
|
|
183
|
+
* Pattern: `/<orgSlug>/token/introspection` where orgSlug uses
|
|
116
184
|
* the same slug format as TOKEN_PATH_REGEX.
|
|
117
185
|
*
|
|
118
|
-
* Matches: `/acme/
|
|
119
|
-
* Rejects: `/acme/
|
|
186
|
+
* Matches: `/acme/token/introspection`, `/my-org/token/introspection`
|
|
187
|
+
* Rejects: `/acme/token`, `/api/admin/introspection`
|
|
120
188
|
*/
|
|
121
|
-
export const INTROSPECTION_PATH_REGEX = /^\/[a-z0-9][a-z0-9-]*\/
|
|
189
|
+
export const INTROSPECTION_PATH_REGEX = /^\/[a-z0-9][a-z0-9-]*\/token\/introspection$/;
|
|
122
190
|
/**
|
|
123
191
|
* Rate limit configuration for the introspection endpoint.
|
|
124
192
|
*
|
|
@@ -131,6 +199,17 @@ export const INTROSPECTION_RATE_LIMIT = {
|
|
|
131
199
|
max: 100,
|
|
132
200
|
windowSeconds: 60,
|
|
133
201
|
};
|
|
202
|
+
/**
|
|
203
|
+
* Aggregate introspection limit for one client address.
|
|
204
|
+
*
|
|
205
|
+
* As on the token endpoint, the per-client key comes from an unauthenticated
|
|
206
|
+
* value and can be varied per request, so this aggregate bounds the total
|
|
207
|
+
* introspection traffic from one address independently of the client value.
|
|
208
|
+
*/
|
|
209
|
+
export const INTROSPECTION_IP_RATE_LIMIT = {
|
|
210
|
+
max: 600,
|
|
211
|
+
windowSeconds: 60,
|
|
212
|
+
};
|
|
134
213
|
/**
|
|
135
214
|
* Create the introspection endpoint rate limiter middleware.
|
|
136
215
|
*
|
|
@@ -150,14 +229,15 @@ export function introspectionRateLimiter() {
|
|
|
150
229
|
if (ctx.method !== 'POST' || !INTROSPECTION_PATH_REGEX.test(ctx.path)) {
|
|
151
230
|
return next();
|
|
152
231
|
}
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
232
|
+
// An unauthenticated client identifier can be varied per request, so an
|
|
233
|
+
// aggregate per-IP counter bounds total introspection traffic.
|
|
234
|
+
const aggregate = await checkRateLimit(`ratelimit:introspect:ip:${ctx.ip}`, INTROSPECTION_IP_RATE_LIMIT);
|
|
235
|
+
if (!aggregate.allowed) {
|
|
236
|
+
logger.warn({ action: 'introspection_ip_rate_limit_exceeded', ip: ctx.ip, path: ctx.path }, 'Introspection endpoint per-IP rate limit exceeded');
|
|
237
|
+
denyRateLimited(ctx, aggregate.retryAfter, 'Too many introspection requests. Please try again later.');
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const clientKey = presentedClientId(ctx);
|
|
161
241
|
// Composite key: per-IP + per-client_id — separate namespace from
|
|
162
242
|
// the token endpoint to keep counters independent.
|
|
163
243
|
const key = `ratelimit:introspect:${ctx.ip}:${clientKey}`;
|
|
@@ -172,15 +252,7 @@ export function introspectionRateLimiter() {
|
|
|
172
252
|
clientId: clientKey,
|
|
173
253
|
path: ctx.path,
|
|
174
254
|
}, 'Introspection endpoint rate limit exceeded');
|
|
175
|
-
ctx.
|
|
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
|
-
};
|
|
255
|
+
denyRateLimited(ctx, result.retryAfter, 'Too many introspection requests. Please try again later.');
|
|
184
256
|
return;
|
|
185
257
|
}
|
|
186
258
|
return next();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-rate-limiter.js","sourceRoot":"","sources":["../../src/middleware/token-rate-limiter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"token-rate-limiter.js","sourceRoot":"","sources":["../../src/middleware/token-rate-limiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Claims flow:
|
|
13
13
|
* 1. buildUserClaims(user, scopes) → standard OIDC claims (sub, name, email...)
|
|
14
|
-
* 2. buildRoleClaims(userId) → role slugs
|
|
15
|
-
* 3. buildPermissionClaims(userId) → permission slugs
|
|
14
|
+
* 2. buildRoleClaims(userId, appId) → application-owned role slugs
|
|
15
|
+
* 3. buildPermissionClaims(userId, appId) → application-owned permission slugs
|
|
16
16
|
* 4. buildCustomClaims(userId, appId, tokenType) → custom per-app claims
|
|
17
17
|
*
|
|
18
|
-
* The applicationId for custom claims is resolved from
|
|
19
|
-
*
|
|
20
|
-
* skipped
|
|
18
|
+
* The applicationId for RBAC and custom claims is resolved from private OIDC
|
|
19
|
+
* client metadata. If it is unavailable or malformed, authority fails closed:
|
|
20
|
+
* RBAC arrays are empty and custom claims are skipped while standard claims remain.
|
|
21
21
|
*
|
|
22
22
|
* @see users/service.ts — User lookup for OIDC
|
|
23
23
|
* @see users/claims.ts — Standard OIDC claims builder
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-finder.d.ts","sourceRoot":"","sources":["../../src/oidc/account-finder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;
|
|
1
|
+
{"version":3,"file":"account-finder.d.ts","sourceRoot":"","sources":["../../src/oidc/account-finder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAeH,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,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAsD7F"}
|
|
@@ -11,24 +11,29 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Claims flow:
|
|
13
13
|
* 1. buildUserClaims(user, scopes) → standard OIDC claims (sub, name, email...)
|
|
14
|
-
* 2. buildRoleClaims(userId) → role slugs
|
|
15
|
-
* 3. buildPermissionClaims(userId) → permission slugs
|
|
14
|
+
* 2. buildRoleClaims(userId, appId) → application-owned role slugs
|
|
15
|
+
* 3. buildPermissionClaims(userId, appId) → application-owned permission slugs
|
|
16
16
|
* 4. buildCustomClaims(userId, appId, tokenType) → custom per-app claims
|
|
17
17
|
*
|
|
18
|
-
* The applicationId for custom claims is resolved from
|
|
19
|
-
*
|
|
20
|
-
* skipped
|
|
18
|
+
* The applicationId for RBAC and custom claims is resolved from private OIDC
|
|
19
|
+
* client metadata. If it is unavailable or malformed, authority fails closed:
|
|
20
|
+
* RBAC arrays are empty and custom claims are skipped while standard claims remain.
|
|
21
21
|
*
|
|
22
22
|
* @see users/service.ts — User lookup for OIDC
|
|
23
23
|
* @see users/claims.ts — Standard OIDC claims builder
|
|
24
24
|
* @see rbac/user-role-service.ts — RBAC claims builders
|
|
25
25
|
* @see custom-claims/service.ts — Custom claims builder
|
|
26
26
|
*/
|
|
27
|
+
import { z } from 'zod';
|
|
27
28
|
import { findUserForOidc } from '../users/service.js';
|
|
28
29
|
import { buildUserClaims } from '../users/claims.js';
|
|
29
30
|
import { buildRoleClaims, buildPermissionClaims } from '../rbac/user-role-service.js';
|
|
30
31
|
import { buildCustomClaims } from '../custom-claims/service.js';
|
|
31
32
|
import { logger } from '../lib/logger.js';
|
|
33
|
+
/** Accepts only database-style UUIDs at the provider metadata trust boundary. */
|
|
34
|
+
const applicationIdSchema = z.string().uuid();
|
|
35
|
+
/** Private provider metadata key carrying the client application's database identity. */
|
|
36
|
+
const INTERNAL_APPLICATION_ID = 'urn:porta:internal_application_id';
|
|
32
37
|
/**
|
|
33
38
|
* Find an OIDC account by subject identifier (user ID).
|
|
34
39
|
*
|
|
@@ -61,14 +66,16 @@ export async function findAccount(ctx, sub) {
|
|
|
61
66
|
const scopes = scope ? scope.split(' ') : [];
|
|
62
67
|
// 1. Standard OIDC claims (scope-filtered)
|
|
63
68
|
const standardClaims = buildUserClaims(user, scopes);
|
|
64
|
-
//
|
|
65
|
-
const [roles, permissions] = await Promise.all([
|
|
66
|
-
buildRoleClaims(user.id),
|
|
67
|
-
buildPermissionClaims(user.id),
|
|
68
|
-
]);
|
|
69
|
-
// 3. Custom claims (per-application, filtered by token type)
|
|
70
|
-
// Resolve applicationId from the OIDC client context if available
|
|
69
|
+
// Resolve the application once so every application-owned claim uses the same boundary.
|
|
71
70
|
const applicationId = resolveApplicationId(ctx);
|
|
71
|
+
// Missing client context must not fall back to a union of authority from every application.
|
|
72
|
+
const [roles, permissions] = applicationId
|
|
73
|
+
? await Promise.all([
|
|
74
|
+
buildRoleClaims(user.id, applicationId),
|
|
75
|
+
buildPermissionClaims(user.id, applicationId),
|
|
76
|
+
])
|
|
77
|
+
: [[], []];
|
|
78
|
+
// 3. Custom claims (per-application, filtered by token type)
|
|
72
79
|
let customClaims = {};
|
|
73
80
|
if (applicationId) {
|
|
74
81
|
// Map OIDC 'use' parameter to our TokenType
|
|
@@ -93,23 +100,24 @@ export async function findAccount(ctx, sub) {
|
|
|
93
100
|
/**
|
|
94
101
|
* Resolve the applicationId from the OIDC context.
|
|
95
102
|
*
|
|
96
|
-
* The OIDC provider passes the Koa context which includes the client
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* expected structure (graceful fallback).
|
|
103
|
+
* The OIDC provider passes the Koa context which includes the client object at
|
|
104
|
+
* `ctx.oidc.client`. Only a valid UUID from Porta's internal metadata is accepted.
|
|
105
|
+
* Missing or malformed metadata returns null so authority fails closed.
|
|
100
106
|
*
|
|
101
107
|
* @param ctx - Koa context with OIDC extensions
|
|
102
108
|
* @returns Application UUID or null if not resolvable
|
|
103
109
|
*/
|
|
104
110
|
function resolveApplicationId(ctx) {
|
|
105
|
-
|
|
106
|
-
// Navigate the OIDC context structure: ctx.oidc.client.applicationId
|
|
107
|
-
const oidcCtx = ctx;
|
|
108
|
-
return oidcCtx?.oidc?.client?.applicationId ?? null;
|
|
109
|
-
}
|
|
110
|
-
catch {
|
|
111
|
+
if (typeof ctx !== 'object' || ctx === null)
|
|
111
112
|
return null;
|
|
112
|
-
|
|
113
|
+
const oidc = Reflect.get(ctx, 'oidc');
|
|
114
|
+
if (typeof oidc !== 'object' || oidc === null)
|
|
115
|
+
return null;
|
|
116
|
+
const client = Reflect.get(oidc, 'client');
|
|
117
|
+
if (typeof client !== 'object' || client === null)
|
|
118
|
+
return null;
|
|
119
|
+
const parsed = applicationIdSchema.safeParse(Reflect.get(client, INTERNAL_APPLICATION_ID));
|
|
120
|
+
return parsed.success ? parsed.data : null;
|
|
113
121
|
}
|
|
114
122
|
/**
|
|
115
123
|
* Map the OIDC 'use' parameter to our TokenType enum.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-finder.js","sourceRoot":"","sources":["../../src/oidc/account-finder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"account-finder.js","sourceRoot":"","sources":["../../src/oidc/account-finder.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter-factory.d.ts","sourceRoot":"","sources":["../../src/oidc/adapter-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,eAAe,EAAqB,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAsB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"adapter-factory.d.ts","sourceRoot":"","sources":["../../src/oidc/adapter-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,eAAe,EAAqB,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAsB,MAAM,oBAAoB,CAAC;AAItE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAwG5D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY,aAOvB,CAAC;AAEH;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB;eAad,MAAM;QANxB,mDAAmD;kBAClC,eAAe,GAAG,YAAY;QAE/C,wDAAwD;cAC3C,MAAM;QAanB,wCAAwC;mBACvB,MAAM,WAAW,cAAc,aAAa,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;QAInF;;;;;;WAMG;iBACY,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;QAQ3D,kDAAkD;iCACnB,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;QAK3E,yCAAyC;uBACpB,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;QAKjE,mCAAmC;oBACjB,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;QAIxC;;;;;;;;;;;;WAYG;oBACe,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;QA6CxC,6CAA6C;iCACd,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;;EAIxD"}
|
|
@@ -18,7 +18,84 @@
|
|
|
18
18
|
import { PostgresAdapter, revokeGrantsByIds } from './postgres-adapter.js';
|
|
19
19
|
import { RedisAdapter, cleanupRedisGrants } from './redis-adapter.js';
|
|
20
20
|
import { findForOidc } from '../clients/service.js';
|
|
21
|
+
import { getPool } from '../lib/database.js';
|
|
21
22
|
import { logger } from '../lib/logger.js';
|
|
23
|
+
/** Return true when a value is a plain object suitable for safe field inspection. */
|
|
24
|
+
function isRecord(value) {
|
|
25
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
26
|
+
}
|
|
27
|
+
/** Add a non-empty string field to an authority-reference set. */
|
|
28
|
+
function addStringReference(target, value) {
|
|
29
|
+
if (typeof value === 'string' && value.length > 0)
|
|
30
|
+
target.add(value);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Extract every database-backed identifier carried by an OIDC payload.
|
|
34
|
+
*
|
|
35
|
+
* Session authorization maps use public client IDs as keys, so they must be
|
|
36
|
+
* checked in addition to the top-level fields shared by other artifacts.
|
|
37
|
+
*/
|
|
38
|
+
function collectAuthorityReferences(payload) {
|
|
39
|
+
const references = {
|
|
40
|
+
clientIds: new Set(),
|
|
41
|
+
userIds: new Set(),
|
|
42
|
+
grantIds: new Set(),
|
|
43
|
+
};
|
|
44
|
+
addStringReference(references.clientIds, payload.clientId);
|
|
45
|
+
addStringReference(references.userIds, payload.accountId);
|
|
46
|
+
addStringReference(references.grantIds, payload.grantId);
|
|
47
|
+
if (isRecord(payload.authorizations)) {
|
|
48
|
+
for (const [clientId, authorization] of Object.entries(payload.authorizations)) {
|
|
49
|
+
addStringReference(references.clientIds, clientId);
|
|
50
|
+
if (isRecord(authorization)) {
|
|
51
|
+
addStringReference(references.grantIds, authorization.grantId);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return references;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Verify cached OIDC references against current PostgreSQL authority.
|
|
59
|
+
*
|
|
60
|
+
* One set-based query checks all identifiers. Invalid UUID-shaped user IDs
|
|
61
|
+
* simply fail the text comparison instead of causing a PostgreSQL cast error.
|
|
62
|
+
* A database failure propagates so callers fail closed rather than accepting
|
|
63
|
+
* an artifact whose authority could not be established.
|
|
64
|
+
*/
|
|
65
|
+
async function hasLiveAuthority(payload) {
|
|
66
|
+
const references = collectAuthorityReferences(payload);
|
|
67
|
+
if (references.clientIds.size === 0 &&
|
|
68
|
+
references.userIds.size === 0 &&
|
|
69
|
+
references.grantIds.size === 0) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
const result = await getPool().query(`SELECT
|
|
73
|
+
NOT EXISTS (
|
|
74
|
+
SELECT 1
|
|
75
|
+
FROM unnest($1::text[]) AS requested(client_id)
|
|
76
|
+
LEFT JOIN clients AS client
|
|
77
|
+
ON client.client_id = requested.client_id AND client.status = 'active'
|
|
78
|
+
WHERE client.id IS NULL
|
|
79
|
+
) AS "clientsLive",
|
|
80
|
+
NOT EXISTS (
|
|
81
|
+
SELECT 1
|
|
82
|
+
FROM unnest($2::text[]) AS requested(user_id)
|
|
83
|
+
LEFT JOIN users AS account
|
|
84
|
+
ON account.id::text = requested.user_id AND account.status = 'active'
|
|
85
|
+
WHERE account.id IS NULL
|
|
86
|
+
) AS "usersLive",
|
|
87
|
+
NOT EXISTS (
|
|
88
|
+
SELECT 1
|
|
89
|
+
FROM unnest($3::text[]) AS requested(grant_id)
|
|
90
|
+
LEFT JOIN oidc_payloads AS grant_payload
|
|
91
|
+
ON grant_payload.id = requested.grant_id
|
|
92
|
+
AND grant_payload.type = 'Grant'
|
|
93
|
+
AND (grant_payload.expires_at IS NULL OR grant_payload.expires_at > NOW())
|
|
94
|
+
WHERE grant_payload.id IS NULL
|
|
95
|
+
) AS "grantsLive"`, [[...references.clientIds], [...references.userIds], [...references.grantIds]]);
|
|
96
|
+
const authority = result.rows[0];
|
|
97
|
+
return Boolean(authority?.clientsLive && authority.usersLive && authority.grantsLive);
|
|
98
|
+
}
|
|
22
99
|
/**
|
|
23
100
|
* Models routed to Redis for performance.
|
|
24
101
|
*
|
|
@@ -93,15 +170,18 @@ export function createAdapterFactory() {
|
|
|
93
170
|
if (this.name === 'Client') {
|
|
94
171
|
return findForOidc(id);
|
|
95
172
|
}
|
|
96
|
-
|
|
173
|
+
const payload = await this.delegate.find(id);
|
|
174
|
+
return payload && (await hasLiveAuthority(payload)) ? payload : undefined;
|
|
97
175
|
}
|
|
98
176
|
/** Find an artifact by user code (device flow) */
|
|
99
177
|
async findByUserCode(userCode) {
|
|
100
|
-
|
|
178
|
+
const payload = await this.delegate.findByUserCode(userCode);
|
|
179
|
+
return payload && (await hasLiveAuthority(payload)) ? payload : undefined;
|
|
101
180
|
}
|
|
102
181
|
/** Find an artifact by UID (sessions) */
|
|
103
182
|
async findByUid(uid) {
|
|
104
|
-
|
|
183
|
+
const payload = await this.delegate.findByUid(uid);
|
|
184
|
+
return payload && (await hasLiveAuthority(payload)) ? payload : undefined;
|
|
105
185
|
}
|
|
106
186
|
/** Mark an artifact as consumed */
|
|
107
187
|
async consume(id) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter-factory.js","sourceRoot":"","sources":["../../src/oidc/adapter-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"adapter-factory.js","sourceRoot":"","sources":["../../src/oidc/adapter-factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import type { OidcTtlConfig } from '../lib/system-config.js';
|
|
19
19
|
import type { JwkKeyPair } from '../lib/signing-keys.js';
|
|
20
|
+
import { type EffectiveBranding } from '../auth/effective-branding.js';
|
|
20
21
|
import type { Organization } from '../organizations/types.js';
|
|
21
22
|
/** Parameters required to build the provider configuration */
|
|
22
23
|
export interface BuildProviderConfigParams {
|
|
@@ -48,13 +49,7 @@ export interface BuildProviderConfigParams {
|
|
|
48
49
|
*
|
|
49
50
|
* @returns Default branding with Porta's standard blue primary color
|
|
50
51
|
*/
|
|
51
|
-
export declare function buildDefaultBranding():
|
|
52
|
-
logoUrl: string | null;
|
|
53
|
-
faviconUrl: string | null;
|
|
54
|
-
primaryColor: string;
|
|
55
|
-
companyName: string;
|
|
56
|
-
customCss: string | null;
|
|
57
|
-
};
|
|
52
|
+
export declare function buildDefaultBranding(): EffectiveBranding;
|
|
58
53
|
/**
|
|
59
54
|
* Best-effort organization resolution for oidc-provider rendering hooks.
|
|
60
55
|
*
|
|
@@ -131,7 +126,7 @@ export declare function renderErrorHook(ctx: any, out: {
|
|
|
131
126
|
* scopes, and `issueRefreshToken` returns false — so no refresh token is minted.
|
|
132
127
|
* This is exactly the CLI failure ("no refresh_token available" ~1h later).
|
|
133
128
|
*
|
|
134
|
-
* The upgrade is strictly gated
|
|
129
|
+
* The upgrade is strictly gated: it applies only when the request asked for
|
|
135
130
|
* `offline_access`, the client allows the `refresh_token` grant, AND the grant
|
|
136
131
|
* does not already include `offline_access`. In every other case the grant is
|
|
137
132
|
* returned unchanged — identical to the provider default — so no other client
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["../../src/oidc/configuration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["../../src/oidc/configuration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIzD,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AAKvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAG9D,8DAA8D;AAC9D,MAAM,WAAW,yBAAyB;IACxC,8DAA8D;IAC9D,GAAG,EAAE,aAAa,CAAC;IACnB,iDAAiD;IACjD,IAAI,EAAE;QAAE,IAAI,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC;IAC7B,sEAAsE;IACtE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,6DAA6D;IAC7D,WAAW,EAAE,CACX,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,MAAM,KACR,OAAO,CACR;QACE,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;KAC1E,GACD,SAAS,CACZ,CAAC;IACF,yEAAyE;IACzE,cAAc,EAAE,OAAO,CAAC;IACxB,sFAAsF;IACtF,cAAc,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAC;IACvE,qEAAqE;IACrE,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;CAC9E;AAMD;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,iBAAiB,CASxD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CA8B/F;AAMD;;;;;;;;;;;;;GAaG;AAEH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoD5E;AAED;;;;;;;;;;;GAWG;AAEH,wBAAsB,2BAA2B,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BzE;AAoDD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CAEnC,GAAG,EAAE,GAAG,EACR,GAAG,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAE,EAClD,MAAM,EAAE,KAAK,GACZ,OAAO,CAAC,IAAI,CAAC,CAwDf;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,iBAAiB,CAErC,GAAG,EAAE,GAAG,GACP,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CA6B9B;AAMD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,yBAAyB,GAChC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAuMzB"}
|