diff --git a/src/context/UserContext.tsx b/src/context/UserContext.tsx index bd07989b5b..270040c214 100644 --- a/src/context/UserContext.tsx +++ b/src/context/UserContext.tsx @@ -23,9 +23,20 @@ export const UserContext = ({ initialUser, children }: UserContextProps) => { }, [router.pathname, revalidate]); useEffect(() => { + // Only treat the session as invalid when the server actually rejected + // it. A check that never completed (e.g. the fetch was aborted because + // the user navigated away, or the server was briefly unreachable) + // rejects without a response; redirecting on those kicks logged-in + // users to /login and can even cancel an in-flight navigation to + // another site, dragging the browser back to the app. A transiently + // empty user (first fetch still in flight) must not navigate either — + // unauthenticated page loads are already redirected server-side. + const sessionInvalid = + error?.response?.status === 401 || error?.response?.status === 403; + if ( !router.pathname.match(/(setup|login|resetpassword)/) && - (!user || error) && + sessionInvalid && !routing.current ) { routing.current = true; diff --git a/src/hooks/useUser.ts b/src/hooks/useUser.ts index ce990d1454..b66dc9bce0 100644 --- a/src/hooks/useUser.ts +++ b/src/hooks/useUser.ts @@ -2,6 +2,7 @@ import { UserType } from '@server/constants/user'; import type { PermissionCheckOptions } from '@server/lib/permissions'; import { hasPermission, Permission } from '@server/lib/permissions'; import type { NotificationAgentKey } from '@server/lib/settings'; +import type { AxiosError } from 'axios'; import { useRouter } from 'next/router'; import type { MutatorCallback } from 'swr'; import useSWR from 'swr'; @@ -41,7 +42,7 @@ export interface UserSettings { interface UserHookResponse { user?: User; loading: boolean; - error: string; + error?: AxiosError; revalidate: ( data?: User | Promise | MutatorCallback | undefined, shouldRevalidate?: boolean | undefined