From a4394e6a2567bb94ba5a4ed3bccec0321f0e3f18 Mon Sep 17 00:00:00 2001 From: lenhanphung Date: Mon, 15 Sep 2025 11:46:20 +0700 Subject: [PATCH] fix(auth): preserve session on reload and correct 401 redirect flow - Save PKCE params to sessionStorage.redirectState on 401 instead of overwriting tokenSet - Persist userData in sessionStorage after OIDC callback for later hydration - Hydrate Redux (tokens + userData) from sessionStorage on app start to avoid forced re-login - Keep SPA single route '/' behavior unchanged; only improves auth robustness Files: src/utils/apiUtils.ts, src/features/User/LoginCallback.tsx, src/features/User/HandleLogin.tsx --- src/features/User/HandleLogin.tsx | 17 ++++++++++++++++- src/features/User/LoginCallback.tsx | 1 + src/utils/apiUtils.ts | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/features/User/HandleLogin.tsx b/src/features/User/HandleLogin.tsx index 6828b22..4091855 100644 --- a/src/features/User/HandleLogin.tsx +++ b/src/features/User/HandleLogin.tsx @@ -5,6 +5,7 @@ import { Loading } from "../../components/Loading/Loading"; import { Error } from "../../components/Error/Error"; import { push } from "redux-first-history"; import { redirectTo } from "../../utils/apiUtils"; +import { setTokens, setUserData } from "./userSlice"; export function HandleLogin() { const userData = useAppSelector((state) => state.user); @@ -13,6 +14,20 @@ export function HandleLogin() { useEffect(() => { const initiateLogin = async () => { if (!userData.userData) { + const savedToken = sessionStorage.getItem("tokenSet") + ? JSON.parse(sessionStorage.getItem("tokenSet")!) + : null; + const savedUser = sessionStorage.getItem("userData") + ? JSON.parse(sessionStorage.getItem("userData")!) + : null; + + if (savedToken && savedUser) { + dispatch(setTokens(savedToken)); + dispatch(setUserData(savedUser)); + dispatch(push("/")); + return; + } + const loginurl = await Auth(); sessionStorage.setItem( @@ -28,7 +43,7 @@ export function HandleLogin() { }; initiateLogin(); - }, [userData]); + }, [userData, dispatch]); if (!calendars.pending && !userData.loading) { dispatch(push("/error")); diff --git a/src/features/User/LoginCallback.tsx b/src/features/User/LoginCallback.tsx index 1eb6392..966307e 100644 --- a/src/features/User/LoginCallback.tsx +++ b/src/features/User/LoginCallback.tsx @@ -27,6 +27,7 @@ export function CallbackResume() { sessionStorage.removeItem("redirectState"); sessionStorage.setItem("tokenSet", JSON.stringify(data?.tokenSet)); + sessionStorage.setItem("userData", JSON.stringify(data?.userinfo)); // Redirect to main page after successful callback dispatch(push("/")); } catch (e) { diff --git a/src/utils/apiUtils.ts b/src/utils/apiUtils.ts index 2050e24..7af4120 100644 --- a/src/utils/apiUtils.ts +++ b/src/utils/apiUtils.ts @@ -23,7 +23,7 @@ export const api = ky.extend({ const loginurl = await Auth(); sessionStorage.setItem( - "tokenSet", + "redirectState", JSON.stringify({ code_verifier: loginurl.code_verifier, state: loginurl.state,