diff --git a/frontend/__tests__/components/features/user/user-context-menu.test.tsx b/frontend/__tests__/components/features/user/user-context-menu.test.tsx
index 635f66e645..07895f547c 100644
--- a/frontend/__tests__/components/features/user/user-context-menu.test.tsx
+++ b/frontend/__tests__/components/features/user/user-context-menu.test.tsx
@@ -156,11 +156,19 @@ describe("UserContextMenu", () => {
useSelectedOrganizationStore.setState({ organizationId: null });
});
- it("should render the default context items for a user", () => {
+ it("should render the default context items for a user", async () => {
+ vi.spyOn(OptionService, "getConfig").mockResolvedValue(
+ createMockWebClientConfig({ app_mode: "saas" }),
+ );
+
renderUserContextMenu({ type: "member", onClose: vi.fn, onOpenInviteModal: vi.fn });
screen.getByTestId("org-selector");
- screen.getByText("ACCOUNT_SETTINGS$LOGOUT");
+
+ // Wait for config to load so logout button appears
+ await waitFor(() => {
+ expect(screen.getByText("ACCOUNT_SETTINGS$LOGOUT")).toBeInTheDocument();
+ });
expect(
screen.queryByText("ORG$INVITE_ORG_MEMBERS"),
@@ -304,6 +312,20 @@ describe("UserContextMenu", () => {
screen.queryByText("Organization Members"),
).not.toBeInTheDocument();
});
+
+ it("should not display logout button in OSS mode", async () => {
+ renderUserContextMenu({ type: "member", onClose: vi.fn, onOpenInviteModal: vi.fn });
+
+ // Wait for the config to load
+ await waitFor(() => {
+ expect(screen.getByText("SETTINGS$NAV_LLM")).toBeInTheDocument();
+ });
+
+ // Verify logout button is NOT rendered in OSS mode
+ expect(
+ screen.queryByText("ACCOUNT_SETTINGS$LOGOUT"),
+ ).not.toBeInTheDocument();
+ });
});
describe("HIDE_LLM_SETTINGS feature flag", () => {
@@ -382,10 +404,15 @@ describe("UserContextMenu", () => {
});
it("should call the logout handler when Logout is clicked", async () => {
+ vi.spyOn(OptionService, "getConfig").mockResolvedValue(
+ createMockWebClientConfig({ app_mode: "saas" }),
+ );
+
const logoutSpy = vi.spyOn(AuthService, "logout");
renderUserContextMenu({ type: "member", onClose: vi.fn, onOpenInviteModal: vi.fn });
- const logoutButton = screen.getByText("ACCOUNT_SETTINGS$LOGOUT");
+ // Wait for config to load so logout button appears
+ const logoutButton = await screen.findByText("ACCOUNT_SETTINGS$LOGOUT");
await userEvent.click(logoutButton);
expect(logoutSpy).toHaveBeenCalledOnce();
@@ -488,6 +515,10 @@ describe("UserContextMenu", () => {
});
it("should call the onClose handler after each action", async () => {
+ vi.spyOn(OptionService, "getConfig").mockResolvedValue(
+ createMockWebClientConfig({ app_mode: "saas" }),
+ );
+
// Mock a team org so org management buttons are visible
vi.spyOn(organizationService, "getOrganizations").mockResolvedValue({
items: [MOCK_TEAM_ORG_ACME],
@@ -497,7 +528,8 @@ describe("UserContextMenu", () => {
const onCloseMock = vi.fn();
renderUserContextMenu({ type: "owner", onClose: onCloseMock, onOpenInviteModal: vi.fn });
- const logoutButton = screen.getByText("ACCOUNT_SETTINGS$LOGOUT");
+ // Wait for config to load so logout button appears
+ const logoutButton = await screen.findByText("ACCOUNT_SETTINGS$LOGOUT");
await userEvent.click(logoutButton);
expect(onCloseMock).toHaveBeenCalledTimes(1);
diff --git a/frontend/src/components/features/user/user-context-menu.tsx b/frontend/src/components/features/user/user-context-menu.tsx
index b9094cc6d3..424dc7c0ec 100644
--- a/frontend/src/components/features/user/user-context-menu.tsx
+++ b/frontend/src/components/features/user/user-context-menu.tsx
@@ -156,13 +156,16 @@ export function UserContextMenu({
{t(I18nKey.SIDEBAR$DOCS)}
-
-
- {t(I18nKey.ACCOUNT_SETTINGS$LOGOUT)}
-
+ {/* Only show logout in saas mode - oss mode has no session to invalidate */}
+ {isSaasMode && (
+
+
+ {t(I18nKey.ACCOUNT_SETTINGS$LOGOUT)}
+
+ )}
diff --git a/frontend/src/hooks/query/use-git-user.ts b/frontend/src/hooks/query/use-git-user.ts
index 971999f25c..a239b2d18a 100644
--- a/frontend/src/hooks/query/use-git-user.ts
+++ b/frontend/src/hooks/query/use-git-user.ts
@@ -35,13 +35,14 @@ export const useGitUser = () => {
}
}, [user.data]);
- // If we get a 401 here, it means that the integration tokens need to be
+ // In saas mode, a 401 means that the integration tokens need to be
// refreshed. Since this happens at login, we log out.
+ // In oss mode, skip auto-logout since there's no token refresh mechanism
React.useEffect(() => {
- if (user?.error?.response?.status === 401) {
+ if (user?.error?.response?.status === 401 && config?.app_mode === "saas") {
logout.mutate();
}
- }, [user.status]);
+ }, [user.status, config?.app_mode]);
return user;
};