Return to the requested URL after logging in (#24637)

When an unauthenticated user opens a deep link, carry the requested
path through the login page in a redirect query param and return to it
after a successful login instead of always going to the home page.
Only same-origin targets are honored.


Claude-Session: https://claude.ai/code/session_011CFGjgYURSRTpXBaxp1Dq7

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ljlongwing
2026-10-11 17:59:13 -06:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 135a95d8f2
commit e7164882f5
5 changed files with 125 additions and 8 deletions
+45
View File
@@ -1,3 +1,5 @@
import { baseUrl } from "./baseUrl";
// Module-level flag to prevent multiple simultaneous redirects
// (eg, when multiple SWR queries fail with 401 at once, or when
// both ApiProvider and ProtectedRoute try to redirect)
@@ -10,3 +12,46 @@ export function isRedirectingToLogin(): boolean {
export function setRedirectingToLogin(value: boolean): void {
_isRedirectingToLogin = value;
}
const REDIRECT_PARAM = "redirect";
// Build the login page url, carrying the current location along
// so the user can be returned to it after logging in
export function getLoginUrl(loginPage: string = `${baseUrl}login`): string {
const url = new URL(loginPage, window.location.href);
// an external login page (eg, an auth proxy) handles its own return url
if (url.origin !== window.location.origin) {
return loginPage;
}
const { pathname, search, hash } = window.location;
const current = `${pathname}${search}${hash}`;
// no need to carry the home page, it is the default after logging in
if (current !== new URL(baseUrl).pathname) {
url.searchParams.set(REDIRECT_PARAM, current);
}
return url.href;
}
// Resolve where to send the user after a successful login
export function getPostLoginUrl(): string {
const target = new URLSearchParams(window.location.search).get(
REDIRECT_PARAM,
);
if (!target) {
return baseUrl;
}
// only return to urls on this origin so the param can't be used
// to send the user to another site
try {
const url = new URL(target, window.location.origin);
return url.origin === window.location.origin ? url.href : baseUrl;
} catch {
return baseUrl;
}
}
+6 -2
View File
@@ -3,7 +3,11 @@ import { SWRConfig } from "swr";
import { WsProvider } from "./WsProvider";
import axios from "axios";
import { ReactNode } from "react";
import { isRedirectingToLogin, setRedirectingToLogin } from "./auth-redirect";
import {
getLoginUrl,
isRedirectingToLogin,
setRedirectingToLogin,
} from "./auth-redirect";
axios.defaults.baseURL = `${baseUrl}api/`;
@@ -34,7 +38,7 @@ export function ApiProvider({ children, options }: ApiProviderType) {
const loginPage = error.response.headers.get("location") ?? "login";
if (window.location.href !== loginPage && !isRedirectingToLogin()) {
setRedirectingToLogin(true);
window.location.href = loginPage;
window.location.href = getLoginUrl(loginPage);
}
}
},
+2 -2
View File
@@ -2,7 +2,7 @@
import * as React from "react";
import { baseUrl } from "../../api/baseUrl";
import { getPostLoginUrl } from "@/api/auth-redirect";
import { cn } from "@/lib/utils";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
@@ -69,7 +69,7 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
username: profileRes.data.username,
role: profileRes.data.role || "viewer",
});
window.location.href = baseUrl;
window.location.href = getPostLoginUrl();
} catch (error) {
if (axios.isAxiosError(error)) {
const err = error as AxiosError;
+2 -2
View File
@@ -3,10 +3,10 @@ import { Navigate, Outlet } from "react-router-dom";
import { AuthContext } from "@/context/auth-context";
import ActivityIndicator from "../indicators/activity-indicator";
import {
getLoginUrl,
isRedirectingToLogin,
setRedirectingToLogin,
} from "@/api/auth-redirect";
import { baseUrl } from "@/api/baseUrl";
export default function ProtectedRoute({
requiredRoles,
@@ -25,7 +25,7 @@ export default function ProtectedRoute({
!isRedirectingToLogin()
) {
setRedirectingToLogin(true);
window.location.href = `${baseUrl}login`;
window.location.href = getLoginUrl();
}
}, [auth.isLoading, auth.isAuthenticated, auth.user]);