@hilbras/keystone 1.6.0 → 2.5.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 +625 -0
- package/README.md +198 -50
- package/dist/cli.js +25 -27
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +23 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +53 -8
- package/dist/config.js.map +1 -1
- package/dist/container.d.ts +4 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js.map +1 -1
- package/dist/db/migrations/0008_flashy_firestar.sql +5 -0
- package/dist/db/migrations/0009_daffy_changeling.sql +1 -0
- package/dist/db/migrations/0010_wealthy_shadow_king.sql +37 -0
- package/dist/db/migrations/0011_overrated_vector.sql +18 -0
- package/dist/db/migrations/0012_brave_darkhawk.sql +28 -0
- package/dist/db/migrations/0013_wonderful_chimera.sql +5 -0
- package/dist/db/migrations/0014_magical_morph.sql +48 -0
- package/dist/db/migrations/0015_chief_vargas.sql +7 -0
- package/dist/db/migrations/0016_gorgeous_charles_xavier.sql +5 -0
- package/dist/db/migrations/0017_flashy_nekra.sql +1 -0
- package/{src/db/migrations/meta/0007_snapshot.json → dist/db/migrations/meta/0008_snapshot.json} +136 -32
- package/{src/db/migrations/meta/0006_snapshot.json → dist/db/migrations/meta/0009_snapshot.json} +359 -30
- package/{src/db/migrations/meta/0005_snapshot.json → dist/db/migrations/meta/0010_snapshot.json} +459 -29
- package/{src/db/migrations/meta/0004_snapshot.json → dist/db/migrations/meta/0011_snapshot.json} +594 -30
- package/dist/db/migrations/meta/0012_snapshot.json +3891 -0
- package/dist/db/migrations/meta/0013_snapshot.json +3919 -0
- package/dist/db/migrations/meta/0014_snapshot.json +4311 -0
- package/dist/db/migrations/meta/0015_snapshot.json +4340 -0
- package/dist/db/migrations/meta/0016_snapshot.json +4363 -0
- package/dist/db/migrations/meta/0017_snapshot.json +4370 -0
- package/dist/db/migrations/meta/_journal.json +70 -0
- package/dist/db/reencryptOidcSecrets.d.ts +2 -0
- package/dist/db/reencryptOidcSecrets.d.ts.map +1 -0
- package/dist/db/reencryptOidcSecrets.js +41 -0
- package/dist/db/reencryptOidcSecrets.js.map +1 -0
- package/dist/db/schema.d.ts +2693 -88
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +157 -4
- package/dist/db/schema.js.map +1 -1
- package/dist/db/seed.js +67 -40
- package/dist/db/seed.js.map +1 -1
- package/dist/di.d.ts.map +1 -1
- package/dist/di.js +11 -3
- package/dist/di.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/errors.d.ts +2 -1
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +10 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/plugins/appContext.d.ts.map +1 -1
- package/dist/plugins/appContext.js +3 -12
- package/dist/plugins/appContext.js.map +1 -1
- package/dist/plugins/audit.d.ts.map +1 -1
- package/dist/plugins/audit.js +26 -6
- package/dist/plugins/audit.js.map +1 -1
- package/dist/plugins/auth.d.ts.map +1 -1
- package/dist/plugins/auth.js +32 -7
- package/dist/plugins/auth.js.map +1 -1
- package/dist/plugins/headerSanitization.d.ts +15 -0
- package/dist/plugins/headerSanitization.d.ts.map +1 -0
- package/dist/plugins/headerSanitization.js +39 -0
- package/dist/plugins/headerSanitization.js.map +1 -0
- package/dist/plugins/metrics.d.ts.map +1 -1
- package/dist/plugins/mtls.d.ts +21 -2
- package/dist/plugins/mtls.d.ts.map +1 -1
- package/dist/plugins/mtls.js +94 -47
- package/dist/plugins/mtls.js.map +1 -1
- package/dist/plugins/permissions.d.ts.map +1 -1
- package/dist/plugins/permissions.js +29 -9
- package/dist/plugins/permissions.js.map +1 -1
- package/dist/plugins/rateLimit.d.ts +16 -0
- package/dist/plugins/rateLimit.d.ts.map +1 -1
- package/dist/plugins/rateLimit.js +16 -3
- package/dist/plugins/rateLimit.js.map +1 -1
- package/dist/plugins/requestLogger.d.ts.map +1 -1
- package/dist/repositories/apiKey.d.ts.map +1 -1
- package/dist/repositories/application.d.ts +1 -1
- package/dist/repositories/application.d.ts.map +1 -1
- package/dist/repositories/application.js +9 -3
- package/dist/repositories/application.js.map +1 -1
- package/dist/repositories/audit.d.ts.map +1 -1
- package/dist/repositories/index.d.ts +3 -0
- package/dist/repositories/index.d.ts.map +1 -1
- package/dist/repositories/index.js +3 -0
- package/dist/repositories/index.js.map +1 -1
- package/dist/repositories/mfaChallenge.d.ts +23 -0
- package/dist/repositories/mfaChallenge.d.ts.map +1 -0
- package/dist/repositories/mfaChallenge.js +71 -0
- package/dist/repositories/mfaChallenge.js.map +1 -0
- package/dist/repositories/oidcConnection.d.ts +27 -43
- package/dist/repositories/oidcConnection.d.ts.map +1 -1
- package/dist/repositories/oidcConnection.js +15 -6
- package/dist/repositories/oidcConnection.js.map +1 -1
- package/dist/repositories/organization.d.ts +9 -9
- package/dist/repositories/organization.d.ts.map +1 -1
- package/dist/repositories/organization.js +91 -37
- package/dist/repositories/organization.js.map +1 -1
- package/dist/repositories/permission.d.ts.map +1 -1
- package/dist/repositories/permission.js +27 -5
- package/dist/repositories/permission.js.map +1 -1
- package/dist/repositories/samlConnection.d.ts +28 -28
- package/dist/repositories/samlConnection.d.ts.map +1 -1
- package/dist/repositories/samlConnection.js +12 -4
- package/dist/repositories/samlConnection.js.map +1 -1
- package/dist/repositories/scimConnection.d.ts +38 -0
- package/dist/repositories/scimConnection.d.ts.map +1 -0
- package/dist/repositories/scimConnection.js +123 -0
- package/dist/repositories/scimConnection.js.map +1 -0
- package/dist/repositories/scimGroup.d.ts +36 -0
- package/dist/repositories/scimGroup.d.ts.map +1 -0
- package/dist/repositories/scimGroup.js +113 -0
- package/dist/repositories/scimGroup.js.map +1 -0
- package/dist/repositories/session.d.ts +24 -13
- package/dist/repositories/session.d.ts.map +1 -1
- package/dist/repositories/session.js +13 -1
- package/dist/repositories/session.js.map +1 -1
- package/dist/repositories/types.d.ts +117 -6
- package/dist/repositories/types.d.ts.map +1 -1
- package/dist/repositories/types.js +7 -1
- package/dist/repositories/types.js.map +1 -1
- package/dist/repositories/user.d.ts +23 -1
- package/dist/repositories/user.d.ts.map +1 -1
- package/dist/repositories/user.js +180 -4
- package/dist/repositories/user.js.map +1 -1
- package/dist/routes/admin/billing.js +4 -4
- package/dist/routes/admin/billing.js.map +1 -1
- package/dist/routes/admin/helpers.d.ts +16 -20
- package/dist/routes/admin/helpers.d.ts.map +1 -1
- package/dist/routes/admin/helpers.js +53 -10
- package/dist/routes/admin/helpers.js.map +1 -1
- package/dist/routes/admin/organizations.d.ts.map +1 -1
- package/dist/routes/admin/organizations.js +105 -69
- package/dist/routes/admin/organizations.js.map +1 -1
- package/dist/routes/admin/permissions.d.ts.map +1 -1
- package/dist/routes/admin/permissions.js +48 -14
- package/dist/routes/admin/permissions.js.map +1 -1
- package/dist/routes/admin/platform.d.ts.map +1 -1
- package/dist/routes/admin/platform.js +86 -35
- package/dist/routes/admin/platform.js.map +1 -1
- package/dist/routes/admin/sso.d.ts.map +1 -1
- package/dist/routes/admin/sso.js +143 -12
- package/dist/routes/admin/sso.js.map +1 -1
- package/dist/routes/admin/webhooks.js +8 -8
- package/dist/routes/admin/webhooks.js.map +1 -1
- package/dist/routes/apiKeys.d.ts.map +1 -1
- package/dist/routes/apiKeys.js +23 -5
- package/dist/routes/apiKeys.js.map +1 -1
- package/dist/routes/auth.d.ts.map +1 -1
- package/dist/routes/auth.js +112 -22
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/authz.d.ts.map +1 -1
- package/dist/routes/authz.js +16 -4
- package/dist/routes/authz.js.map +1 -1
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +16 -14
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/federation.d.ts.map +1 -1
- package/dist/routes/federation.js +17 -3
- package/dist/routes/federation.js.map +1 -1
- package/dist/routes/magicLinks.d.ts.map +1 -1
- package/dist/routes/magicLinks.js +15 -3
- package/dist/routes/magicLinks.js.map +1 -1
- package/dist/routes/oauth.d.ts.map +1 -1
- package/dist/routes/oauth.js +60 -5
- package/dist/routes/oauth.js.map +1 -1
- package/dist/routes/oauth2.d.ts.map +1 -1
- package/dist/routes/oauth2.js +188 -22
- package/dist/routes/oauth2.js.map +1 -1
- package/dist/routes/oidcEnterprise.d.ts.map +1 -1
- package/dist/routes/oidcEnterprise.js +67 -21
- package/dist/routes/oidcEnterprise.js.map +1 -1
- package/dist/routes/password.d.ts.map +1 -1
- package/dist/routes/password.js.map +1 -1
- package/dist/routes/profile.js +4 -4
- package/dist/routes/profile.js.map +1 -1
- package/dist/routes/saml.d.ts +33 -0
- package/dist/routes/saml.d.ts.map +1 -1
- package/dist/routes/saml.js +217 -12
- package/dist/routes/saml.js.map +1 -1
- package/dist/routes/scim.d.ts +13 -0
- package/dist/routes/scim.d.ts.map +1 -1
- package/dist/routes/scim.js +885 -110
- package/dist/routes/scim.js.map +1 -1
- package/dist/routes/sdk.d.ts.map +1 -1
- package/dist/routes/sdk.js +24 -8
- package/dist/routes/sdk.js.map +1 -1
- package/dist/routes/serviceAccounts.d.ts.map +1 -1
- package/dist/routes/serviceAccounts.js +63 -2
- package/dist/routes/serviceAccounts.js.map +1 -1
- package/dist/routes/setup.d.ts.map +1 -1
- package/dist/routes/setup.js +15 -16
- package/dist/routes/setup.js.map +1 -1
- package/dist/routes/smsOtp.js +1 -1
- package/dist/routes/smsOtp.js.map +1 -1
- package/dist/routes/totp.d.ts.map +1 -1
- package/dist/routes/totp.js +112 -23
- package/dist/routes/totp.js.map +1 -1
- package/dist/routes/webauthn.d.ts.map +1 -1
- package/dist/routes/webauthn.js +44 -8
- package/dist/routes/webauthn.js.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/routes/workflows.js +58 -25
- package/dist/routes/workflows.js.map +1 -1
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +141 -40
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/types.d.ts +64 -30
- package/dist/sdk/types.d.ts.map +1 -1
- package/dist/services/application/authentication.d.ts +39 -1
- package/dist/services/application/authentication.d.ts.map +1 -1
- package/dist/services/application/authentication.js +43 -1
- package/dist/services/application/authentication.js.map +1 -1
- package/dist/services/application/identity.d.ts +10 -7
- package/dist/services/application/identity.d.ts.map +1 -1
- package/dist/services/application/identity.js +13 -10
- package/dist/services/application/identity.js.map +1 -1
- package/dist/services/application/organization.d.ts +16 -8
- package/dist/services/application/organization.d.ts.map +1 -1
- package/dist/services/application/organization.js +160 -12
- package/dist/services/application/organization.js.map +1 -1
- package/dist/services/applications.js +2 -2
- package/dist/services/applications.js.map +1 -1
- package/dist/services/audit.d.ts +1 -0
- package/dist/services/audit.d.ts.map +1 -1
- package/dist/services/audit.js +13 -0
- package/dist/services/audit.js.map +1 -1
- package/dist/services/cache.d.ts.map +1 -1
- package/dist/services/configuration/profiles.d.ts +4 -0
- package/dist/services/configuration/profiles.d.ts.map +1 -1
- package/dist/services/configuration/profiles.js +50 -0
- package/dist/services/configuration/profiles.js.map +1 -1
- package/dist/services/configuration.d.ts.map +1 -1
- package/dist/services/configuration.js.map +1 -1
- package/dist/services/connectors/google.d.ts +18 -1
- package/dist/services/connectors/google.d.ts.map +1 -1
- package/dist/services/connectors/google.js +27 -4
- package/dist/services/connectors/google.js.map +1 -1
- package/dist/services/connectors/oidc.d.ts +14 -2
- package/dist/services/connectors/oidc.d.ts.map +1 -1
- package/dist/services/connectors/oidc.js +35 -6
- package/dist/services/connectors/oidc.js.map +1 -1
- package/dist/services/connectors/types.d.ts +13 -1
- package/dist/services/connectors/types.d.ts.map +1 -1
- package/dist/services/connectors/zitadel.d.ts.map +1 -1
- package/dist/services/connectors/zitadel.js +4 -3
- package/dist/services/connectors/zitadel.js.map +1 -1
- package/dist/services/domain/authentication.d.ts +43 -4
- package/dist/services/domain/authentication.d.ts.map +1 -1
- package/dist/services/domain/authentication.js +119 -40
- package/dist/services/domain/authentication.js.map +1 -1
- package/dist/services/domain/authorization.d.ts +11 -0
- package/dist/services/domain/authorization.d.ts.map +1 -1
- package/dist/services/domain/authorization.js +36 -1
- package/dist/services/domain/authorization.js.map +1 -1
- package/dist/services/domain/identity.d.ts +21 -9
- package/dist/services/domain/identity.d.ts.map +1 -1
- package/dist/services/domain/identity.js +187 -10
- package/dist/services/domain/identity.js.map +1 -1
- package/dist/services/domain/organization.d.ts +11 -10
- package/dist/services/domain/organization.d.ts.map +1 -1
- package/dist/services/domain/organization.js +48 -30
- package/dist/services/domain/organization.js.map +1 -1
- package/dist/services/emailVerification.d.ts +2 -2
- package/dist/services/enterpriseSso.d.ts +13 -6
- package/dist/services/enterpriseSso.d.ts.map +1 -1
- package/dist/services/enterpriseSso.js +75 -6
- package/dist/services/enterpriseSso.js.map +1 -1
- package/dist/services/events/subscribers/auditLog.js +1 -1
- package/dist/services/events/subscribers/auditLog.js.map +1 -1
- package/dist/services/events/types.d.ts +7 -1
- package/dist/services/events/types.d.ts.map +1 -1
- package/dist/services/events/types.js.map +1 -1
- package/dist/services/events/validate.d.ts.map +1 -1
- package/dist/services/events/validate.js +33 -0
- package/dist/services/events/validate.js.map +1 -1
- package/dist/services/features.d.ts.map +1 -1
- package/dist/services/features.js.map +1 -1
- package/dist/services/federation.d.ts +6 -1
- package/dist/services/federation.d.ts.map +1 -1
- package/dist/services/federation.js +31 -4
- package/dist/services/federation.js.map +1 -1
- package/dist/services/ipControls.js +2 -2
- package/dist/services/ipControls.js.map +1 -1
- package/dist/services/magicLinks.d.ts +2 -2
- package/dist/services/magicLinks.d.ts.map +1 -1
- package/dist/services/magicLinks.js +13 -9
- package/dist/services/magicLinks.js.map +1 -1
- package/dist/services/mfa.d.ts +53 -0
- package/dist/services/mfa.d.ts.map +1 -0
- package/dist/services/mfa.js +157 -0
- package/dist/services/mfa.js.map +1 -0
- package/dist/services/oauth2.d.ts +88 -14
- package/dist/services/oauth2.d.ts.map +1 -1
- package/dist/services/oauth2.js +125 -16
- package/dist/services/oauth2.js.map +1 -1
- package/dist/services/oidcSecretFormat.d.ts +3 -0
- package/dist/services/oidcSecretFormat.d.ts.map +1 -0
- package/dist/services/oidcSecretFormat.js +5 -0
- package/dist/services/oidcSecretFormat.js.map +1 -0
- package/dist/services/organizations.d.ts +4 -16
- package/dist/services/organizations.d.ts.map +1 -1
- package/dist/services/organizations.js +0 -62
- package/dist/services/organizations.js.map +1 -1
- package/dist/services/permissions.d.ts.map +1 -1
- package/dist/services/permissions.js +9 -1
- package/dist/services/permissions.js.map +1 -1
- package/dist/services/plugins/registry.d.ts.map +1 -1
- package/dist/services/plugins/registry.js.map +1 -1
- package/dist/services/plugins/types.d.ts.map +1 -1
- package/dist/services/queue/bullmq.d.ts.map +1 -1
- package/dist/services/queue/inProcess.d.ts.map +1 -1
- package/dist/services/redirectUri.d.ts +56 -0
- package/dist/services/redirectUri.d.ts.map +1 -0
- package/dist/services/redirectUri.js +129 -0
- package/dist/services/redirectUri.js.map +1 -0
- package/dist/services/redis.d.ts +1 -1
- package/dist/services/redis.d.ts.map +1 -1
- package/dist/services/scimCredentials.d.ts +46 -0
- package/dist/services/scimCredentials.d.ts.map +1 -0
- package/dist/services/scimCredentials.js +123 -0
- package/dist/services/scimCredentials.js.map +1 -0
- package/dist/services/scimLegacyMigration.d.ts +12 -0
- package/dist/services/scimLegacyMigration.d.ts.map +1 -0
- package/dist/services/scimLegacyMigration.js +44 -0
- package/dist/services/scimLegacyMigration.js.map +1 -0
- package/dist/services/secrets/awsKms.d.ts +2 -2
- package/dist/services/secrets/awsKms.d.ts.map +1 -1
- package/dist/services/secrets/awsKms.js +3 -5
- package/dist/services/secrets/awsKms.js.map +1 -1
- package/dist/services/secrets/azureKeyVault.d.ts.map +1 -1
- package/dist/services/secrets/database.d.ts.map +1 -1
- package/dist/services/secrets/database.js.map +1 -1
- package/dist/services/secrets/environment.d.ts.map +1 -1
- package/dist/services/secrets/environment.js.map +1 -1
- package/dist/services/secrets/index.d.ts.map +1 -1
- package/dist/services/secrets/password.d.ts.map +1 -1
- package/dist/services/secrets/password.js +0 -4
- package/dist/services/secrets/password.js.map +1 -1
- package/dist/services/secrets/provider.d.ts +2 -3
- package/dist/services/secrets/provider.d.ts.map +1 -1
- package/dist/services/secrets/vault.d.ts.map +1 -1
- package/dist/services/serviceAccounts.d.ts +20 -0
- package/dist/services/serviceAccounts.d.ts.map +1 -1
- package/dist/services/serviceAccounts.js +47 -1
- package/dist/services/serviceAccounts.js.map +1 -1
- package/dist/services/sessionRevocation.d.ts +76 -0
- package/dist/services/sessionRevocation.d.ts.map +1 -0
- package/dist/services/sessionRevocation.js +86 -0
- package/dist/services/sessionRevocation.js.map +1 -0
- package/dist/services/setup/configWriter.d.ts.map +1 -1
- package/dist/services/setup/configWriter.js.map +1 -1
- package/dist/services/singleUse.d.ts +68 -0
- package/dist/services/singleUse.d.ts.map +1 -0
- package/dist/services/singleUse.js +54 -0
- package/dist/services/singleUse.js.map +1 -0
- package/dist/services/smsOtp.d.ts.map +1 -1
- package/dist/services/smsOtp.js +12 -8
- package/dist/services/smsOtp.js.map +1 -1
- package/dist/services/ssoEndpointPolicy.d.ts +8 -0
- package/dist/services/ssoEndpointPolicy.d.ts.map +1 -0
- package/dist/services/ssoEndpointPolicy.js +136 -0
- package/dist/services/ssoEndpointPolicy.js.map +1 -0
- package/dist/services/stepUp.d.ts +35 -0
- package/dist/services/stepUp.d.ts.map +1 -0
- package/dist/services/stepUp.js +65 -0
- package/dist/services/stepUp.js.map +1 -0
- package/dist/services/tokens.d.ts +39 -11
- package/dist/services/tokens.d.ts.map +1 -1
- package/dist/services/tokens.js +80 -14
- package/dist/services/tokens.js.map +1 -1
- package/dist/services/totp.d.ts +46 -0
- package/dist/services/totp.d.ts.map +1 -1
- package/dist/services/totp.js +178 -41
- package/dist/services/totp.js.map +1 -1
- package/dist/services/trustedProxies.d.ts +90 -0
- package/dist/services/trustedProxies.d.ts.map +1 -0
- package/dist/services/trustedProxies.js +283 -0
- package/dist/services/trustedProxies.js.map +1 -0
- package/dist/services/users.d.ts +20 -12
- package/dist/services/users.d.ts.map +1 -1
- package/dist/services/users.js +24 -51
- package/dist/services/users.js.map +1 -1
- package/dist/services/webauthn.d.ts +8 -4
- package/dist/services/webauthn.d.ts.map +1 -1
- package/dist/services/webauthn.js +4 -4
- package/dist/services/webauthn.js.map +1 -1
- package/dist/services/webhooks.d.ts +20 -20
- package/dist/services/workflows/engine.d.ts +4 -2
- package/dist/services/workflows/engine.d.ts.map +1 -1
- package/dist/services/workflows/engine.js +181 -12
- package/dist/services/workflows/engine.js.map +1 -1
- package/dist/services/workflows/steps.d.ts +2 -0
- package/dist/services/workflows/steps.d.ts.map +1 -1
- package/dist/services/workflows/steps.js +8 -75
- package/dist/services/workflows/steps.js.map +1 -1
- package/dist/setup-server.js +2 -1
- package/dist/setup-server.js.map +1 -1
- package/dist/types.d.ts +33 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +26 -2
- package/dist/types.js.map +1 -1
- package/package.json +25 -3
- package/dist/tests/errors.test.d.ts +0 -2
- package/dist/tests/errors.test.d.ts.map +0 -1
- package/dist/tests/errors.test.js +0 -64
- package/dist/tests/errors.test.js.map +0 -1
- package/dist/tests/events.test.d.ts +0 -2
- package/dist/tests/events.test.d.ts.map +0 -1
- package/dist/tests/events.test.js +0 -45
- package/dist/tests/events.test.js.map +0 -1
- package/dist/tests/featureFlags.test.d.ts +0 -2
- package/dist/tests/featureFlags.test.d.ts.map +0 -1
- package/dist/tests/featureFlags.test.js +0 -24
- package/dist/tests/featureFlags.test.js.map +0 -1
- package/dist/tests/integration/queue.test.d.ts +0 -2
- package/dist/tests/integration/queue.test.d.ts.map +0 -1
- package/dist/tests/integration/queue.test.js +0 -49
- package/dist/tests/integration/queue.test.js.map +0 -1
- package/dist/tests/plugins.test.d.ts +0 -2
- package/dist/tests/plugins.test.d.ts.map +0 -1
- package/dist/tests/plugins.test.js +0 -56
- package/dist/tests/plugins.test.js.map +0 -1
- package/dist/tests/result.test.d.ts +0 -2
- package/dist/tests/result.test.d.ts.map +0 -1
- package/dist/tests/result.test.js +0 -46
- package/dist/tests/result.test.js.map +0 -1
- package/dist/tests/setup/configWriter.test.d.ts +0 -2
- package/dist/tests/setup/configWriter.test.d.ts.map +0 -1
- package/dist/tests/setup/configWriter.test.js +0 -55
- package/dist/tests/setup/configWriter.test.js.map +0 -1
- package/dist/tests/setup/validation.test.d.ts +0 -2
- package/dist/tests/setup/validation.test.d.ts.map +0 -1
- package/dist/tests/setup/validation.test.js +0 -26
- package/dist/tests/setup/validation.test.js.map +0 -1
- package/dist/tests/smoke.test.d.ts +0 -2
- package/dist/tests/smoke.test.d.ts.map +0 -1
- package/dist/tests/smoke.test.js +0 -124
- package/dist/tests/smoke.test.js.map +0 -1
- package/src/cli.ts +0 -172
- package/src/config.ts +0 -143
- package/src/container.ts +0 -48
- package/src/db/index.ts +0 -53
- package/src/db/migrations/0000_skinny_ben_parker.sql +0 -553
- package/src/db/migrations/0001_brief_joseph.sql +0 -10
- package/src/db/migrations/0002_add_account_lockout.sql +0 -3
- package/src/db/migrations/0003_steep_rachel_grey.sql +0 -1
- package/src/db/migrations/0004_clever_bill_hollister.sql +0 -20
- package/src/db/migrations/0005_steep_the_call.sql +0 -2
- package/src/db/migrations/0006_ambitious_boomer.sql +0 -17
- package/src/db/migrations/0007_greedy_vindicator.sql +0 -40
- package/src/db/migrations/meta/0000_snapshot.json +0 -2964
- package/src/db/migrations/meta/0001_snapshot.json +0 -3031
- package/src/db/migrations/meta/0003_snapshot.json +0 -3051
- package/src/db/migrations/meta/_journal.json +0 -62
- package/src/db/schema.ts +0 -676
- package/src/db/seed.ts +0 -146
- package/src/di.ts +0 -81
- package/src/index.ts +0 -326
- package/src/lib/errors.ts +0 -139
- package/src/lib/result.ts +0 -44
- package/src/lib/webhookSignature.ts +0 -41
- package/src/plugins/appContext.ts +0 -76
- package/src/plugins/audit.ts +0 -42
- package/src/plugins/auth.ts +0 -230
- package/src/plugins/metrics.ts +0 -41
- package/src/plugins/mtls.ts +0 -94
- package/src/plugins/permissions.ts +0 -29
- package/src/plugins/rateLimit.ts +0 -133
- package/src/plugins/requestLogger.ts +0 -25
- package/src/plugins/tracing.ts +0 -22
- package/src/repositories/apiKey.ts +0 -158
- package/src/repositories/application.ts +0 -70
- package/src/repositories/audit.ts +0 -32
- package/src/repositories/index.ts +0 -9
- package/src/repositories/oidcConnection.ts +0 -111
- package/src/repositories/organization.ts +0 -167
- package/src/repositories/permission.ts +0 -153
- package/src/repositories/samlConnection.ts +0 -96
- package/src/repositories/session.ts +0 -86
- package/src/repositories/types.ts +0 -134
- package/src/repositories/user.ts +0 -161
- package/src/routes/admin/billing.ts +0 -48
- package/src/routes/admin/helpers.ts +0 -50
- package/src/routes/admin/index.ts +0 -16
- package/src/routes/admin/organizations.ts +0 -299
- package/src/routes/admin/permissions.ts +0 -80
- package/src/routes/admin/platform.ts +0 -335
- package/src/routes/admin/sso.ts +0 -138
- package/src/routes/admin/webhooks.ts +0 -73
- package/src/routes/admin.ts +0 -1
- package/src/routes/apiKeys.ts +0 -60
- package/src/routes/auth.ts +0 -214
- package/src/routes/authz.ts +0 -38
- package/src/routes/config.ts +0 -67
- package/src/routes/emailVerification.ts +0 -73
- package/src/routes/federation.ts +0 -156
- package/src/routes/helpers.ts +0 -17
- package/src/routes/magicLinks.ts +0 -107
- package/src/routes/oauth.ts +0 -167
- package/src/routes/oauth2.ts +0 -284
- package/src/routes/oidcEnterprise.ts +0 -166
- package/src/routes/password.ts +0 -90
- package/src/routes/profile.ts +0 -44
- package/src/routes/saml.ts +0 -209
- package/src/routes/scim.ts +0 -199
- package/src/routes/sdk.ts +0 -141
- package/src/routes/serviceAccounts.ts +0 -124
- package/src/routes/sessions.ts +0 -62
- package/src/routes/setup.ts +0 -315
- package/src/routes/smsOtp.ts +0 -67
- package/src/routes/totp.ts +0 -122
- package/src/routes/webauthn.ts +0 -147
- package/src/routes/workflows.ts +0 -157
- package/src/sdk/index.ts +0 -152
- package/src/sdk/types.ts +0 -54
- package/src/services/anomalyDetection.ts +0 -73
- package/src/services/application/authentication.ts +0 -75
- package/src/services/application/identity.ts +0 -56
- package/src/services/application/index.ts +0 -3
- package/src/services/application/organization.ts +0 -77
- package/src/services/applications.ts +0 -89
- package/src/services/audit.ts +0 -41
- package/src/services/auditExport.ts +0 -59
- package/src/services/billing.ts +0 -87
- package/src/services/cache.ts +0 -131
- package/src/services/configuration/profiles.ts +0 -115
- package/src/services/configuration.ts +0 -120
- package/src/services/connectors/google.ts +0 -92
- package/src/services/connectors/oidc.ts +0 -111
- package/src/services/connectors/registry.ts +0 -124
- package/src/services/connectors/types.ts +0 -38
- package/src/services/connectors/zitadel.ts +0 -77
- package/src/services/devices.ts +0 -76
- package/src/services/domain/authentication.ts +0 -279
- package/src/services/domain/authorization.ts +0 -49
- package/src/services/domain/identity.ts +0 -108
- package/src/services/domain/index.ts +0 -4
- package/src/services/domain/organization.ts +0 -114
- package/src/services/email.ts +0 -167
- package/src/services/emailVerification.ts +0 -75
- package/src/services/enterpriseSso.ts +0 -58
- package/src/services/events/bus.ts +0 -43
- package/src/services/events/subscribers/anomaly.ts +0 -13
- package/src/services/events/subscribers/auditLog.ts +0 -21
- package/src/services/events/subscribers/webhook.ts +0 -39
- package/src/services/events/subscribers/webhookDispatch.ts +0 -14
- package/src/services/events/types.ts +0 -106
- package/src/services/events/validate.ts +0 -118
- package/src/services/featureFlags.ts +0 -90
- package/src/services/features.ts +0 -22
- package/src/services/federation.ts +0 -30
- package/src/services/hibp.ts +0 -28
- package/src/services/ipControls.ts +0 -99
- package/src/services/magicLinks.ts +0 -51
- package/src/services/oauth2.ts +0 -216
- package/src/services/organizations.ts +0 -171
- package/src/services/permissions.ts +0 -146
- package/src/services/plugins/registry.ts +0 -242
- package/src/services/plugins/types.ts +0 -98
- package/src/services/queue/bullmq.ts +0 -104
- package/src/services/queue/inProcess.ts +0 -75
- package/src/services/queue/index.ts +0 -19
- package/src/services/queue/types.ts +0 -26
- package/src/services/redis.ts +0 -16
- package/src/services/secrets/awsKms.ts +0 -97
- package/src/services/secrets/azureKeyVault.ts +0 -171
- package/src/services/secrets/database.ts +0 -166
- package/src/services/secrets/environment.ts +0 -117
- package/src/services/secrets/index.ts +0 -50
- package/src/services/secrets/password.ts +0 -73
- package/src/services/secrets/provider.ts +0 -36
- package/src/services/secrets/vault.ts +0 -149
- package/src/services/secrets.ts +0 -2
- package/src/services/serviceAccounts.ts +0 -74
- package/src/services/setup/configWriter.ts +0 -200
- package/src/services/setup/diagnostics.ts +0 -76
- package/src/services/setup/token.ts +0 -42
- package/src/services/setup/types.ts +0 -40
- package/src/services/setup/validation.ts +0 -205
- package/src/services/sms.ts +0 -81
- package/src/services/smsOtp.ts +0 -74
- package/src/services/tokens.ts +0 -287
- package/src/services/totp.ts +0 -113
- package/src/services/users.ts +0 -239
- package/src/services/webauthn.ts +0 -193
- package/src/services/webhooks.ts +0 -190
- package/src/services/workflows/engine.ts +0 -125
- package/src/services/workflows/steps.ts +0 -129
- package/src/services/zitadel.ts +0 -277
- package/src/setup-server.ts +0 -71
- package/src/tests/errors.test.ts +0 -74
- package/src/tests/events.test.ts +0 -52
- package/src/tests/featureFlags.test.ts +0 -26
- package/src/tests/integration/queue.test.ts +0 -54
- package/src/tests/plugins.test.ts +0 -73
- package/src/tests/result.test.ts +0 -52
- package/src/tests/setup/configWriter.test.ts +0 -69
- package/src/tests/setup/validation.test.ts +0 -29
- package/src/tests/smoke.test.ts +0 -136
- package/src/types.ts +0 -70
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,630 @@ All notable changes to Hilbras Keystone are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.5.0] - 2026-09-27
|
|
9
|
+
|
|
10
|
+
### Security
|
|
11
|
+
|
|
12
|
+
- **The OIDC nonce never reached the Google connector.**
|
|
13
|
+
`GoogleConnector.exchangeCode` overrode the base method and called
|
|
14
|
+
`super.exchangeCode(code, redirectUri)` without forwarding its options, so the
|
|
15
|
+
nonce added in 2.4.0 was discarded. Every other OIDC provider validated the
|
|
16
|
+
nonce; Google — the default, and therefore the most likely to be deployed — did
|
|
17
|
+
not. An ID token minted for a different user or session would have been
|
|
18
|
+
accepted on that path.
|
|
19
|
+
- **The unsigned SAML `Issuer` was never validated.** The response-level
|
|
20
|
+
`<saml:Issuer>` sits outside both signed regions, so rewriting it does not
|
|
21
|
+
invalidate the signature, and neither samlify nor Keystone compared it to the
|
|
22
|
+
registered IdP. SAML 2.0 §2.5.1.5 requires a relying party to verify an
|
|
23
|
+
unsigned issuer against trusted metadata. An assertion could claim to have been
|
|
24
|
+
issued by a different identity provider. The assertion's own issuer is inside
|
|
25
|
+
the signed region and was always covered; this closes the element the signature
|
|
26
|
+
cannot.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- `verifyRelayState` returned by throwing on a missing or non-string signature,
|
|
31
|
+
turning a malformed RelayState — a bad request an attacker fully controls —
|
|
32
|
+
into a 500 rather than a 400. It now returns false for anything malformed.
|
|
33
|
+
- A missing `userinfoEndpoint` was passed to the fetcher behind a non-null
|
|
34
|
+
assertion, producing `userinfoEndpoint must be a valid URL` for a URL that was
|
|
35
|
+
never configured. It is now reported as unconfigured so enrichment is skipped.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- 24 adversarial SAML tests covering tampered signatures, untrusted signing keys,
|
|
40
|
+
rotated-out certificates, unsigned assertions, XML signature wrapping, issuer
|
|
41
|
+
and audience substitution, destination and recipient prefix / superstring / case
|
|
42
|
+
variants, expired assertions, `NotBefore` violations, `InResponseTo` mismatch,
|
|
43
|
+
transaction replay, and five RelayState tampering scenarios.
|
|
44
|
+
- 10 tests for OIDC userinfo endpoint resolution, Google nonce forwarding, and
|
|
45
|
+
organization-scoped membership.
|
|
46
|
+
- `docs/security/enterprise-sso.md` — SAML and OIDC setup, every check applied to
|
|
47
|
+
an assertion or ID token, certificate rotation, endpoint SSRF policy,
|
|
48
|
+
organization scoping, and recommendations.
|
|
49
|
+
|
|
50
|
+
### Already sound, verified rather than assumed
|
|
51
|
+
|
|
52
|
+
Membership is keyed on `(orgId, userId)` throughout, with a unique constraint on
|
|
53
|
+
that pair and provisioning via `ON CONFLICT DO NOTHING` — so two simultaneous
|
|
54
|
+
logins cannot create duplicate memberships. SAML connections are resolved by
|
|
55
|
+
`(connectionId, orgId)`, the transaction is consumed atomically for replay
|
|
56
|
+
protection, RelayState is HMAC-signed and bound to a browser nonce compared in
|
|
57
|
+
constant time, and assertions and messages are both required to be signed. A test
|
|
58
|
+
now pins the membership behaviour rather than leaving it to inspection.
|
|
59
|
+
|
|
60
|
+
## [2.4.0] - 2026-09-27
|
|
61
|
+
|
|
62
|
+
### Security
|
|
63
|
+
|
|
64
|
+
- **The `authorization_code` grant did not authenticate the client.** It looked
|
|
65
|
+
the application up by `client_id` and went straight to redeeming the code,
|
|
66
|
+
never calling `verifyClientSecret`. RFC 6749 §3.2.1 requires a confidential
|
|
67
|
+
client to authenticate at the token endpoint. The code and its PKCE verifier
|
|
68
|
+
were the only factors, so an intercepted code was redeemable by whoever
|
|
69
|
+
intercepted it. Confidential clients must now present `client_secret`; public
|
|
70
|
+
clients are exempt because they have none, and PKCE is what authenticates them.
|
|
71
|
+
- **Redirect URIs accepted script-bearing schemes.** Registration validated with
|
|
72
|
+
`z.string().url()`, which accepts anything the URL parser accepts — verified to
|
|
73
|
+
include `javascript:alert(1)` and
|
|
74
|
+
`data:text/html,<script>alert(1)</script>`. A redirect URI becomes a `Location`
|
|
75
|
+
header that the identity provider itself emits, so an organization admin could
|
|
76
|
+
register one and hand any user who authorized their application a redirect
|
|
77
|
+
toward script execution on the auth domain. Browser policy against top-level
|
|
78
|
+
`javascript:` navigation limits the practical impact, but on an identity
|
|
79
|
+
provider this is not an acceptable input. Registration now also rejects
|
|
80
|
+
wildcards, fragments, embedded credentials, and plaintext HTTP to non-loopback
|
|
81
|
+
hosts. A test records that `z.string().url()` accepted each of these.
|
|
82
|
+
- **OIDC federation sent no nonce and verified none.** `state` proved the callback
|
|
83
|
+
belonged to a login this browser started, but nothing bound the returned ID
|
|
84
|
+
token to that login. Any ID token the provider considered valid was accepted,
|
|
85
|
+
including one minted for a different user or session. A nonce is now generated
|
|
86
|
+
per authorization request, kept in an httpOnly cookie, sent to the provider, and
|
|
87
|
+
required to match.
|
|
88
|
+
- **ID token verification inferred rather than required.** Algorithms are now
|
|
89
|
+
pinned to RS256/ES256/PS256 instead of being derived from the key material, and
|
|
90
|
+
`exp`, `iat`, `iss`, `aud`, `sub` are required rather than validated only when
|
|
91
|
+
present — a token with no expiry was previously accepted indefinitely.
|
|
92
|
+
|
|
93
|
+
### Changed
|
|
94
|
+
|
|
95
|
+
- **Effective scopes are intersected, not trusted.** The client's `scope`
|
|
96
|
+
parameter was stored verbatim, with consent as the only filter. The effective
|
|
97
|
+
set is now registered ∩ requested ∩ consented, and a scope outside the
|
|
98
|
+
registration is refused with `invalid_scope` rather than silently dropped, so a
|
|
99
|
+
client asking for authority it was never granted is visible instead of quietly
|
|
100
|
+
downgraded. An empty `allowed_scopes` preserves existing behaviour.
|
|
101
|
+
- **Public clients.** A `client_type` column distinguishes `confidential` from
|
|
102
|
+
`public`; a public client is issued no secret rather than a secret it is
|
|
103
|
+
expected to ignore, and a check constraint keeps the two halves consistent.
|
|
104
|
+
`client_secret_hash` is now nullable. PKCE is mandatory for a secretless client
|
|
105
|
+
at both `/authorize` and `/token`; the `verifyPKCE` branch that returned true
|
|
106
|
+
when no challenge was registered is gone.
|
|
107
|
+
- **Redirect URIs are compared with one shared helper** at registration and at
|
|
108
|
+
use, so the two cannot drift. Exact string comparison throughout — no prefix
|
|
109
|
+
matching, no normalization, no case folding. The token endpoint's dead
|
|
110
|
+
`redirect_uri IS NULL` tolerance was removed: `redirect_uri` is required at
|
|
111
|
+
`/authorize`, so the branch was unreachable, and it would have accepted any
|
|
112
|
+
redirect URI had the field ever become optional.
|
|
113
|
+
- **Refresh tokens carry the granted scope set** in a new `scopes` column, so the
|
|
114
|
+
authorization context survives rotation instead of being dropped at the first
|
|
115
|
+
refresh. A refresh may narrow the grant but never widen it.
|
|
116
|
+
- **PKCE comparison is constant-time**, so a verifier cannot be recovered byte by
|
|
117
|
+
byte.
|
|
118
|
+
|
|
119
|
+
### Already sound, verified rather than assumed
|
|
120
|
+
|
|
121
|
+
Authorization code consumption was already atomic — a conditional `UPDATE` with
|
|
122
|
+
`used_at IS NULL` and a required returned row — and is now covered by a
|
|
123
|
+
concurrency test (20 parallel redemptions, exactly one winner). PKCE was already
|
|
124
|
+
required at `/authorize` by the request schema, so the dead branch in
|
|
125
|
+
`verifyPKCE` was a latent weakness rather than a live bypass. The refresh grant
|
|
126
|
+
already validated client binding, MFA context, and organization membership.
|
|
127
|
+
|
|
128
|
+
### Added
|
|
129
|
+
|
|
130
|
+
- 42 tests: redirect URI registration and exact matching, PKCE verification,
|
|
131
|
+
scope intersection, client authentication at the token endpoint, public client
|
|
132
|
+
invariants, atomic code consumption, ID token verification against a locally
|
|
133
|
+
signed key (missing / wrong / replayed / expired nonce, no-expiry, wrong issuer,
|
|
134
|
+
wrong audience, foreign signing key), and scope preservation across rotation.
|
|
135
|
+
|
|
136
|
+
## [2.3.0] - 2026-09-27
|
|
137
|
+
|
|
138
|
+
### Security
|
|
139
|
+
|
|
140
|
+
**Completing a password reset did not remove existing access.** It changed the
|
|
141
|
+
password and left every session, every refresh token, and every other
|
|
142
|
+
outstanding reset token working. A password reset is the standard response to a
|
|
143
|
+
suspected compromise, so the previous behaviour defeated its own purpose: an
|
|
144
|
+
attacker who triggered the reset kept their session and kept their access, while
|
|
145
|
+
the victim believed they had locked the intruder out.
|
|
146
|
+
|
|
147
|
+
- A successful reset now invalidates all sessions, all refresh tokens, and all
|
|
148
|
+
outstanding recovery credentials for the account.
|
|
149
|
+
- Reset tokens issued alongside the one used are now spent, so a reset email
|
|
150
|
+
captured earlier cannot be completed after the user has already recovered.
|
|
151
|
+
- Revocation is centralized in `src/services/sessionRevocation.ts`
|
|
152
|
+
(`revokeUserSessions`, `revokeRefreshTokens`,
|
|
153
|
+
`revokeAuthenticationSessions`, `revokeRecoveryCredentials`). It was
|
|
154
|
+
previously open-coded at each call site, which is how the most important site
|
|
155
|
+
came to omit it. The MFA-enablement path now routes through the same function,
|
|
156
|
+
so the rule cannot drift between the two.
|
|
157
|
+
- Revocation is scoped to one user, is idempotent, and honours an exclusion for
|
|
158
|
+
a change the user makes to their own account.
|
|
159
|
+
|
|
160
|
+
API keys are deliberately **not** revoked by a password reset. They are
|
|
161
|
+
separately issued, long-lived credentials belonging to integrations rather than
|
|
162
|
+
to the person, and killing them silently breaks deployments. The residual gap is
|
|
163
|
+
real — a key minted by an attacker who already held the password survives — and
|
|
164
|
+
key expiry and rotation is the right answer rather than coupling key lifetime to
|
|
165
|
+
a human's password.
|
|
166
|
+
|
|
167
|
+
### Already sound, verified rather than assumed
|
|
168
|
+
|
|
169
|
+
- Recovery credentials are 384-bit `crypto.randomBytes`, stored only as a SHA-256
|
|
170
|
+
digest, single-use (since 2.2.0), valid for one hour, rate-limited to 5 per 15
|
|
171
|
+
minutes, and audited.
|
|
172
|
+
- `POST /auth/forgot-password` returns `{ success: true }` on both the found and
|
|
173
|
+
not-found paths, so the response does not disclose whether an account exists.
|
|
174
|
+
(A residual timing difference remains, since the found path sends mail.)
|
|
175
|
+
|
|
176
|
+
### Added
|
|
177
|
+
|
|
178
|
+
- 9 tests, including that an attacker's session and refresh token do not survive
|
|
179
|
+
a reset, that an intercepted earlier reset token is dead, that a bystander's
|
|
180
|
+
credentials are untouched, and that the recovered user can still log in.
|
|
181
|
+
|
|
182
|
+
## [2.2.0] - 2026-09-26
|
|
183
|
+
|
|
184
|
+
### Security
|
|
185
|
+
|
|
186
|
+
Three single-use credentials were validated with a conditional `SELECT` and then
|
|
187
|
+
marked used with an **unconditional** `UPDATE`:
|
|
188
|
+
|
|
189
|
+
```text
|
|
190
|
+
SELECT ... WHERE used_at IS NULL <- conditional
|
|
191
|
+
if (!row) return
|
|
192
|
+
UPDATE ... SET used_at = now() <- UNCONDITIONAL: the race
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Between those two statements, any number of concurrent requests pass the same
|
|
196
|
+
check. Every one of them then succeeds.
|
|
197
|
+
|
|
198
|
+
- **Magic links** could be redeemed by any number of parallel requests, each
|
|
199
|
+
producing a full login. A link that was meant to be usable once was usable
|
|
200
|
+
indefinitely under concurrency.
|
|
201
|
+
- **Password reset tokens** could be spent by parallel requests, each writing a
|
|
202
|
+
different password, last writer winning. This was the most consequential of
|
|
203
|
+
the three: whoever won the race held the account, and an attacker racing the
|
|
204
|
+
legitimate user could take it over.
|
|
205
|
+
- **SMS OTP codes** could be verified more than once concurrently, so a
|
|
206
|
+
six-digit code was not single-use.
|
|
207
|
+
|
|
208
|
+
The fix is to make the write the gate rather than a follow-up:
|
|
209
|
+
|
|
210
|
+
```text
|
|
211
|
+
UPDATE ... SET used_at = now()
|
|
212
|
+
WHERE token_hash = ? AND expires_at > now() AND used_at IS NULL
|
|
213
|
+
RETURNING ...
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
PostgreSQL evaluates that predicate while holding a row lock, so exactly one
|
|
217
|
+
transaction updates the row and observes a returned row. The claim and the
|
|
218
|
+
validation become one statement with no window between them.
|
|
219
|
+
|
|
220
|
+
The four other single-use credentials in scope were already atomic and were
|
|
221
|
+
verified rather than assumed: refresh token rotation, MFA challenges, OAuth2
|
|
222
|
+
authorization codes, and TOTP backup codes all perform a conditional update and
|
|
223
|
+
require a returned row.
|
|
224
|
+
|
|
225
|
+
### Added
|
|
226
|
+
|
|
227
|
+
- `src/services/singleUse.ts` — one atomic claim and refusal-classification
|
|
228
|
+
primitive, used by all three credentials. Consumption now lives in one place,
|
|
229
|
+
so a credential cannot drift back into a hand-rolled read-then-write.
|
|
230
|
+
- Replay detection. A credential presented after it was already spent now emits
|
|
231
|
+
`magic_link_replayed`, `sms_otp_replayed`, or
|
|
232
|
+
`password_reset_token_replayed`, all of which reach the audit log through the
|
|
233
|
+
event bus. Previously a replay was indistinguishable from a typo, so a leaked
|
|
234
|
+
token returning was invisible to an operator. An **expired** credential is
|
|
235
|
+
deliberately not reported as a replay, because that is not a leak.
|
|
236
|
+
- 25 tests, including the plan's 10 / 50 / 100 concurrent-request levels against
|
|
237
|
+
every affected credential, and the same levels through the service entry points
|
|
238
|
+
a route actually calls.
|
|
239
|
+
|
|
240
|
+
### Changed
|
|
241
|
+
|
|
242
|
+
- `resetPasswordWithToken` now spends the token before doing any work, and
|
|
243
|
+
reports an expired link distinctly from an invalid one. Spending other live
|
|
244
|
+
reset tokens for the same user after a successful reset, since any were issued
|
|
245
|
+
alongside the one just used.
|
|
246
|
+
|
|
247
|
+
## [2.1.0] - 2026-09-26
|
|
248
|
+
|
|
249
|
+
### Security
|
|
250
|
+
|
|
251
|
+
Four moderate advisories in the development tree, from `drizzle-kit` pulling
|
|
252
|
+
`@esbuild-kit/esm-loader`, which pinned its own copy of `esbuild@0.18.20`
|
|
253
|
+
(`GHSA-67mh-4wv8-2f99`, fixed in 0.25.0).
|
|
254
|
+
|
|
255
|
+
The advisory allows a website to send requests to an esbuild **dev server** and
|
|
256
|
+
read the response. Keystone never calls esbuild's `serve()` API, the packages
|
|
257
|
+
are `devDependencies`, and `npm audit --omit=dev` was already clean, so this was
|
|
258
|
+
not exploitable here. It was still a real advisory in the tree that builds and
|
|
259
|
+
publishes the artifact, and `npm audit fix --force` offered only a downgrade of
|
|
260
|
+
`drizzle-kit` to 0.18.1, which is a breaking change.
|
|
261
|
+
|
|
262
|
+
Resolved with an `overrides` entry forcing `esbuild >= 0.25.0`, which collapses
|
|
263
|
+
all three copies to 0.28.2 and clears the audit for production and development
|
|
264
|
+
trees alike. Verified that `db:generate`, `db:migrate`, and `db:seed` all still
|
|
265
|
+
work against the forced version, and that no non-dev package resolves esbuild.
|
|
266
|
+
|
|
267
|
+
### Fixed
|
|
268
|
+
|
|
269
|
+
- The published container image shipped 8 HIGH-severity advisories that no
|
|
270
|
+
JavaScript scanner can detect. `npm audit` and OSV both read
|
|
271
|
+
`package-lock.json` and correctly reported zero, because the vulnerable
|
|
272
|
+
packages are not in Keystone's dependency tree: they are the ones bundled
|
|
273
|
+
inside the base image's `npm@10.9.9` (`brace-expansion@2.0.2`,
|
|
274
|
+
`ip-address@10.1.0`, `pacote@19.0.2`, `picomatch@4.0.3`, `sigstore@3.1.0`).
|
|
275
|
+
The runtime image never invokes npm — `CMD` is `node dist/index.js`, and the
|
|
276
|
+
development compose override builds the `builder` target, which keeps its own
|
|
277
|
+
npm — so it is now removed from the production stage. This clears all 8 and
|
|
278
|
+
reduces the image from 600 MB to 550 MB.
|
|
279
|
+
|
|
280
|
+
Only container scanning finds this class of problem, which is why the gate
|
|
281
|
+
exists. Keystones own `brace-expansion@5.0.12` is already above the fixed
|
|
282
|
+
version and was never affected.
|
|
283
|
+
|
|
284
|
+
### Added
|
|
285
|
+
|
|
286
|
+
- `.github/dependabot.yml` — weekly updates for npm (root and frontend), GitHub
|
|
287
|
+
Actions, and Docker. Routine patches are grouped; security updates are not, so
|
|
288
|
+
a compromised package lands alone and identifiable.
|
|
289
|
+
- `.github/workflows/supply-chain.yml` — OSV scanning (independent advisory
|
|
290
|
+
source from npm's), enforced `npm audit` over both trees, dependency review on
|
|
291
|
+
pull requests, SBOM generation, container scanning, and a license gate.
|
|
292
|
+
- `npm run verify:release` — fails on a version that disagrees between
|
|
293
|
+
`package.json` and the lockfile, a dependency in one and not the other, a
|
|
294
|
+
missing or malformed license, or a missing `repository` field. Wired into both
|
|
295
|
+
CI and the release workflow so a bad artifact cannot be published.
|
|
296
|
+
|
|
297
|
+
### Fixed
|
|
298
|
+
|
|
299
|
+
- `package.json` declared no `license` field, despite shipping an MIT `LICENSE`
|
|
300
|
+
file. The published package carried no machine-readable terms.
|
|
301
|
+
|
|
302
|
+
### Changed
|
|
303
|
+
|
|
304
|
+
- Fastify is at 5.12.5 and `fast-uri` resolves to 3.1.8, both already above the
|
|
305
|
+
5.12.2 / 3.1.7 targets. No upgrade was required.
|
|
306
|
+
- The license allowlist permits only permissive terms, with an explicit
|
|
307
|
+
exception for the pre-SPDX `MIT*` identifier that older packages emit.
|
|
308
|
+
|
|
309
|
+
## [2.0.0] - 2026-09-26
|
|
310
|
+
|
|
311
|
+
### Security
|
|
312
|
+
|
|
313
|
+
The mTLS trust boundary trusted whatever the request said about itself. Any
|
|
314
|
+
client that could reach Keystone could name a service account in a header and
|
|
315
|
+
become it, and could set its own IP address to escape every rate limit. This
|
|
316
|
+
release makes identity come only from values a client cannot forge.
|
|
317
|
+
|
|
318
|
+
- **`x-service-account-id` no longer authenticates.** It previously resolved a
|
|
319
|
+
service account on its own, with no certificate and no credential, so anyone
|
|
320
|
+
who knew or guessed an account ID became that account. It is now read only as a
|
|
321
|
+
hint alongside a valid certificate, and only when the account it names is the
|
|
322
|
+
one that certificate is bound to. A mismatch is refused, not fallen back from.
|
|
323
|
+
- **Client identity is bound to a certificate fingerprint.** A new unique
|
|
324
|
+
`service_accounts.cert_fingerprint` column pins a SHA-256 fingerprint to
|
|
325
|
+
exactly one account. Fingerprints are stored canonicalized, so the hex and
|
|
326
|
+
colon-separated spellings of one certificate cannot become two bindings, and
|
|
327
|
+
malformed values are rejected before reaching the database. Service accounts
|
|
328
|
+
are resolved by fingerprint rather than by their operator-chosen name.
|
|
329
|
+
- **Identity headers are stripped from untrusted peers.** An `onRequest` hook
|
|
330
|
+
registered before every plugin and route removes
|
|
331
|
+
`x-forwarded-client-cert`, `x-client-cert-fingerprint`,
|
|
332
|
+
`x-forwarded-client-cert-chain`, `x-service-account-id`, `x-forwarded-for`,
|
|
333
|
+
`x-real-ip`, and `forwarded` unless the peer is a configured trusted proxy.
|
|
334
|
+
Stripping rather than ignoring means a route added later cannot read a
|
|
335
|
+
spoofed identity by accident.
|
|
336
|
+
- **Rate limits can no longer be escaped.** The server was created with
|
|
337
|
+
`trustProxy: true` and the limiter read `x-forwarded-for` unconditionally, so
|
|
338
|
+
any client could present a fresh address per request and never be limited —
|
|
339
|
+
including against login, password reset, MFA verification, and SCIM. Limit keys
|
|
340
|
+
now come from the peer address unless a trusted proxy forwarded one.
|
|
341
|
+
- **Trust decisions do not use `request.ip`.** With `trustProxy` enabled that
|
|
342
|
+
value is derived from the attacker-controlled header, so the trusted-proxy
|
|
343
|
+
check uses the socket peer address, the only value a client cannot set.
|
|
344
|
+
- **Forwarded values are validated before use.** Certificate headers are
|
|
345
|
+
length-capped, and a fingerprint must be a well-formed SHA-256 digest, so
|
|
346
|
+
garbage cannot be used as a lookup key.
|
|
347
|
+
- **Inactive and revoked service accounts cannot authenticate by certificate.**
|
|
348
|
+
|
|
349
|
+
### Added
|
|
350
|
+
|
|
351
|
+
- `KEYSTONE_TRUSTED_PROXIES` — comma-separated proxy IPs, IPv4 CIDRs, or IPv6
|
|
352
|
+
prefixes permitted to set client-identity headers. Unset by default, which
|
|
353
|
+
trusts nothing. IPv4-mapped IPv6 peers are normalized before matching, and
|
|
354
|
+
unrecognized input fails closed.
|
|
355
|
+
- `PUT /v1/admin/organizations/:id/service-accounts/:accountId/certificate` —
|
|
356
|
+
bind or clear a client-certificate fingerprint, auditing
|
|
357
|
+
`service_account_certificate_bound` / `service_account_certificate_unbound`.
|
|
358
|
+
A certificate already held by another account returns `409`.
|
|
359
|
+
- `POST /v1/admin/organizations/:id/service-accounts/:accountId/revoke` —
|
|
360
|
+
permanently stop an account authenticating, auditing
|
|
361
|
+
`service_account_revoked`. Revoking an already-revoked account returns `409`
|
|
362
|
+
rather than a silent success.
|
|
363
|
+
- `docs/security/trust-boundaries.md`, `docs/security/proxy-security.md`, and
|
|
364
|
+
`docs/security/mtls.md` — the trust model, proxy requirements with working
|
|
365
|
+
nginx and ALB configuration, and the mTLS identity rules.
|
|
366
|
+
- `docs/MIGRATION-2.0.md` — migration instructions, including the failure mode
|
|
367
|
+
that presents as unrelated clients sharing a rate-limit budget.
|
|
368
|
+
|
|
369
|
+
### Fixed
|
|
370
|
+
|
|
371
|
+
- The SCIM token-hash unique indexes introduced in 1.9.0 were declared in the
|
|
372
|
+
Drizzle schema but never emitted as a migration, so they did not exist in any
|
|
373
|
+
deployed database. They are created by migration `0015`.
|
|
374
|
+
- The documented nginx configuration used `$proxy_add_x_forwarded_for`, which
|
|
375
|
+
appends to a client-supplied value and lets a client prepend a forged address.
|
|
376
|
+
Corrected to `$remote_addr`, with inbound identity headers stripped.
|
|
377
|
+
|
|
378
|
+
### Changed
|
|
379
|
+
|
|
380
|
+
- `trustProxy` is derived from `KEYSTONE_TRUSTED_PROXIES` instead of being
|
|
381
|
+
unconditionally `true`.
|
|
382
|
+
- `requireMTLS` distinguishes an untrusted peer (`401 MTLS_UNTRUSTED_PEER`) from
|
|
383
|
+
a missing certificate (`401 MTLS_CERTIFICATE_MISSING`), so a misconfiguration
|
|
384
|
+
is distinguishable from an attack.
|
|
385
|
+
|
|
386
|
+
### Breaking
|
|
387
|
+
|
|
388
|
+
- mTLS clients that authenticated with `x-service-account-id` alone must bind a
|
|
389
|
+
certificate fingerprint instead.
|
|
390
|
+
- Deployments behind a reverse proxy must set `KEYSTONE_TRUSTED_PROXIES`.
|
|
391
|
+
Without it, forwarded headers are stripped and every client shares one
|
|
392
|
+
rate-limit budget, so unrelated users can rate-limit each other.
|
|
393
|
+
|
|
394
|
+
## [1.9.0] - 2026-09-25
|
|
395
|
+
|
|
396
|
+
### Security
|
|
397
|
+
|
|
398
|
+
SCIM was a single global credential. In 1.8.x a deployment could provision
|
|
399
|
+
exactly one organization, the bearer token was stored and compared in plaintext,
|
|
400
|
+
and mutations reached global user records.
|
|
401
|
+
|
|
402
|
+
- SCIM credentials are per-organization. Every connection belongs to exactly one
|
|
403
|
+
organization, at most one is live per organization, and every user and group
|
|
404
|
+
read and write is filtered by it. A cross-tenant target returns `404`, so the
|
|
405
|
+
endpoint is not a tenant oracle.
|
|
406
|
+
- Bearer tokens are stored only as a SHA-256 digest and resolved by that digest,
|
|
407
|
+
so a database dump yields no usable token and the comparison carries no timing
|
|
408
|
+
signal. Connections can expire, be rotated, and be revoked.
|
|
409
|
+
- Issuing, rotating, and revoking a SCIM credential is owner-only. A SCIM token
|
|
410
|
+
provisions and deactivates tenant users, so a mere admin or member cannot mint
|
|
411
|
+
one.
|
|
412
|
+
- Deprovisioning removes the organization's membership and deactivates the
|
|
413
|
+
account only when no membership remains. Previously it deactivated a shared
|
|
414
|
+
account in every organization that user belonged to, without those
|
|
415
|
+
organizations' authorization.
|
|
416
|
+
- SCIM refuses to change the global attributes of a shared user, to reactivate a
|
|
417
|
+
shared account, and to remove the last owner of an organization.
|
|
418
|
+
- SCIM can no longer attach a user who already belongs to another organization,
|
|
419
|
+
and the conflict message no longer names the organization that holds them.
|
|
420
|
+
- Unauthenticated SCIM traffic is budgeted before the authentication hook emits
|
|
421
|
+
audit and webhook events, closing an unauthenticated write-amplification path.
|
|
422
|
+
- SCIM request budgets are keyed per credential, so one noisy identity provider
|
|
423
|
+
cannot exhaust every other tenant's allowance.
|
|
424
|
+
|
|
425
|
+
### Changed
|
|
426
|
+
|
|
427
|
+
- `SCIM_BEARER_TOKEN` and `SCIM_ORG_ID` are deprecated. They are adopted once
|
|
428
|
+
into a connection at startup and then ignored, so an upgrade does not break an
|
|
429
|
+
existing identity provider. Adoption is one-time in any state, so a restart
|
|
430
|
+
cannot resurrect a revoked credential.
|
|
431
|
+
- Deprovisioning a single-tenant user now removes the membership as well as
|
|
432
|
+
deactivating the account. Deprovisioning a shared user removes only the
|
|
433
|
+
membership, and a follow-up `DELETE` returns `404`.
|
|
434
|
+
- `POST /scim/v2/Users` is create-or-update; profile fields for an existing
|
|
435
|
+
member are applied rather than dropped.
|
|
436
|
+
- A user removed from an organization can be re-provisioned. Previously the
|
|
437
|
+
global email lookup found the inactive row and returned `409` forever.
|
|
438
|
+
- Rotating a credential revokes the previous token immediately
|
|
439
|
+
(`SCIM_ROTATION_GRACE_SECONDS` defaults to `0`). A grace window is now an
|
|
440
|
+
explicit opt-in and is not a revocation mechanism.
|
|
441
|
+
- Groups are real organization-scoped records. The synthetic role-bucket
|
|
442
|
+
projection (`<orgId>:<role>` ids) is removed.
|
|
443
|
+
- Malformed path ids return a SCIM `404` instead of surfacing a driver error, and
|
|
444
|
+
validation and internal failures return SCIM `Error` objects.
|
|
445
|
+
|
|
446
|
+
### Added
|
|
447
|
+
|
|
448
|
+
- `scim_connections` with rotation, revocation, expiry, and a token hint.
|
|
449
|
+
- `scim_groups` and `scim_group_members`, organization-scoped throughout.
|
|
450
|
+
- `PATCH /scim/v2/Users/:userId` and `POST /scim/v2/Users/.search`.
|
|
451
|
+
- Group create, replace, patch, delete, and member management endpoints.
|
|
452
|
+
- `filter`, `startIndex`, and `count` on list endpoints; unsupported filters are
|
|
453
|
+
rejected rather than silently ignored.
|
|
454
|
+
- `GET /scim/v2/ServiceProviderConfig` and `GET /scim/v2/ResourceTypes`.
|
|
455
|
+
- Owner-only admin API for creating, listing, rotating, and revoking SCIM
|
|
456
|
+
connections, with a dashboard UI for the same.
|
|
457
|
+
- `findByIdInOrg`, `updateInOrg`, `listOrgIdsForUser`, and `removeFromOrg`
|
|
458
|
+
repository methods for organization-scoped user access.
|
|
459
|
+
- Audit events `scim_connection_created`, `scim_connection_rotated`,
|
|
460
|
+
`scim_connection_revoked`, `scim_access_denied`, `scim_authentication_failed`,
|
|
461
|
+
and the `scim_group_*` group events.
|
|
462
|
+
- `docs/MIGRATION-1.9.md`.
|
|
463
|
+
|
|
464
|
+
## [1.8.0] - 2026-09-25
|
|
465
|
+
|
|
466
|
+
### Security
|
|
467
|
+
|
|
468
|
+
MFA was advisory in 1.7.x. A user with TOTP enabled could sign in with only a
|
|
469
|
+
password, and when a code was supplied it was verified *after* the access token,
|
|
470
|
+
refresh token, and session had already been created. 1.8.0 makes the second
|
|
471
|
+
factor mandatory.
|
|
472
|
+
|
|
473
|
+
- Password authentication now stops at `requires_mfa` for MFA-enabled accounts.
|
|
474
|
+
No access token, refresh token, or session row is created at that stage.
|
|
475
|
+
- New `POST /auth/mfa/verify` completes the transition. The challenge is opaque,
|
|
476
|
+
stored only as a hash, short-lived, single-use, and bounded by an attempt
|
|
477
|
+
budget enforced in the database.
|
|
478
|
+
- Token issuance is guarded at a single chokepoint. A token cannot be minted for
|
|
479
|
+
an MFA-enabled user without a recorded factor, so no login path bypasses MFA by
|
|
480
|
+
omission.
|
|
481
|
+
- TOTP verification uses the user's own decrypted secret. Each time-step is
|
|
482
|
+
accepted exactly once, so a captured code is rejected even against a freshly
|
|
483
|
+
issued challenge.
|
|
484
|
+
- Enabling MFA revokes every existing refresh token and session for the account.
|
|
485
|
+
Sessions record how MFA was satisfied, and refresh rotation refuses sessions
|
|
486
|
+
with no recorded factor.
|
|
487
|
+
- Backup codes carry 80 bits of entropy, are stored as a keyed (peppered) hash,
|
|
488
|
+
expire after 90 days, and are consumed by a conditional update so concurrent
|
|
489
|
+
use has exactly one winner.
|
|
490
|
+
- TOTP secrets are written with AES-256-GCM. Values written by earlier versions
|
|
491
|
+
used AES-256-CBC and remain readable.
|
|
492
|
+
|
|
493
|
+
### Changed
|
|
494
|
+
|
|
495
|
+
- `POST /auth/login` and `POST /auth/token-login` return `401` with
|
|
496
|
+
`code: "MFA_REQUIRED"` and a challenge when MFA is required. See
|
|
497
|
+
[MIGRATION-1.8.md](docs/MIGRATION-1.8.md).
|
|
498
|
+
- The undocumented `totp_code` field on the login endpoints is removed and
|
|
499
|
+
ignored.
|
|
500
|
+
- `POST /auth/totp/backup` now regenerates backup codes and requires a current
|
|
501
|
+
TOTP code. `POST /auth/totp/backup/verify` consumes a backup code.
|
|
502
|
+
- `POST /auth/totp/verify` additionally reports `sessionsRevoked`.
|
|
503
|
+
- WebAuthn assertions satisfy MFA on their own. Magic links refuse to downgrade a
|
|
504
|
+
TOTP-protected account, and SAML, enterprise OIDC, federation, and OAuth2
|
|
505
|
+
report a typed `mfa_required` error.
|
|
506
|
+
- OAuth2 authorization codes carry the MFA factor of the session that approved
|
|
507
|
+
them, so the token exchange cannot launder an unverified login.
|
|
508
|
+
- Access tokens for MFA sessions carry `mfa_verified`, `mfa_factor`, and `amr`.
|
|
509
|
+
- Factor management requires step-up: `/auth/totp/enroll`, `/auth/totp/verify`,
|
|
510
|
+
`/auth/totp/backup`, `/auth/totp/disable`, and passkey registration for a
|
|
511
|
+
TOTP-protected account all require the account password in addition to the
|
|
512
|
+
session.
|
|
513
|
+
- A passkey registered after TOTP was enabled is treated as a single factor and
|
|
514
|
+
cannot be used to sign in on its own.
|
|
515
|
+
- Disabling TOTP deletes its backup codes.
|
|
516
|
+
- Failed MFA factor attempts count toward the account lockout.
|
|
517
|
+
- `SDK.authentication.login()` returns a discriminated union; `completeMfa()` is
|
|
518
|
+
new.
|
|
519
|
+
|
|
520
|
+
### Fixed
|
|
521
|
+
|
|
522
|
+
- `/auth/mfa/verify` wrote session cookies under a name derived from the login
|
|
523
|
+
flow instead of the client id, so MFA-completed sessions were not readable by
|
|
524
|
+
the auth plugin and every application on the cookie domain shared one name.
|
|
525
|
+
- The MFA step no longer accepted accounts that are deactivated, under review,
|
|
526
|
+
or locked out, which the password step already refused.
|
|
527
|
+
- Repeated password steps no longer cancel an MFA challenge created moments
|
|
528
|
+
earlier.
|
|
529
|
+
- `MFA_CHALLENGE_TTL_SECONDS`, `MFA_MAX_ATTEMPTS`, and
|
|
530
|
+
`TOTP_BACKUP_CODE_TTL_SECONDS` are validated at startup and fall back to their
|
|
531
|
+
defaults instead of silently breaking every login.
|
|
532
|
+
- The MFA factor copied out of a verified token into the authorization-code
|
|
533
|
+
table is validated against the column's check constraint.
|
|
534
|
+
- SAML now reports `mfa_required` for MFA-protected accounts instead of
|
|
535
|
+
collapsing the failure into a generic validation error.
|
|
536
|
+
|
|
537
|
+
### Added
|
|
538
|
+
|
|
539
|
+
- `mfa_challenges` table, `MfaChallengeRepository`, and `MfaService`.
|
|
540
|
+
- `MFA_CHALLENGE_TTL_SECONDS`, `MFA_MAX_ATTEMPTS`, and
|
|
541
|
+
`TOTP_BACKUP_CODE_TTL_SECONDS` configuration.
|
|
542
|
+
- Audit events `mfa_challenge_created`, `mfa_challenge_failed`,
|
|
543
|
+
`mfa_challenge_expired`, `mfa_challenge_rejected`, `mfa_verified`,
|
|
544
|
+
`mfa_bypass_blocked`, and `mfa_backup_code_regenerated`.
|
|
545
|
+
- Dedicated rate limits for MFA verification and every TOTP management endpoint.
|
|
546
|
+
- MFA challenge step in the admin dashboard login form.
|
|
547
|
+
- `docs/MIGRATION-1.8.md`.
|
|
548
|
+
- MFA security regression suite (`src/tests/security/mfa.test.ts`).
|
|
549
|
+
|
|
550
|
+
## [1.7.0] - 2026-09-24
|
|
551
|
+
|
|
552
|
+
### Added
|
|
553
|
+
|
|
554
|
+
- Dedicated owner-only platform-role endpoint at `PATCH /v1/admin/platform/users/:userId/role`.
|
|
555
|
+
- Centralized platform and organization authorization guards with explicit organization context.
|
|
556
|
+
- Versioned audit events for platform-role, membership, permission, and denied-authorization transitions.
|
|
557
|
+
- Dedicated authorization regression suite covering privilege escalation, tenant isolation, workflow safety, secret disclosure, and audit metadata.
|
|
558
|
+
- RBAC and authorization-boundary documentation.
|
|
559
|
+
|
|
560
|
+
### Changed
|
|
561
|
+
|
|
562
|
+
- Platform roles are explicitly limited to `owner` and `user`.
|
|
563
|
+
- Organization roles are explicitly limited to `owner`, `admin`, and `member`.
|
|
564
|
+
- Authorization checks now require an explicit `organizationId`.
|
|
565
|
+
- Frontend administration clients use the dedicated platform-role endpoint and safe workflow definitions.
|
|
566
|
+
- SAML metadata lookups require both connection and organization identifiers.
|
|
567
|
+
|
|
568
|
+
### Fixed
|
|
569
|
+
|
|
570
|
+
- Organization user routes can no longer mutate global users or deactivate shared accounts.
|
|
571
|
+
- Generic profile and in-process identity contracts can no longer carry a platform role.
|
|
572
|
+
- Organization admins cannot promote themselves or other members to organization owner.
|
|
573
|
+
- The sole organization owner cannot be demoted or removed.
|
|
574
|
+
- Tenant workflows now use a closed safe-step allowlist; plugin aliases, arbitrary webhooks, organization creation, and authorization-mutating steps fail closed.
|
|
575
|
+
- Global workflows require platform-owner access, and workflow execution rechecks organization membership.
|
|
576
|
+
- Organization creation always assigns an owner; actorless global deactivation APIs were removed from the organization domain.
|
|
577
|
+
- Last-owner transitions use database row locks to prevent concurrent demotion/removal.
|
|
578
|
+
- Platform-user deactivation now disables login, invalidates existing sessions, revokes refresh tokens and user API keys, and preserves the last active owner invariant.
|
|
579
|
+
- User-management responses redact application secret hashes, OIDC/API-key credentials, configuration values, password hashes, TOTP secrets, and metadata.
|
|
580
|
+
- SAML/OIDC public lookups require an organization context; new OIDC client secrets are encrypted at rest, and legacy plaintext values are re-encrypted on first callback use.
|
|
581
|
+
- OAuth/OIDC client context no longer places an organization claim in a user token unless the user is a member of that application's organization.
|
|
582
|
+
- Failed authorization attempts and role transitions now produce structured audit evidence.
|
|
583
|
+
- SAML schema validation now has a signed-response regression test and audience/destination/recipient checks, alongside one-time transaction claiming and OIDC ID-token/JWKS verification.
|
|
584
|
+
- Enterprise SSO requires an explicit connection/subject identity link and rejects platform-owner tenant login; generic OAuth no longer auto-links by email.
|
|
585
|
+
- SCIM is scoped to `SCIM_ORG_ID`, cannot re-enable quarantined or platform-owner accounts, attributes audits to the SCIM credential, and deactivates rather than deleting users.
|
|
586
|
+
- OIDC endpoint checks cover private, carrier-grade, benchmarking, dotted/hex IPv4-mapped, DNS-pinned, and redirecting targets.
|
|
587
|
+
- Legacy account migration quarantines ambiguous unverified rows instead of activating them.
|
|
588
|
+
- SAML semantic validation and OIDC/JWKS checks are covered by signed-response tests; OAuth2 refresh success and failure emit audit events.
|
|
589
|
+
- Refresh-token rotation and OAuth authorization-code consumption are atomic and client-bound.
|
|
590
|
+
- Legacy unverified accounts are quarantined for explicit review during the deactivation migration.
|
|
591
|
+
- OIDC endpoint configuration blocks private/redirected targets by default; SCIM reflects account deactivation.
|
|
592
|
+
- Added a blocking `oxlint` gate with warnings denied; CI now runs lint separately from typecheck.
|
|
593
|
+
- Pinned safe transitive versions for `@xmldom/xmldom`, `fast-uri`, and `find-my-way`; the High-severity production audit gate now passes.
|
|
594
|
+
- API-key validation uses public user projections and emits `api_key_used` audit events; the compiled OIDC re-encryption helper closes its database pool before exit.
|
|
595
|
+
|
|
596
|
+
### Security
|
|
597
|
+
|
|
598
|
+
- Critical organization-admin-to-platform-owner escalation paths are closed at HTTP, application, domain, SDK, and repository boundaries.
|
|
599
|
+
- Cross-tenant authorization context is resolved from authenticated database membership rather than client-controlled application context.
|
|
600
|
+
- Workflow definitions that are malformed or contain blocked authorization steps fail closed.
|
|
601
|
+
|
|
602
|
+
### Breaking Changes
|
|
603
|
+
|
|
604
|
+
- Organization user PATCH/DELETE endpoints no longer mutate global accounts; they return a migration response. Use platform user administration or organization member endpoints.
|
|
605
|
+
- `/v1/authz/check` requests must include `organizationId`.
|
|
606
|
+
- Custom organization role names are no longer accepted; only `owner`, `admin`, and `member` are supported.
|
|
607
|
+
- Public SAML/OIDC initiation and metadata URLs require `organizationId`.
|
|
608
|
+
- Direct authorization SDK calls now require both actor and organization IDs.
|
|
609
|
+
- OIDC connections require a JWKS URI; OAuth2 application-bound refresh requests must provide the bound `client_id` and `client_secret`.
|
|
610
|
+
- SCIM requires both `SCIM_BEARER_TOKEN` and `SCIM_ORG_ID`, and operates only within that organization.
|
|
611
|
+
- Legacy unverified accounts may be marked `account_review_required` and require explicit review.
|
|
612
|
+
- Tenant workflow definitions containing authorization-mutating, plugin, organization-creation, or arbitrary webhook steps are rejected or blocked.
|
|
613
|
+
|
|
614
|
+
### Migration
|
|
615
|
+
|
|
616
|
+
- Move platform role changes to `PATCH /v1/admin/platform/users/:userId/role`.
|
|
617
|
+
- Use `/v1/admin/organizations/:id/members/:userId` for organization role changes.
|
|
618
|
+
- Remove unsafe workflow steps before deployment.
|
|
619
|
+
- Update SAML/OIDC URLs to include the organization ID.
|
|
620
|
+
- Update authorization-check clients to send the organization ID explicitly.
|
|
621
|
+
|
|
622
|
+
### Dependencies
|
|
623
|
+
|
|
624
|
+
- No dependency changes in this release. Existing dependency audit findings remain tracked for the planned supply-chain phase.
|
|
625
|
+
|
|
626
|
+
### Testing
|
|
627
|
+
|
|
628
|
+
- Backend typecheck and build pass.
|
|
629
|
+
- Backend test suite passes with the security regression suite enabled.
|
|
630
|
+
- Frontend production build passes.
|
|
631
|
+
|
|
8
632
|
## [1.1.0] - 2026-09-19
|
|
9
633
|
|
|
10
634
|
Security hardening, architecture improvements, and enterprise SSO enhancements.
|
|
@@ -144,6 +768,7 @@ Frontend upgrades — React 19, Vite 8, Tailwind 4, TypeScript 7.
|
|
|
144
768
|
- `postcss.config.js` deleted — Tailwind 4 uses Vite plugin directly.
|
|
145
769
|
- `src/index.css` updated to use `@import "./tailwind.css"` instead of `@tailwind base/components/utilities`.
|
|
146
770
|
|
|
771
|
+
[1.7.0]: https://github.com/Hilbras/Keystone/releases/tag/v1.7.0
|
|
147
772
|
[1.6.0]: https://github.com/Hilbras/Keystone/releases/tag/v1.6.0
|
|
148
773
|
[1.5.0]: https://github.com/Hilbras/Keystone/releases/tag/v1.5.0
|
|
149
774
|
[1.4.0]: https://github.com/Hilbras/Keystone/releases/tag/v1.4.0
|