Skip to content

Commit 849d616

Browse files
authored
Fix refreshing causing you to go to the Processor (Stirling-Tools#7694)
1 parent a48356a commit 849d616

5 files changed

Lines changed: 45 additions & 6 deletions

File tree

app/common/src/test/java/stirling/software/common/util/RequestUriUtilsTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,16 @@ void testIsFrontendRoute_extensionlessPath() {
113113
assertTrue(RequestUriUtils.isFrontendRoute("", "/split-pdf"));
114114
}
115115

116+
@Test
117+
void testIsFrontendRoute_editorRouteOwnedByFrontend() {
118+
// /editor (and its tool routes) is an SPA route: a direct-nav/refresh must
119+
// serve index.html, not the auth filter's 302-to-/login. Regression test for
120+
// the editor moving from / to /editor, whose refresh bounced processor users
121+
// to the processor because the redirect dropped the return path.
122+
assertTrue(RequestUriUtils.isFrontendRoute("", "/editor"));
123+
assertTrue(RequestUriUtils.isFrontendRoute("/app", "/app/editor"));
124+
}
125+
116126
@Test
117127
void testIsFrontendRoute_filesRouteOwnedByFrontend() {
118128
// /files and /files/<folder-uuid> are FileManagerView routes - they

app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -357,12 +357,12 @@ private SecurityFilterChain configureSecurity(
357357
req -> {
358358
String uri = req.getRequestURI();
359359
String contextPath = req.getContextPath();
360-
// Check if it's a public auth endpoint or static
361-
// resource
362360
return RequestUriUtils.isStaticResource(
363361
contextPath, uri)
364362
|| RequestUriUtils.isPublicAuthEndpoint(
365-
uri, contextPath);
363+
uri, contextPath)
364+
|| RequestUriUtils.isFrontendRoute(
365+
contextPath, uri);
366366
})
367367
.permitAll()
368368
.anyRequest()

frontend/editor/src/proprietary/routes/Login.test.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,10 @@ describe("Login", () => {
234234
);
235235
};
236236

237-
afterEach(() => window.history.replaceState({}, "", "/"));
237+
afterEach(() => {
238+
window.history.replaceState({}, "", "/");
239+
sessionStorage.clear();
240+
});
238241

239242
it("returns to where the user came from", async () => {
240243
signedIn();
@@ -247,6 +250,24 @@ describe("Login", () => {
247250
});
248251
});
249252

253+
// A full-page 401 redirect drops router state and Spring can strip ?from=,
254+
// leaving the return path only in the sessionStorage stash. Without reading
255+
// it here a processor user refreshing /editor falls through to the role
256+
// router and lands on the processor.
257+
it("returns to the stashed path when there is no ?from=", async () => {
258+
signedIn();
259+
sessionStorage.setItem("stirling_post_login_path", "/compress");
260+
renderAtLogin("");
261+
262+
await waitFor(() => {
263+
expect(mockNavigate).toHaveBeenCalledWith("/compress", {
264+
replace: true,
265+
});
266+
});
267+
// Consumed, so a later sign-in can't reuse a stale path.
268+
expect(sessionStorage.getItem("stirling_post_login_path")).toBeNull();
269+
});
270+
250271
// Delegated to the shared isSafePostLoginRedirect, so the backslash form
251272
// (browsers normalise "\" to "/") and auth routes are covered too.
252273
it.each([

frontend/editor/src/proprietary/routes/Login.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import {
77
} from "react-router-dom";
88
import { Button } from "@app/ui/Button";
99
import { isSafePostLoginRedirect } from "@app/auth";
10-
import { setPostLoginRedirectPath } from "@app/auth/spring/springAuthClient";
10+
import {
11+
setPostLoginRedirectPath,
12+
consumePostLoginRedirectPath,
13+
} from "@app/auth/spring/springAuthClient";
1114
import { useAuth } from "@app/auth/UseSession";
1215
import { useAppConfig } from "@app/contexts/AppConfigContext";
1316
import { useTranslation } from "react-i18next";
@@ -207,7 +210,8 @@ export default function Login() {
207210
useEffect(() => {
208211
if (loading) return;
209212
if (!session) return;
210-
const returnPath = resolveReturnPath();
213+
const stashed = consumePostLoginRedirectPath();
214+
const returnPath = resolveReturnPath() ?? stashed;
211215
if (returnPath) {
212216
navigate(returnPath, { replace: true });
213217
return;

frontend/editor/src/proprietary/services/apiClientSetup.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from "axios";
22
import { withBasePath } from "@app/constants/app";
33
import { getBrowserId } from "@app/utils/browserIdentifier";
4+
import { setPostLoginRedirectPath } from "@app/auth/spring/springAuthClient";
45

56
let isRefreshing = false;
67
let failedQueue: Array<{
@@ -93,6 +94,9 @@ async function refreshAuthToken(client: AxiosInstance): Promise<string> {
9394
// Redirect to login
9495
const loginPath = withBasePath("/login");
9596
if (window.location.pathname !== loginPath) {
97+
setPostLoginRedirectPath(
98+
window.location.pathname + window.location.search,
99+
);
96100
console.log("[API Client] Redirecting to login page...");
97101
window.location.href = loginPath;
98102
}

0 commit comments

Comments
 (0)