fix(front): gate SSO and audit logs on enterprise validity token (#22459)

## 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).

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22459?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Charles Bochet
2026-07-03 10:42:06 +02:00
committed by GitHub
parent 9ef1af9799
commit caa282f5da
2 changed files with 4 additions and 4 deletions
@@ -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 ? (
<StyledLinkContainer
isDisabled={currentWorkspace?.hasValidSignedEnterpriseKey !== true}
isDisabled={currentWorkspace?.hasValidEnterpriseValidityToken !== true}
>
<Link to={getSettingsPath(SettingsPath.NewSSOIdentityProvider)}>
<SettingsCard
title={t`Add SSO Identity Provider`}
disabled={currentWorkspace?.hasValidSignedEnterpriseKey !== true}
disabled={currentWorkspace?.hasValidEnterpriseValidityToken !== true}
Icon={<IconKey />}
/>
</Link>
@@ -178,7 +178,7 @@ export const SettingsSecuritySettings = () => {
hasBypassProviderAvailable;
const hasEnterpriseAccess =
currentWorkspace?.hasValidSignedEnterpriseKey === true;
currentWorkspace?.hasValidEnterpriseValidityToken === true;
const isEventLogsEnabled = hasEnterpriseAccess && isClickHouseConfigured;
return (