diff --git a/site/src/pages/LoginPage/LoginPage.test.tsx b/site/src/pages/LoginPage/LoginPage.test.tsx index 554a4be49f..6cf8a6646f 100644 --- a/site/src/pages/LoginPage/LoginPage.test.tsx +++ b/site/src/pages/LoginPage/LoginPage.test.tsx @@ -1,10 +1,11 @@ +import { MockUserOwner } from "testHelpers/entities"; import { render, renderWithRouter, waitForLoaderToBeRemoved, } from "testHelpers/renderHelpers"; import { server } from "testHelpers/server"; -import { fireEvent, screen } from "@testing-library/react"; +import { fireEvent, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { HttpResponse, http } from "msw"; import { createMemoryRouter } from "react-router"; @@ -76,4 +77,147 @@ describe("LoginPage", () => { // Then await screen.findByText("Setup"); }); + + it("redirects to /oauth2/authorize via server-side redirect when signed in", async () => { + // Given - user is signed in + server.use( + http.get("/api/v2/users/me", () => { + return HttpResponse.json(MockUserOwner); + }), + ); + + const redirectPath = + "/oauth2/authorize?client_id=xxx&response_type=code&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback"; + + // Spy on window.location.href assignment + const locationHrefSpy = vi.fn(); + const originalLocation = window.location; + Object.defineProperty(window, "location", { + configurable: true, + value: { + ...originalLocation, + origin: originalLocation.origin, + set href(url: string) { + locationHrefSpy(url); + }, + get href() { + return originalLocation.href; + }, + }, + }); + + // When + renderWithRouter( + createMemoryRouter( + [ + { + path: "/login", + element: , + }, + ], + { + initialEntries: [ + `/login?redirect=${encodeURIComponent(redirectPath)}`, + ], + }, + ), + ); + + // Then - it should perform a server-side redirect, not a React navigate + await waitFor(() => { + expect(locationHrefSpy).toHaveBeenCalledWith( + expect.stringContaining("/oauth2/authorize"), + ); + }); + + // Cleanup + Object.defineProperty(window, "location", { + configurable: true, + value: originalLocation, + }); + }); + + it("redirects to /oauth2/authorize after successful login when not already signed in", async () => { + // Given - user is NOT signed in + let loggedIn = false; + server.use( + http.get("/api/v2/users/me", () => { + if (!loggedIn) { + return HttpResponse.json( + { message: "no user here" }, + { status: 401 }, + ); + } + return HttpResponse.json(MockUserOwner); + }), + http.post("/api/v2/users/login", () => { + loggedIn = true; + return HttpResponse.json({ + session_token: "test-session-token", + }); + }), + ); + + const redirectPath = + "/oauth2/authorize?client_id=xxx&response_type=code&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback"; + + // Spy on window.location.href + const originalLocation = window.location; + const locationHrefSpy = vi.fn(); + + Object.defineProperty(window, "location", { + configurable: true, + value: { + ...originalLocation, + origin: originalLocation.origin, + set href(url: string) { + locationHrefSpy(url); + }, + get href() { + return originalLocation.href; + }, + }, + }); + + // When + renderWithRouter( + createMemoryRouter( + [ + { + path: "/login", + element: , + }, + ], + { + initialEntries: [ + `/login?redirect=${encodeURIComponent(redirectPath)}`, + ], + }, + ), + ); + + await waitForLoaderToBeRemoved(); + + const email = screen.getByLabelText(new RegExp(Language.emailLabel)); + const password = screen.getByLabelText(new RegExp(Language.passwordLabel)); + + await userEvent.type(email, "test@coder.com"); + await userEvent.type(password, "password"); + + const signInButton = await screen.findByText(Language.passwordSignIn); + fireEvent.click(signInButton); + + // Then - it should hard redirect to OAuth endpoint + await waitFor(() => { + expect(locationHrefSpy).toHaveBeenCalledWith( + expect.stringContaining("/oauth2/authorize"), + ); + }); + + // Cleanup + Object.defineProperty(window, "location", { + configurable: true, + value: originalLocation, + }); + }); }); diff --git a/site/src/pages/LoginPage/LoginPage.tsx b/site/src/pages/LoginPage/LoginPage.tsx index 20c1d4dea3..50da94534a 100644 --- a/site/src/pages/LoginPage/LoginPage.tsx +++ b/site/src/pages/LoginPage/LoginPage.tsx @@ -35,7 +35,9 @@ const LoginPage: FC = () => { // Do nothing } - const isApiRouteRedirect = redirectTo.startsWith("/api/v2"); + const isApiRouteRedirect = + redirectTo.startsWith("/api/v2") || + redirectTo.startsWith("/oauth2/authorize"); useEffect(() => { if (!buildInfoQuery.data || isSignedIn) {