This is an automated email from the ASF dual-hosted git repository.

richardfogaca pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new e06427d1efe feat(embedded): add feature flag to disable logout button 
in embedded contexts (#37537)
e06427d1efe is described below

commit e06427d1efe0e5126fd1d265a3e51fe6fe16c37c
Author: Richard Fogaca Nienkotter 
<[email protected]>
AuthorDate: Mon Feb 23 17:56:02 2026 -0300

    feat(embedded): add feature flag to disable logout button in embedded 
contexts (#37537)
    
    Co-authored-by: richard <[email protected]>
---
 docs/docs/configuration/networking-settings.mdx    | 18 +++++
 docs/static/feature-flags.json                     |  8 +++
 .../superset-ui-core/src/utils/featureFlags.ts     |  1 +
 .../src/features/home/RightMenu.test.tsx           | 83 ++++++++++++++++++++++
 superset-frontend/src/features/home/RightMenu.tsx  | 33 ++++++---
 superset/config.py                                 |  5 ++
 6 files changed, 138 insertions(+), 10 deletions(-)

diff --git a/docs/docs/configuration/networking-settings.mdx 
b/docs/docs/configuration/networking-settings.mdx
index 74f13ff7986..59017fa9612 100644
--- a/docs/docs/configuration/networking-settings.mdx
+++ b/docs/docs/configuration/networking-settings.mdx
@@ -96,6 +96,24 @@ To enable this entry, add the following line to the `.env` 
file:
 SUPERSET_FEATURE_EMBEDDED_SUPERSET=true
 ```
 
+### Hiding the Logout Button in Embedded Contexts
+
+When Superset is embedded in an application that manages authentication via 
SSO (OAuth2, SAML, or JWT), the logout button should be hidden since session 
management is handled by the parent application.
+
+To hide the logout button in embedded contexts, add to `superset_config.py`:
+
+```python
+FEATURE_FLAGS = {
+    "DISABLE_EMBEDDED_SUPERSET_LOGOUT": True,
+}
+```
+
+This flag only hides the logout button when Superset detects it is running 
inside an iframe. Users accessing Superset directly (not embedded) will still 
see the logout button regardless of this setting.
+
+:::note
+When embedding with SSO, also set `SESSION_COOKIE_SAMESITE = 'None'` and 
`SESSION_COOKIE_SECURE = True`. See [Security 
documentation](/docs/security/securing_superset) for details.
+:::
+
 ## CSRF settings
 
 Similarly, [flask-wtf](https://flask-wtf.readthedocs.io/en/0.15.x/config/) is 
used to manage
diff --git a/docs/static/feature-flags.json b/docs/static/feature-flags.json
index 6ca4c2ea818..227d529c1db 100644
--- a/docs/static/feature-flags.json
+++ b/docs/static/feature-flags.json
@@ -261,6 +261,14 @@
         "description": "Data panel closed by default in chart builder",
         "category": "runtime_config"
       },
+      {
+        "name": "DISABLE_EMBEDDED_SUPERSET_LOGOUT",
+        "default": false,
+        "lifecycle": "stable",
+        "description": "Hide the logout button in embedded contexts (e.g., 
when using SSO in iframes)",
+        "docs": 
"https://superset.apache.org/docs/configuration/networking-settings#hiding-the-logout-button-in-embedded-contexts";,
+        "category": "runtime_config"
+      },
       {
         "name": "DRILL_BY",
         "default": true,
diff --git 
a/superset-frontend/packages/superset-ui-core/src/utils/featureFlags.ts 
b/superset-frontend/packages/superset-ui-core/src/utils/featureFlags.ts
index 57bda77b7db..9770951342b 100644
--- a/superset-frontend/packages/superset-ui-core/src/utils/featureFlags.ts
+++ b/superset-frontend/packages/superset-ui-core/src/utils/featureFlags.ts
@@ -39,6 +39,7 @@ export enum FeatureFlag {
   DatapanelClosedByDefault = 'DATAPANEL_CLOSED_BY_DEFAULT',
   DatasetFolders = 'DATASET_FOLDERS',
   DateRangeTimeshiftsEnabled = 'DATE_RANGE_TIMESHIFTS_ENABLED',
+  DisableEmbeddedSupersetLogout = 'DISABLE_EMBEDDED_SUPERSET_LOGOUT',
   /** @deprecated */
   DrillToDetail = 'DRILL_TO_DETAIL',
   DrillBy = 'DRILL_BY',
diff --git a/superset-frontend/src/features/home/RightMenu.test.tsx 
b/superset-frontend/src/features/home/RightMenu.test.tsx
index 851aaf2e224..4c15c585ad6 100644
--- a/superset-frontend/src/features/home/RightMenu.test.tsx
+++ b/superset-frontend/src/features/home/RightMenu.test.tsx
@@ -24,9 +24,26 @@ import {
   userEvent,
   waitFor,
 } from 'spec/helpers/testing-library';
+import { isFeatureEnabled, FeatureFlag } from '@superset-ui/core';
+import { isEmbedded } from 'src/dashboard/util/isEmbedded';
 import RightMenu from './RightMenu';
 import { GlobalMenuDataOptions, RightMenuProps } from './types';
 
+jest.mock('@superset-ui/core', () => ({
+  ...jest.requireActual('@superset-ui/core'),
+  isFeatureEnabled: jest.fn(),
+}));
+
+const mockIsFeatureEnabled = isFeatureEnabled as jest.MockedFunction<
+  typeof isFeatureEnabled
+>;
+
+jest.mock('src/dashboard/util/isEmbedded', () => ({
+  isEmbedded: jest.fn(() => false),
+}));
+
+const mockIsEmbedded = isEmbedded as jest.MockedFunction<typeof isEmbedded>;
+
 jest.mock('react-redux', () => ({
   ...jest.requireActual('react-redux'),
   useSelector: jest.fn(),
@@ -160,6 +177,8 @@ const getDatabaseWithNameFilterMockUrl =
   
'glob:*api/v1/database/?q=(filters:!((col:database_name,opr:neq,value:examples)))';
 
 beforeEach(async () => {
+  mockIsFeatureEnabled.mockReturnValue(false);
+  mockIsEmbedded.mockReturnValue(false);
   useSelectorMock.mockReset();
   fetchMock.get(
     getDatabaseWithFileFiterMockUrl,
@@ -393,3 +412,67 @@ test('Logs out and clears local storage item redux', async 
() => {
     expect(sessionStorage.getItem('login_attempted')).toBeNull();
   });
 });
+
+test('shows logout button when not embedded', async () => {
+  mockIsEmbedded.mockReturnValue(false);
+  mockIsFeatureEnabled.mockReturnValue(false);
+  resetUseSelectorMock();
+  render(<RightMenu {...createProps()} />, {
+    useRedux: true,
+    useQueryParams: true,
+    useRouter: true,
+    useTheme: true,
+  });
+
+  userEvent.hover(await screen.findByText(/Settings/i));
+  expect(await screen.findByText('Logout')).toBeInTheDocument();
+});
+
+test('shows logout button when embedded but flag is disabled', async () => {
+  mockIsEmbedded.mockReturnValue(true);
+  mockIsFeatureEnabled.mockReturnValue(false);
+  resetUseSelectorMock();
+  render(<RightMenu {...createProps()} />, {
+    useRedux: true,
+    useQueryParams: true,
+    useRouter: true,
+    useTheme: true,
+  });
+
+  userEvent.hover(await screen.findByText(/Settings/i));
+  expect(await screen.findByText('Logout')).toBeInTheDocument();
+});
+
+test('shows logout button when not embedded even if flag is enabled', async () 
=> {
+  mockIsEmbedded.mockReturnValue(false);
+  mockIsFeatureEnabled.mockImplementation(
+    (flag: FeatureFlag) => flag === FeatureFlag.DisableEmbeddedSupersetLogout,
+  );
+  resetUseSelectorMock();
+  render(<RightMenu {...createProps()} />, {
+    useRedux: true,
+    useQueryParams: true,
+    useRouter: true,
+    useTheme: true,
+  });
+
+  userEvent.hover(await screen.findByText(/Settings/i));
+  expect(await screen.findByText('Logout')).toBeInTheDocument();
+});
+
+test('hides logout button when embedded and flag is enabled', async () => {
+  mockIsEmbedded.mockReturnValue(true);
+  mockIsFeatureEnabled.mockImplementation(
+    (flag: FeatureFlag) => flag === FeatureFlag.DisableEmbeddedSupersetLogout,
+  );
+  resetUseSelectorMock();
+  render(<RightMenu {...createProps()} />, {
+    useRedux: true,
+    useQueryParams: true,
+    useRouter: true,
+    useTheme: true,
+  });
+
+  userEvent.hover(await screen.findByText(/Settings/i));
+  expect(screen.queryByText('Logout')).not.toBeInTheDocument();
+});
diff --git a/superset-frontend/src/features/home/RightMenu.tsx 
b/superset-frontend/src/features/home/RightMenu.tsx
index dcca12f0304..553908f2550 100644
--- a/superset-frontend/src/features/home/RightMenu.tsx
+++ b/superset-frontend/src/features/home/RightMenu.tsx
@@ -23,7 +23,12 @@ import { Link } from 'react-router-dom';
 import { useQueryParams, BooleanParam } from 'use-query-params';
 import { isEmpty } from 'lodash';
 import { t } from '@apache-superset/core';
-import { SupersetClient, getExtensionsRegistry } from '@superset-ui/core';
+import {
+  SupersetClient,
+  getExtensionsRegistry,
+  isFeatureEnabled,
+  FeatureFlag,
+} from '@superset-ui/core';
 import { styled, css, SupersetTheme, useTheme } from 
'@apache-superset/core/ui';
 import {
   Tag,
@@ -35,6 +40,7 @@ import {
 } from '@superset-ui/core/components';
 import type { ItemType, MenuItem } from '@superset-ui/core/components/Menu';
 import { ensureAppRoot, makeUrl } from 'src/utils/pathUtils';
+import { isEmbedded } from 'src/dashboard/util/isEmbedded';
 import { findPermission } from 'src/utils/findPermission';
 import { isUserAdmin } from 'src/dashboard/util/permissionUtils';
 import {
@@ -489,15 +495,22 @@ const RightMenu = ({
             ),
           });
         }
-        userItems.push({
-          key: 'logout',
-          label: (
-            <Typography.Link href={navbarRight.user_logout_url}>
-              {t('Logout')}
-            </Typography.Link>
-          ),
-          onClick: handleLogout,
-        });
+        const showLogout =
+          !isEmbedded() ||
+          !isFeatureEnabled(FeatureFlag.DisableEmbeddedSupersetLogout);
+        if (showLogout) {
+          userItems.push({
+            key: 'logout',
+            label: (
+              <Typography.Link
+                href={ensureAppRoot(navbarRight.user_logout_url)}
+              >
+                {t('Logout')}
+              </Typography.Link>
+            ),
+            onClick: handleLogout,
+          });
+        }
 
         items.push({
           type: 'group',
diff --git a/superset/config.py b/superset/config.py
index c4cae11114e..dfeafb3b261 100644
--- a/superset/config.py
+++ b/superset/config.py
@@ -714,6 +714,11 @@ DEFAULT_FEATURE_FLAGS: dict[str, bool] = {
     # @lifecycle: stable
     # @category: runtime_config
     "DATAPANEL_CLOSED_BY_DEFAULT": False,
+    # Hide the logout button in embedded contexts (e.g., when using SSO in 
iframes)
+    # @lifecycle: stable
+    # @category: runtime_config
+    # @docs: 
https://superset.apache.org/docs/configuration/networking-settings#hiding-the-logout-button-in-embedded-contexts
+    "DISABLE_EMBEDDED_SUPERSET_LOGOUT": False,
     # Enable drill-by functionality in charts
     # @lifecycle: stable
     # @category: runtime_config

Reply via email to