From 3664f5bc200c027f58263f214c57356299e89dc2 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Fri, 31 Oct 2025 12:10:40 +0100 Subject: [PATCH] Tweak browser extension logout flow to suppress session errors (#1325) --- .../src/entrypoints/popup/App.tsx | 2 -- .../entrypoints/popup/context/AppContext.tsx | 23 ++++++++----------- .../popup/context/NavigationContext.tsx | 1 - .../entrypoints/popup/pages/auth/Logout.tsx | 21 ----------------- .../entrypoints/popup/pages/auth/Unlock.tsx | 2 +- .../entrypoints/popup/pages/auth/Upgrade.tsx | 4 ++-- .../popup/pages/settings/Settings.tsx | 2 +- 7 files changed, 13 insertions(+), 42 deletions(-) delete mode 100644 apps/browser-extension/src/entrypoints/popup/pages/auth/Logout.tsx diff --git a/apps/browser-extension/src/entrypoints/popup/App.tsx b/apps/browser-extension/src/entrypoints/popup/App.tsx index a629d1b01..9e22443f8 100644 --- a/apps/browser-extension/src/entrypoints/popup/App.tsx +++ b/apps/browser-extension/src/entrypoints/popup/App.tsx @@ -12,7 +12,6 @@ import { useLoading } from '@/entrypoints/popup/context/LoadingContext'; import { NavigationProvider } from '@/entrypoints/popup/context/NavigationContext'; import AuthSettings from '@/entrypoints/popup/pages/auth/AuthSettings'; import Login from '@/entrypoints/popup/pages/auth/Login'; -import Logout from '@/entrypoints/popup/pages/auth/Logout'; import Unlock from '@/entrypoints/popup/pages/auth/Unlock'; import UnlockSuccess from '@/entrypoints/popup/pages/auth/UnlockSuccess'; import Upgrade from '@/entrypoints/popup/pages/auth/Upgrade'; @@ -168,7 +167,6 @@ const App: React.FC = () => { { path: '/settings/language', element: , showBackButton: true, title: t('settings.language') }, { path: '/settings/auto-lock', element: , showBackButton: true, title: t('settings.autoLockTimeout') }, { path: '/settings/passkeys', element: , showBackButton: true, title: t('settings.passkeySettings') }, - { path: '/logout', element: , showBackButton: false }, ], [t]); useEffect(() => { diff --git a/apps/browser-extension/src/entrypoints/popup/context/AppContext.tsx b/apps/browser-extension/src/entrypoints/popup/context/AppContext.tsx index c947c4ed8..c4b8bab0a 100644 --- a/apps/browser-extension/src/entrypoints/popup/context/AppContext.tsx +++ b/apps/browser-extension/src/entrypoints/popup/context/AppContext.tsx @@ -1,4 +1,4 @@ -import React, { createContext, useContext, useMemo, useCallback, useEffect, useState } from 'react'; +import React, { createContext, useContext, useMemo, useCallback, useEffect, useState, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useAuth } from '@/entrypoints/popup/context/AuthContext'; @@ -15,7 +15,6 @@ type AppContextType = { setAuthTokens: (username: string, accessToken: string, refreshToken: string) => Promise; globalMessage: string | null; clearGlobalMessage: () => void; - isLoggingOut: boolean; } const AppContext = createContext(undefined); @@ -27,7 +26,7 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({ children const auth = useAuth(); const webApi = useWebApi(); const [isLoggedIn, setIsLoggedIn] = useState(false); - const [isLoggingOut, setIsLoggingOut] = useState(false); + const isLoggingOutRef = useRef(false); const { t } = useTranslation(); /** @@ -35,23 +34,21 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({ children * Prevents recursive logout calls by tracking logout state. */ const logout = useCallback(async (errorMessage?: string): Promise => { - // Prevent recursive logout calls - if (isLoggingOut) { - console.debug('Logout already in progress, ignoring duplicate call'); + if (isLoggingOutRef.current) { return; } try { - setIsLoggingOut(true); - webApi.revokeTokens(); - auth.clearAuth(errorMessage); - setIsLoggedIn(false); + isLoggingOutRef.current = true; + await webApi.revokeTokens(); + await auth.clearAuth(errorMessage); } catch (error) { console.error('Error during logout:', error); } finally { - setIsLoggingOut(false); + isLoggingOutRef.current = false; + setIsLoggedIn(false); } - }, [auth, webApi, isLoggingOut]); + }, [auth, webApi]); /** * Initialize the authentication state. @@ -93,7 +90,6 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({ children setAuthTokens: auth.setAuthTokens, clearGlobalMessage: auth.clearGlobalMessage, isLoggedIn: isLoggedIn, - isLoggingOut: isLoggingOut, }), [ auth.isInitialized, auth.username, @@ -103,7 +99,6 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({ children logout, initializeAuth, isLoggedIn, - isLoggingOut, ]); return ( diff --git a/apps/browser-extension/src/entrypoints/popup/context/NavigationContext.tsx b/apps/browser-extension/src/entrypoints/popup/context/NavigationContext.tsx index 99bb53744..d5af47ce4 100644 --- a/apps/browser-extension/src/entrypoints/popup/context/NavigationContext.tsx +++ b/apps/browser-extension/src/entrypoints/popup/context/NavigationContext.tsx @@ -52,7 +52,6 @@ export const NavigationProvider: React.FC<{ children: React.ReactNode }> = ({ ch '/unlock-success', '/auth-settings', '/upgrade', - '/logout', '/passkeys/create', '/passkeys/authenticate' ]; diff --git a/apps/browser-extension/src/entrypoints/popup/pages/auth/Logout.tsx b/apps/browser-extension/src/entrypoints/popup/pages/auth/Logout.tsx deleted file mode 100644 index 93db3c1c1..000000000 --- a/apps/browser-extension/src/entrypoints/popup/pages/auth/Logout.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React, { useEffect } from 'react'; - -import { useApp } from '@/entrypoints/popup/context/AppContext'; - -/** - * Logout page. - */ -const Logout: React.FC = () => { - const app = useApp(); - /** - * Logout and navigate to home page. - */ - useEffect(() => { - app.logout(); - }, [app]); - - // Return null since this is just a functional component that handles logout. - return null; -}; - -export default Logout; diff --git a/apps/browser-extension/src/entrypoints/popup/pages/auth/Unlock.tsx b/apps/browser-extension/src/entrypoints/popup/pages/auth/Unlock.tsx index 65c132f84..40adc9c02 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/auth/Unlock.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/auth/Unlock.tsx @@ -152,7 +152,7 @@ const Unlock: React.FC = () => { * Handle logout */ const handleLogout = () : void => { - navigate('/logout', { replace: true }); + app.logout(); }; return ( diff --git a/apps/browser-extension/src/entrypoints/popup/pages/auth/Upgrade.tsx b/apps/browser-extension/src/entrypoints/popup/pages/auth/Upgrade.tsx index 0459322d6..7ca57ca5b 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/auth/Upgrade.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/auth/Upgrade.tsx @@ -24,7 +24,7 @@ import { VaultSqlGenerator } from '@/utils/dist/shared/vault-sql'; */ const Upgrade: React.FC = () => { const { t } = useTranslation(); - const { username } = useApp(); + const { username, logout } = useApp(); const dbContext = useDb(); const { sqliteClient } = dbContext; const { setHeaderButtons } = useHeaderButtons(); @@ -206,7 +206,7 @@ const Upgrade: React.FC = () => { * Handle the logout. */ const handleLogout = async (): Promise => { - navigate('/logout'); + logout(); }; /** diff --git a/apps/browser-extension/src/entrypoints/popup/pages/settings/Settings.tsx b/apps/browser-extension/src/entrypoints/popup/pages/settings/Settings.tsx index 934a5c785..89ef7b4d0 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/settings/Settings.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/settings/Settings.tsx @@ -109,7 +109,7 @@ const Settings: React.FC = () => { * Handle logout. */ const handleLogout = async () : Promise => { - navigate('/logout', { replace: true }); + app.logout(); }; /**