From caa282f5dafc668e6e39c5d63708abf1b6823199 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Fri, 3 Jul 2026 10:42:06 +0200 Subject: [PATCH] fix(front): gate SSO and audit logs on enterprise validity token (#22459) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem The Security settings UI gates the **SSO** card and **audit logs** on `currentWorkspace.hasValidSignedEnterpriseKey`, but the backend enforces enterprise features through `EnterprisePlanService.isValid()`, which returns `hasValidEnterpriseValidityToken()`. These check different things: - **`hasValidSignedEnterpriseKey`** — the `ENTERPRISE_KEY` config var is present and is a correctly-signed JWT (signature check only, no expiry). It's the credential the operator installs. - **`hasValidEnterpriseValidityToken`** — a validity token (fetched from the licensing API, stored as an `AppToken`, refreshed by cron) that is present and not expired. This is the runtime "is enterprise active right now" check, and it's the one every backend gate uses (`EnterpriseFeaturesEnabledGuard`, SSO sign-in, event-log retention, billing, row-level permissions, signing-key rotation). As a result, a workspace with a valid unexpired validity token but no locally-signed key (e.g. the `ENTERPRISE_KEY` env var isn't set on a given replica, or an "orphaned validity token" state) shows SSO **disabled** in the UI while the server would actually authorize SSO operations. ## Change Gate the SSO card and audit-logs section on `hasValidEnterpriseValidityToken` so the UI matches backend enforcement. The Enterprise management page (`SettingsEnterprise.tsx`) deliberately keeps the key/token distinction — it needs the signed key for subscription status, the customer portal, and the orphaned-validity-token warning — so it is left unchanged. ## Files - `SettingsSSOIdentitiesProvidersListCard.tsx` — skip/disable now driven by the validity token - `SettingsSecuritySettings.tsx` — `hasEnterpriseAccess` now driven by the validity token ## Notes - The SSO `skip` condition changed from `=== false` to `!== true`, so an undefined workspace (still loading) now skips the query rather than firing it — consistent with the `disabled` checks below it. ## Test - `nx lint:diff-with-main twenty-front` passes. - Not manually verifiable in local dev without an enterprise license setup (requires a workspace in the token-valid / key-absent state). Review in cubic --- .../SSO/SettingsSSOIdentitiesProvidersListCard.tsx | 6 +++--- .../security/components/SettingsSecuritySettings.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/twenty-front/src/modules/settings/security/components/SSO/SettingsSSOIdentitiesProvidersListCard.tsx b/packages/twenty-front/src/modules/settings/security/components/SSO/SettingsSSOIdentitiesProvidersListCard.tsx index 2024f0e0cf..5aad8f19b6 100644 --- a/packages/twenty-front/src/modules/settings/security/components/SSO/SettingsSSOIdentitiesProvidersListCard.tsx +++ b/packages/twenty-front/src/modules/settings/security/components/SSO/SettingsSSOIdentitiesProvidersListCard.tsx @@ -42,7 +42,7 @@ export const SettingsSSOIdentitiesProvidersListCard = () => { error: ssoError, } = useQuery(GetSsoIdentityProvidersDocument, { fetchPolicy: 'network-only', - skip: currentWorkspace?.hasValidSignedEnterpriseKey === false, + skip: currentWorkspace?.hasValidEnterpriseValidityToken !== true, }); useEffect(() => { @@ -55,12 +55,12 @@ export const SettingsSSOIdentitiesProvidersListCard = () => { return loading || !SSOIdentitiesProviders.length ? ( } /> diff --git a/packages/twenty-front/src/modules/settings/security/components/SettingsSecuritySettings.tsx b/packages/twenty-front/src/modules/settings/security/components/SettingsSecuritySettings.tsx index c4c8a9b8c4..299016e01e 100644 --- a/packages/twenty-front/src/modules/settings/security/components/SettingsSecuritySettings.tsx +++ b/packages/twenty-front/src/modules/settings/security/components/SettingsSecuritySettings.tsx @@ -178,7 +178,7 @@ export const SettingsSecuritySettings = () => { hasBypassProviderAvailable; const hasEnterpriseAccess = - currentWorkspace?.hasValidSignedEnterpriseKey === true; + currentWorkspace?.hasValidEnterpriseValidityToken === true; const isEventLogsEnabled = hasEnterpriseAccess && isClickHouseConfigured; return (