diff --git a/src/components/Calendar/hooks/useUtilMenus.ts b/src/components/Calendar/hooks/useUtilMenus.ts index 5974124..bfd967b 100644 --- a/src/components/Calendar/hooks/useUtilMenus.ts +++ b/src/components/Calendar/hooks/useUtilMenus.ts @@ -58,7 +58,7 @@ export const useUtilMenus = (): { } catch (error) { console.error('Logout failed:', error) } finally { - sessionStorage.removeItem('tokenSet') + localStorage.removeItem('tokenSet') handleUserMenuClose() } } diff --git a/src/features/User/LoginCallback.tsx b/src/features/User/LoginCallback.tsx index b3c2425..781553e 100644 --- a/src/features/User/LoginCallback.tsx +++ b/src/features/User/LoginCallback.tsx @@ -27,26 +27,26 @@ export function CallbackResume() { const runCallback = async () => { // Read redirectState inside useEffect to avoid stale closures - const saved = sessionStorage.getItem('redirectState') - ? JSON.parse(sessionStorage.getItem('redirectState') ?? '{}') + const saved = localStorage.getItem('redirectState') + ? JSON.parse(localStorage.getItem('redirectState') ?? '{}') : null // Check if we have saved tokens (already logged in) - const savedToken = sessionStorage.getItem('tokenSet') - ? JSON.parse(sessionStorage.getItem('tokenSet') ?? '{}') + const savedToken = localStorage.getItem('tokenSet') + ? JSON.parse(localStorage.getItem('tokenSet') ?? '{}') : null // If no redirectState but we have saved session, just go home // This can happen if user refreshes callback page or gets redirected here after already logged in if (!saved?.code_verifier) { if (savedToken) { - sessionStorage.removeItem('redirectState') + localStorage.removeItem('redirectState') dispatch(replace('/')) return } console.warn('Missing redirectState') - sessionStorage.removeItem('redirectState') + localStorage.removeItem('redirectState') dispatch(replace('/')) return } @@ -60,10 +60,14 @@ export function CallbackResume() { throw new Error('OAuth callback failed') } - // IMPORTANT: Save tokens to sessionStorage FIRST before making any API calls - // because API calls will read token from sessionStorage - sessionStorage.setItem('tokenSet', JSON.stringify(data.tokenSet)) - sessionStorage.setItem('userData', JSON.stringify(data.userinfo)) + // IMPORTANT: Save tokens to localStorage FIRST before making any API calls + // because API calls will read token from localStorage. + // localStorage (not sessionStorage) so the OIDC session is shared + // across browser tabs — otherwise each new tab forces a re-login + // since Dex (with enablePasswordDB connector) does NOT keep an + // IdP-side session cookie. + localStorage.setItem('tokenSet', JSON.stringify(data.tokenSet)) + localStorage.setItem('userData', JSON.stringify(data.userinfo)) dispatch(setUserData(data.userinfo)) dispatch(setTokens(data.tokenSet)) @@ -71,7 +75,7 @@ export function CallbackResume() { await dispatch(getOpenPaasUserDataAsync()) await dispatch(getCalendarsListAsync()) - sessionStorage.removeItem('redirectState') + localStorage.removeItem('redirectState') } catch (e) { console.error('OIDC callback error:', e) dispatch(setAppLoading(false)) diff --git a/src/features/User/useInitializeApp.ts b/src/features/User/useInitializeApp.ts index 7a63b16..907ea7d 100644 --- a/src/features/User/useInitializeApp.ts +++ b/src/features/User/useInitializeApp.ts @@ -23,11 +23,11 @@ export function useInitializeApp() { hasInitiatedRef.current = true const initiateLogin = async () => { - const savedToken = sessionStorage.getItem('tokenSet') - ? JSON.parse(sessionStorage.getItem('tokenSet') ?? '{}') + const savedToken = localStorage.getItem('tokenSet') + ? JSON.parse(localStorage.getItem('tokenSet') ?? '{}') : null - const savedUser = sessionStorage.getItem('userData') - ? JSON.parse(sessionStorage.getItem('userData') ?? '{}') + const savedUser = localStorage.getItem('userData') + ? JSON.parse(localStorage.getItem('userData') ?? '{}') : null if (savedToken && savedUser) { @@ -45,7 +45,7 @@ export function useInitializeApp() { } const loginurl = await Auth() - sessionStorage.setItem( + localStorage.setItem( 'redirectState', JSON.stringify({ code_verifier: loginurl.code_verifier, diff --git a/src/utils/apiUtils.ts b/src/utils/apiUtils.ts index a04e0b8..16a0c9b 100644 --- a/src/utils/apiUtils.ts +++ b/src/utils/apiUtils.ts @@ -35,7 +35,7 @@ const redirectSSO = async ( try { const loginurl = await Auth() - sessionStorage.setItem( + localStorage.setItem( 'redirectState', JSON.stringify({ code_verifier: loginurl.code_verifier, @@ -85,9 +85,9 @@ export const api: KyInstance = ky.extend({ hooks: { beforeRequest: [ async (request: KyRequest): Promise => { - const saved = sessionStorage.getItem('tokenSet') + const saved = localStorage.getItem('tokenSet') ? (JSON.parse( - sessionStorage.getItem('tokenSet') ?? '{}' + localStorage.getItem('tokenSet') ?? '{}' ) as TokenEndpointResponse & TokenEndpointResponseHelpers) : null const access_token = saved?.access_token as string