From caf8d43afc7662c75b51b50d0b88ba0074a006b2 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Wed, 7 Jan 2026 17:22:38 +0200 Subject: [PATCH 1/9] auto-create default search space on registration --- surfsense_backend/app/users.py | 67 +++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/surfsense_backend/app/users.py b/surfsense_backend/app/users.py index d51b30bd7..dd284307f 100644 --- a/surfsense_backend/app/users.py +++ b/surfsense_backend/app/users.py @@ -1,3 +1,4 @@ +import logging import uuid from fastapi import Depends, Request, Response @@ -12,7 +13,17 @@ from fastapi_users.db import SQLAlchemyUserDatabase from pydantic import BaseModel from app.config import config -from app.db import User, get_user_db +from app.db import ( + SearchSpace, + SearchSpaceMembership, + SearchSpaceRole, + User, + async_session_maker, + get_default_roles_config, + get_user_db, +) + +logger = logging.getLogger(__name__) class BearerResponse(BaseModel): @@ -36,7 +47,59 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]): verification_token_secret = SECRET async def on_after_register(self, user: User, request: Request | None = None): - print(f"User {user.id} has registered.") + """ + Called after a user registers. Creates a default search space for the user + so they can start chatting immediately without manual setup. + """ + logger.info(f"User {user.id} has registered. Creating default search space...") + + try: + async with async_session_maker() as session: + # Create default search space + default_search_space = SearchSpace( + name="My Search Space", + description="Your personal search space", + user_id=user.id, + ) + session.add(default_search_space) + await session.flush() # Get the search space ID + + # Create default roles + default_roles = get_default_roles_config() + owner_role_id = None + + for role_config in default_roles: + db_role = SearchSpaceRole( + name=role_config["name"], + description=role_config["description"], + permissions=role_config["permissions"], + is_default=role_config["is_default"], + is_system_role=role_config["is_system_role"], + search_space_id=default_search_space.id, + ) + session.add(db_role) + await session.flush() + + if role_config["name"] == "Owner": + owner_role_id = db_role.id + + # Create owner membership + owner_membership = SearchSpaceMembership( + user_id=user.id, + search_space_id=default_search_space.id, + role_id=owner_role_id, + is_owner=True, + ) + session.add(owner_membership) + + await session.commit() + logger.info( + f"Created default search space (ID: {default_search_space.id}) for user {user.id}" + ) + except Exception as e: + logger.error( + f"Failed to create default search space for user {user.id}: {e}" + ) async def on_after_forgot_password( self, user: User, token: str, request: Request | None = None From 2f8919baef7ef6cea947f4a2c618972ec85a1f43 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Wed, 7 Jan 2026 17:22:49 +0200 Subject: [PATCH 2/9] auto-redirect to chat on dashboard load --- surfsense_web/app/dashboard/page.tsx | 47 ++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/surfsense_web/app/dashboard/page.tsx b/surfsense_web/app/dashboard/page.tsx index e241428d1..d343a28e3 100644 --- a/surfsense_web/app/dashboard/page.tsx +++ b/surfsense_web/app/dashboard/page.tsx @@ -7,7 +7,11 @@ import Image from "next/image"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; -import { deleteSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms"; +import { useEffect, useRef, useState } from "react"; +import { + createSearchSpaceMutationAtom, + deleteSearchSpaceMutationAtom, +} from "@/atoms/search-spaces/search-space-mutation.atoms"; import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { currentUserAtom } from "@/atoms/user/user-query.atoms"; import { Logo } from "@/components/Logo"; @@ -129,6 +133,11 @@ const ErrorScreen = ({ message }: { message: string }) => { const DashboardPage = () => { const t = useTranslations("dashboard"); const tCommon = useTranslations("common"); + const router = useRouter(); + + // State for auto-creating search space + const [isAutoCreating, setIsAutoCreating] = useState(false); + const hasAttemptedAutoCreate = useRef(false); // Animation variants const containerVariants: Variants = { @@ -161,9 +170,42 @@ const DashboardPage = () => { refetch: refreshSearchSpaces, } = useAtomValue(searchSpacesAtom); const { mutateAsync: deleteSearchSpace } = useAtomValue(deleteSearchSpaceMutationAtom); + const { mutateAsync: createSearchSpace } = useAtomValue(createSearchSpaceMutationAtom); const { data: user, isPending: isLoadingUser, error: userError } = useAtomValue(currentUserAtom); + // Auto-redirect to chat or auto-create search space + useEffect(() => { + const handleAutoRedirect = async () => { + // Don't run if still loading or already attempted + if (loading || hasAttemptedAutoCreate.current) return; + + // If user has search spaces, redirect to the first one's chat + if (searchSpaces.length > 0) { + router.replace(`/dashboard/${searchSpaces[0].id}/new-chat`); + return; + } + + // If no search spaces exist (edge case for users who registered before this feature), + // auto-create one and redirect + hasAttemptedAutoCreate.current = true; + setIsAutoCreating(true); + + try { + const newSearchSpace = await createSearchSpace({ + name: "My Search Space", + description: "Your personal search space", + }); + router.replace(`/dashboard/${newSearchSpace.id}/new-chat`); + } catch (err) { + console.error("Failed to auto-create search space:", err); + setIsAutoCreating(false); + } + }; + + handleAutoRedirect(); + }, [loading, searchSpaces, router, createSearchSpace]); + // Create user object for UserDropdown const customUser = { name: user?.email ? user.email.split("@")[0] : "User", @@ -173,7 +215,8 @@ const DashboardPage = () => { avatar: "/icon-128.png", // Default avatar }; - if (loading) return ; + // Show loading while loading, auto-redirecting, or auto-creating + if (loading || isAutoCreating || (searchSpaces.length > 0 && !error)) return ; if (error) return ; const handleDeleteSearchSpace = async (id: number) => { From 348898b08b76599dfcf3648cd2f0e7a875a2f31a Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Wed, 7 Jan 2026 17:22:57 +0200 Subject: [PATCH 3/9] check search space access before redirect --- surfsense_web/components/TokenHandler.tsx | 74 +++++++++++++++-------- 1 file changed, 48 insertions(+), 26 deletions(-) diff --git a/surfsense_web/components/TokenHandler.tsx b/surfsense_web/components/TokenHandler.tsx index 42905ac0d..6117f6ede 100644 --- a/surfsense_web/components/TokenHandler.tsx +++ b/surfsense_web/components/TokenHandler.tsx @@ -2,8 +2,10 @@ import { useRouter, useSearchParams } from "next/navigation"; import { useEffect } from "react"; +import { membersApiService } from "@/lib/apis/members-api.service"; import { getAndClearRedirectPath, setBearerToken } from "@/lib/auth-utils"; import { trackLoginSuccess } from "@/lib/posthog/events"; +import { queryClient } from "@/lib/query-client/client"; interface TokenHandlerProps { redirectPath?: string; // Default path to redirect after storing token (if no saved path) @@ -36,34 +38,54 @@ const TokenHandler = ({ const token = searchParams.get(tokenParamName); if (token) { - try { - // Track login success for OAuth flows (e.g., Google) - // Local login already tracks success before redirecting here - const alreadyTracked = sessionStorage.getItem("login_success_tracked"); - if (!alreadyTracked) { - // This is an OAuth flow (Google login) - track success - trackLoginSuccess("google"); + const handleAuth = async () => { + try { + // Track login success for OAuth flows (e.g., Google) + // Local login already tracks success before redirecting here + const alreadyTracked = sessionStorage.getItem("login_success_tracked"); + if (!alreadyTracked) { + // This is an OAuth flow (Google login) - track success + trackLoginSuccess("google"); + } + // Clear the flag for future logins + sessionStorage.removeItem("login_success_tracked"); + + // Store token in localStorage using both methods for compatibility + localStorage.setItem(storageKey, token); + setBearerToken(token); + + // Clear any cached data from previous sessions + queryClient.clear(); + + // Check if there's a saved redirect path from before the auth flow + const savedRedirectPath = getAndClearRedirectPath(); + + // Check if saved path contains a search space ID and verify access + const searchSpaceMatch = savedRedirectPath?.match(/^\/dashboard\/(\d+)/); + if (searchSpaceMatch && savedRedirectPath) { + const searchSpaceId = Number(searchSpaceMatch[1]); + try { + await membersApiService.getMyAccess({ search_space_id: searchSpaceId }); + router.push(savedRedirectPath); + return; + } catch { + // User doesn't have access, fall through to default + } + } + + // Use the saved path if available, otherwise use the default redirectPath + const finalRedirectPath = savedRedirectPath || redirectPath; + + // Redirect to the appropriate path + router.push(finalRedirectPath); + } catch (error) { + console.error("Error storing token in localStorage:", error); + // Even if there's an error, try to redirect to the default path + router.push(redirectPath); } - // Clear the flag for future logins - sessionStorage.removeItem("login_success_tracked"); + }; - // Store token in localStorage using both methods for compatibility - localStorage.setItem(storageKey, token); - setBearerToken(token); - - // Check if there's a saved redirect path from before the auth flow - const savedRedirectPath = getAndClearRedirectPath(); - - // Use the saved path if available, otherwise use the default redirectPath - const finalRedirectPath = savedRedirectPath || redirectPath; - - // Redirect to the appropriate path - router.push(finalRedirectPath); - } catch (error) { - console.error("Error storing token in localStorage:", error); - // Even if there's an error, try to redirect to the default path - router.push(redirectPath); - } + handleAuth(); } }, [searchParams, tokenParamName, storageKey, redirectPath, router]); From 5151ba381e25395b3259ab126b7cf88a1114b77c Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Wed, 7 Jan 2026 18:13:31 +0200 Subject: [PATCH 4/9] use hard navigation on login and logout to clear state --- surfsense_web/components/TokenHandler.tsx | 68 ++++++----------------- surfsense_web/components/UserDropdown.tsx | 4 +- 2 files changed, 19 insertions(+), 53 deletions(-) diff --git a/surfsense_web/components/TokenHandler.tsx b/surfsense_web/components/TokenHandler.tsx index 6117f6ede..a190fe73f 100644 --- a/surfsense_web/components/TokenHandler.tsx +++ b/surfsense_web/components/TokenHandler.tsx @@ -1,11 +1,8 @@ "use client"; -import { useRouter, useSearchParams } from "next/navigation"; +import { useSearchParams } from "next/navigation"; import { useEffect } from "react"; -import { membersApiService } from "@/lib/apis/members-api.service"; import { getAndClearRedirectPath, setBearerToken } from "@/lib/auth-utils"; -import { trackLoginSuccess } from "@/lib/posthog/events"; -import { queryClient } from "@/lib/query-client/client"; interface TokenHandlerProps { redirectPath?: string; // Default path to redirect after storing token (if no saved path) @@ -27,7 +24,6 @@ const TokenHandler = ({ tokenParamName = "token", storageKey = "surfsense_bearer_token", }: TokenHandlerProps) => { - const router = useRouter(); const searchParams = useSearchParams(); useEffect(() => { @@ -38,56 +34,26 @@ const TokenHandler = ({ const token = searchParams.get(tokenParamName); if (token) { - const handleAuth = async () => { - try { - // Track login success for OAuth flows (e.g., Google) - // Local login already tracks success before redirecting here - const alreadyTracked = sessionStorage.getItem("login_success_tracked"); - if (!alreadyTracked) { - // This is an OAuth flow (Google login) - track success - trackLoginSuccess("google"); - } - // Clear the flag for future logins - sessionStorage.removeItem("login_success_tracked"); + try { + // Store token in localStorage using both methods for compatibility + localStorage.setItem(storageKey, token); + setBearerToken(token); - // Store token in localStorage using both methods for compatibility - localStorage.setItem(storageKey, token); - setBearerToken(token); + // Check if there's a saved redirect path from before the auth flow + const savedRedirectPath = getAndClearRedirectPath(); - // Clear any cached data from previous sessions - queryClient.clear(); + // Use the saved path if available, otherwise use the default redirectPath + const finalRedirectPath = savedRedirectPath || redirectPath; - // Check if there's a saved redirect path from before the auth flow - const savedRedirectPath = getAndClearRedirectPath(); - - // Check if saved path contains a search space ID and verify access - const searchSpaceMatch = savedRedirectPath?.match(/^\/dashboard\/(\d+)/); - if (searchSpaceMatch && savedRedirectPath) { - const searchSpaceId = Number(searchSpaceMatch[1]); - try { - await membersApiService.getMyAccess({ search_space_id: searchSpaceId }); - router.push(savedRedirectPath); - return; - } catch { - // User doesn't have access, fall through to default - } - } - - // Use the saved path if available, otherwise use the default redirectPath - const finalRedirectPath = savedRedirectPath || redirectPath; - - // Redirect to the appropriate path - router.push(finalRedirectPath); - } catch (error) { - console.error("Error storing token in localStorage:", error); - // Even if there's an error, try to redirect to the default path - router.push(redirectPath); - } - }; - - handleAuth(); + // Use hard navigation to clear all React/jotai state from previous session + window.location.href = finalRedirectPath; + } catch (error) { + console.error("Error storing token in localStorage:", error); + // Even if there's an error, try to redirect to the default path + window.location.href = redirectPath; + } } - }, [searchParams, tokenParamName, storageKey, redirectPath, router]); + }, [searchParams, tokenParamName, storageKey, redirectPath]); return (
diff --git a/surfsense_web/components/UserDropdown.tsx b/surfsense_web/components/UserDropdown.tsx index 966193c7f..a7f9c89ac 100644 --- a/surfsense_web/components/UserDropdown.tsx +++ b/surfsense_web/components/UserDropdown.tsx @@ -34,14 +34,14 @@ export function UserDropdown({ if (typeof window !== "undefined") { localStorage.removeItem("surfsense_bearer_token"); - router.push("/"); + window.location.href = "/"; } } catch (error) { console.error("Error during logout:", error); // Optionally, provide user feedback if (typeof window !== "undefined") { alert("Logout failed. Please try again."); - router.push("/"); + window.location.href = "/"; } } }; From edc5f379d38413fb0903958cd5353d57f5ebd7ec Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Wed, 7 Jan 2026 18:33:32 +0200 Subject: [PATCH 5/9] auto-redirect only for single search space users --- surfsense_web/app/dashboard/page.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/surfsense_web/app/dashboard/page.tsx b/surfsense_web/app/dashboard/page.tsx index d343a28e3..22a2307f9 100644 --- a/surfsense_web/app/dashboard/page.tsx +++ b/surfsense_web/app/dashboard/page.tsx @@ -174,18 +174,22 @@ const DashboardPage = () => { const { data: user, isPending: isLoadingUser, error: userError } = useAtomValue(currentUserAtom); - // Auto-redirect to chat or auto-create search space + // Auto-redirect to chat for users with exactly 1 search space, or auto-create if none useEffect(() => { const handleAutoRedirect = async () => { // Don't run if still loading or already attempted if (loading || hasAttemptedAutoCreate.current) return; - // If user has search spaces, redirect to the first one's chat - if (searchSpaces.length > 0) { + // If user has exactly 1 search space, redirect to its chat + if (searchSpaces.length === 1) { router.replace(`/dashboard/${searchSpaces[0].id}/new-chat`); return; } + if (searchSpaces.length > 1) { + return; + } + // If no search spaces exist (edge case for users who registered before this feature), // auto-create one and redirect hasAttemptedAutoCreate.current = true; @@ -215,8 +219,8 @@ const DashboardPage = () => { avatar: "/icon-128.png", // Default avatar }; - // Show loading while loading, auto-redirecting, or auto-creating - if (loading || isAutoCreating || (searchSpaces.length > 0 && !error)) return ; + // Show loading while loading, auto-redirecting (single search space), or auto-creating + if (loading || isAutoCreating || (searchSpaces.length === 1 && !error)) return ; if (error) return ; const handleDeleteSearchSpace = async (id: number) => { From 1d3fd8d47c3e7212e83740cc86c33751e7dafb5b Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Wed, 7 Jan 2026 18:34:51 +0200 Subject: [PATCH 6/9] remove redundant comments --- surfsense_web/app/dashboard/page.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/surfsense_web/app/dashboard/page.tsx b/surfsense_web/app/dashboard/page.tsx index 22a2307f9..fbf567cba 100644 --- a/surfsense_web/app/dashboard/page.tsx +++ b/surfsense_web/app/dashboard/page.tsx @@ -177,10 +177,10 @@ const DashboardPage = () => { // Auto-redirect to chat for users with exactly 1 search space, or auto-create if none useEffect(() => { const handleAutoRedirect = async () => { - // Don't run if still loading or already attempted + if (loading || hasAttemptedAutoCreate.current) return; - // If user has exactly 1 search space, redirect to its chat + if (searchSpaces.length === 1) { router.replace(`/dashboard/${searchSpaces[0].id}/new-chat`); return; @@ -190,8 +190,7 @@ const DashboardPage = () => { return; } - // If no search spaces exist (edge case for users who registered before this feature), - // auto-create one and redirect + hasAttemptedAutoCreate.current = true; setIsAutoCreating(true); From a099bcf5fb35ae9094ebb0090267b14c4b016f89 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 9 Jan 2026 14:47:00 +0200 Subject: [PATCH 7/9] refactor: remove frontend auto-create search space logic - Removed auto-creation of default search space from frontend dashboard - Frontend now only handles auto-redirect for users with exactly 1 search space - All default search space creation should be handled by backend on_after_register --- surfsense_web/app/dashboard/page.tsx | 54 ++++++---------------------- 1 file changed, 10 insertions(+), 44 deletions(-) diff --git a/surfsense_web/app/dashboard/page.tsx b/surfsense_web/app/dashboard/page.tsx index fbf567cba..951e17a8c 100644 --- a/surfsense_web/app/dashboard/page.tsx +++ b/surfsense_web/app/dashboard/page.tsx @@ -7,11 +7,8 @@ import Image from "next/image"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; -import { useEffect, useRef, useState } from "react"; -import { - createSearchSpaceMutationAtom, - deleteSearchSpaceMutationAtom, -} from "@/atoms/search-spaces/search-space-mutation.atoms"; +import { useEffect } from "react"; +import { deleteSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms"; import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { currentUserAtom } from "@/atoms/user/user-query.atoms"; import { Logo } from "@/components/Logo"; @@ -135,10 +132,6 @@ const DashboardPage = () => { const tCommon = useTranslations("common"); const router = useRouter(); - // State for auto-creating search space - const [isAutoCreating, setIsAutoCreating] = useState(false); - const hasAttemptedAutoCreate = useRef(false); - // Animation variants const containerVariants: Variants = { hidden: { opacity: 0 }, @@ -170,44 +163,17 @@ const DashboardPage = () => { refetch: refreshSearchSpaces, } = useAtomValue(searchSpacesAtom); const { mutateAsync: deleteSearchSpace } = useAtomValue(deleteSearchSpaceMutationAtom); - const { mutateAsync: createSearchSpace } = useAtomValue(createSearchSpaceMutationAtom); const { data: user, isPending: isLoadingUser, error: userError } = useAtomValue(currentUserAtom); - // Auto-redirect to chat for users with exactly 1 search space, or auto-create if none + // Auto-redirect to chat for users with exactly 1 search space useEffect(() => { - const handleAutoRedirect = async () => { - - if (loading || hasAttemptedAutoCreate.current) return; + if (loading) return; - - if (searchSpaces.length === 1) { - router.replace(`/dashboard/${searchSpaces[0].id}/new-chat`); - return; - } - - if (searchSpaces.length > 1) { - return; - } - - - hasAttemptedAutoCreate.current = true; - setIsAutoCreating(true); - - try { - const newSearchSpace = await createSearchSpace({ - name: "My Search Space", - description: "Your personal search space", - }); - router.replace(`/dashboard/${newSearchSpace.id}/new-chat`); - } catch (err) { - console.error("Failed to auto-create search space:", err); - setIsAutoCreating(false); - } - }; - - handleAutoRedirect(); - }, [loading, searchSpaces, router, createSearchSpace]); + if (searchSpaces.length === 1) { + router.replace(`/dashboard/${searchSpaces[0].id}/new-chat`); + } + }, [loading, searchSpaces, router]); // Create user object for UserDropdown const customUser = { @@ -218,8 +184,8 @@ const DashboardPage = () => { avatar: "/icon-128.png", // Default avatar }; - // Show loading while loading, auto-redirecting (single search space), or auto-creating - if (loading || isAutoCreating || (searchSpaces.length === 1 && !error)) return ; + // Show loading while loading or auto-redirecting (single search space) + if (loading || (searchSpaces.length === 1 && !error)) return ; if (error) return ; const handleDeleteSearchSpace = async (id: number) => { From 101dd5745c0a46bcb6a344ebaad9117fa2587bc5 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 9 Jan 2026 15:00:15 +0200 Subject: [PATCH 8/9] merge dev --- surfsense_web/components/Logo.tsx | 8 +++++++- surfsense_web/components/TokenHandler.tsx | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/surfsense_web/components/Logo.tsx b/surfsense_web/components/Logo.tsx index 79799942b..58f8d1c9f 100644 --- a/surfsense_web/components/Logo.tsx +++ b/surfsense_web/components/Logo.tsx @@ -7,7 +7,13 @@ import { cn } from "@/lib/utils"; export const Logo = ({ className }: { className?: string }) => { return ( - logo + logo ); }; diff --git a/surfsense_web/components/TokenHandler.tsx b/surfsense_web/components/TokenHandler.tsx index a190fe73f..24260f485 100644 --- a/surfsense_web/components/TokenHandler.tsx +++ b/surfsense_web/components/TokenHandler.tsx @@ -3,6 +3,7 @@ import { useSearchParams } from "next/navigation"; import { useEffect } from "react"; import { getAndClearRedirectPath, setBearerToken } from "@/lib/auth-utils"; +import { trackLoginSuccess } from "@/lib/posthog/events"; interface TokenHandlerProps { redirectPath?: string; // Default path to redirect after storing token (if no saved path) @@ -35,6 +36,16 @@ const TokenHandler = ({ if (token) { try { + // Track login success for OAuth flows (e.g., Google) + // Local login already tracks success before redirecting here + const alreadyTracked = sessionStorage.getItem("login_success_tracked"); + if (!alreadyTracked) { + // This is an OAuth flow (Google login) - track success + trackLoginSuccess("google"); + } + // Clear the flag for future logins + sessionStorage.removeItem("login_success_tracked"); + // Store token in localStorage using both methods for compatibility localStorage.setItem(storageKey, token); setBearerToken(token); From 532f0039d59c3306ccb5a64fded5a021d4322506 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 9 Jan 2026 15:01:33 +0200 Subject: [PATCH 9/9] merge dev --- surfsense_web/components/TokenHandler.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/surfsense_web/components/TokenHandler.tsx b/surfsense_web/components/TokenHandler.tsx index 24260f485..b4ca36298 100644 --- a/surfsense_web/components/TokenHandler.tsx +++ b/surfsense_web/components/TokenHandler.tsx @@ -45,7 +45,7 @@ const TokenHandler = ({ } // Clear the flag for future logins sessionStorage.removeItem("login_success_tracked"); - + // Store token in localStorage using both methods for compatibility localStorage.setItem(storageKey, token); setBearerToken(token); @@ -56,7 +56,7 @@ const TokenHandler = ({ // Use the saved path if available, otherwise use the default redirectPath const finalRedirectPath = savedRedirectPath || redirectPath; - // Use hard navigation to clear all React/jotai state from previous session + // Redirect to the appropriate path window.location.href = finalRedirectPath; } catch (error) { console.error("Error storing token in localStorage:", error);