Revert "hotfix(frontend): Show auth modal on 401" (#8287)

This commit is contained in:
sp.wack 2025-05-06 00:10:07 +04:00 committed by GitHub
parent a8e15be13f
commit da4bf5bb29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 49 additions and 56 deletions

View File

@ -6,7 +6,7 @@ import * as AuthHook from "#/context/auth-context";
// Mock the useAuthUrl hook
vi.mock("#/hooks/use-auth-url", () => ({
useAuthUrl: () => "https://gitlab.com/oauth/authorize",
useAuthUrl: () => "https://gitlab.com/oauth/authorize"
}));
describe("AuthModal", () => {
@ -16,8 +16,7 @@ describe("AuthModal", () => {
providersAreSet: false,
setProvidersAreSet: vi.fn(),
providerTokensSet: [],
setProviderTokensSet: vi.fn(),
clear: vi.fn(),
setProviderTokensSet: vi.fn()
});
});
@ -29,12 +28,8 @@ describe("AuthModal", () => {
it("should render the GitHub and GitLab buttons", () => {
render(<AuthModal githubAuthUrl="mock-url" appMode="saas" />);
const githubButton = screen.getByRole("button", {
name: "GITHUB$CONNECT_TO_GITHUB",
});
const gitlabButton = screen.getByRole("button", {
name: "GITLAB$CONNECT_TO_GITLAB",
});
const githubButton = screen.getByRole("button", { name: "GITHUB$CONNECT_TO_GITHUB" });
const gitlabButton = screen.getByRole("button", { name: "GITLAB$CONNECT_TO_GITLAB" });
expect(githubButton).toBeInTheDocument();
expect(gitlabButton).toBeInTheDocument();
@ -45,9 +40,7 @@ describe("AuthModal", () => {
const mockUrl = "https://github.com/login/oauth/authorize";
render(<AuthModal githubAuthUrl={mockUrl} appMode="saas" />);
const githubButton = screen.getByRole("button", {
name: "GITHUB$CONNECT_TO_GITHUB",
});
const githubButton = screen.getByRole("button", { name: "GITHUB$CONNECT_TO_GITHUB" });
await user.click(githubButton);
expect(window.location.href).toBe(mockUrl);

View File

@ -12,6 +12,7 @@ import { ConversationPanel } from "#/components/features/conversation-panel/conv
import OpenHands from "#/api/open-hands";
import { AuthProvider } from "#/context/auth-context";
import { clickOnEditButton } from "./utils";
import { queryClientConfig } from "#/query-client-config";
import { renderWithProviders } from "test-utils";
describe("ConversationPanel", () => {
@ -23,7 +24,7 @@ describe("ConversationPanel", () => {
},
]);
const renderConversationPanel = (config?: object) =>
const renderConversationPanel = (config?: QueryClientConfig) =>
renderWithProviders(<RouterStub />, {
preloadedState: {
metrics: {

View File

@ -73,7 +73,7 @@ describe("HomeScreen", () => {
screen.getByTestId("task-suggestions");
});
it("should have responsive layout for mobile and desktop screens", () => {
it("should have responsive layout for mobile and desktop screens", async () => {
renderHomeScreen();
const mainContainer = screen

View File

@ -6,7 +6,6 @@ interface AuthContextType {
setProviderTokensSet: (tokens: Provider[]) => void;
providersAreSet: boolean;
setProvidersAreSet: (status: boolean) => void;
clear: () => void;
}
interface AuthContextProps extends React.PropsWithChildren {
@ -24,24 +23,19 @@ function AuthProvider({
const [providerTokensSet, setProviderTokensSet] = React.useState<Provider[]>(
initialProviderTokens,
);
const [providersAreSet, setProvidersAreSet] = React.useState<boolean>(
initialProvidersAreSet,
);
const clear = React.useCallback(() => {
setProviderTokensSet([]);
setProvidersAreSet(false);
}, []);
const value = React.useMemo(
() => ({
providerTokensSet,
setProviderTokensSet,
providersAreSet,
setProvidersAreSet,
clear,
}),
[providerTokensSet, providersAreSet, clear],
[providerTokensSet],
);
return <AuthContext value={value}>{children}</AuthContext>;

View File

@ -11,10 +11,10 @@ import { hydrateRoot } from "react-dom/client";
import { Provider } from "react-redux";
import posthog from "posthog-js";
import "./i18n";
import { QueryClientProvider } from "@tanstack/react-query";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import store from "./store";
import { AuthProvider } from "./context/auth-context";
import { queryClient } from "./query-client-config";
import { queryClientConfig } from "./query-client-config";
import OpenHands from "./api/open-hands";
import { displayErrorToast } from "./utils/custom-toast-handlers";
@ -59,6 +59,8 @@ async function prepareApp() {
}
}
export const queryClient = new QueryClient(queryClientConfig);
prepareApp().then(() =>
startTransition(() => {
hydrateRoot(

View File

@ -1,16 +1,19 @@
import { useQuery } from "@tanstack/react-query";
import React from "react";
import OpenHands from "#/api/open-hands";
import { useConfig } from "./use-config";
import { useAuth } from "#/context/auth-context";
import { useIsOnTosPage } from "#/hooks/use-is-on-tos-page";
export const useIsAuthed = () => {
const { providersAreSet } = useAuth();
const { data: config } = useConfig();
const isOnTosPage = useIsOnTosPage();
const appMode = config?.APP_MODE;
const appMode = React.useMemo(() => config?.APP_MODE, [config]);
return useQuery({
queryKey: ["user", "authenticated", appMode],
queryKey: ["user", "authenticated", providersAreSet, appMode],
queryFn: () => OpenHands.authenticate(appMode!),
enabled: !!appMode && !isOnTosPage,
staleTime: 1000 * 60 * 5, // 5 minutes

View File

@ -1,10 +1,29 @@
import React from "react";
import { generateAuthUrl } from "#/utils/generate-auth-url";
import { GetConfigResponse } from "#/api/open-hands.types";
import { useAuth } from "#/context/auth-context";
interface UseAuthUrlConfig {
appMode: GetConfigResponse["APP_MODE"] | null;
identityProvider: string;
}
export const useAuthUrl = (config: UseAuthUrlConfig) =>
generateAuthUrl(config.identityProvider, new URL(window.location.href));
export const useAuthUrl = (config: UseAuthUrlConfig) => {
const { providersAreSet } = useAuth();
return React.useMemo(() => {
if (config.appMode === "saas" && !providersAreSet) {
try {
return generateAuthUrl(
config.identityProvider,
new URL(window.location.href),
);
} catch (e) {
// In test environment, window.location.href might not be a valid URL
return null;
}
}
return null;
}, [providersAreSet, config.appMode, config.identityProvider]);
};

View File

@ -1,27 +1,17 @@
import { QueryCache, MutationCache, QueryClient } from "@tanstack/react-query";
import {
QueryClientConfig,
QueryCache,
MutationCache,
} from "@tanstack/react-query";
import i18next from "i18next";
import { AxiosError } from "axios";
import { I18nKey } from "./i18n/declaration";
import { retrieveAxiosErrorMessage } from "./utils/retrieve-axios-error-message";
import { displayErrorToast } from "./utils/custom-toast-handlers";
const handle401Error = (error: AxiosError, queryClient: QueryClient) => {
if (error?.response?.status === 401 || error?.status === 401) {
queryClient.invalidateQueries({ queryKey: ["user", "authenticated"] });
}
};
const shownErrors = new Set<string>();
export const queryClient = new QueryClient({
export const queryClientConfig: QueryClientConfig = {
queryCache: new QueryCache({
onError: (error, query) => {
const isAuthQuery =
query.queryKey[0] === "user" && query.queryKey[1] === "authenticated";
if (!isAuthQuery) {
handle401Error(error, queryClient);
}
if (!query.meta?.disableToast) {
const errorMessage = retrieveAxiosErrorMessage(error);
@ -38,12 +28,10 @@ export const queryClient = new QueryClient({
}),
mutationCache: new MutationCache({
onError: (error, _, __, mutation) => {
handle401Error(error, queryClient);
if (!mutation?.meta?.disableToast) {
const message = retrieveAxiosErrorMessage(error);
displayErrorToast(message || i18next.t(I18nKey.ERROR$GENERIC));
}
},
}),
});
};

View File

@ -21,7 +21,6 @@ import { useBalance } from "#/hooks/query/use-balance";
import { SetupPaymentModal } from "#/components/features/payment/setup-payment-modal";
import { displaySuccessToast } from "#/utils/custom-toast-handlers";
import { useIsOnTosPage } from "#/hooks/use-is-on-tos-page";
import { useAuth } from "#/context/auth-context";
export function ErrorBoundary() {
const error = useRouteError();
@ -57,7 +56,6 @@ export function ErrorBoundary() {
}
export default function MainApp() {
const { clear } = useAuth();
const navigate = useNavigate();
const { pathname } = useLocation();
const tosPageStatus = useIsOnTosPage();
@ -73,8 +71,6 @@ export default function MainApp() {
isError: authError,
} = useIsAuthed();
// When on TOS page, we don't make any API calls, so we need to handle this case
const userIsAuthed = tosPageStatus ? false : !!isAuthed && !authError;
// Always call the hook, but we'll only use the result when not on TOS page
const gitHubAuthUrl = useGitHubAuthUrl({
appMode: config.data?.APP_MODE || null,
@ -129,11 +125,8 @@ export default function MainApp() {
}
}, [error?.status, pathname, tosPageStatus]);
React.useEffect(() => {
if (!userIsAuthed && !isFetchingAuth && authError) {
clear();
}
}, [userIsAuthed, isFetchingAuth, authError, clear]);
// When on TOS page, we don't make any API calls, so we need to handle this case
const userIsAuthed = tosPageStatus ? false : !!isAuthed && !authError;
// Only show the auth modal if:
// 1. User is not authenticated