@@ -166,6 +166,69 @@ export const MainMenuConfig: React.FC<MainMenuConfigProps> = ({
166166 } ) ;
167167 } ;
168168
169+ const handleLogout = async ( ) => {
170+ capture ( 'logout_clicked' ) ;
171+
172+ try {
173+ // Call the logout endpoint and get the logout_url
174+ const response = await fetch ( '/auth/logout' , {
175+ method : 'GET' ,
176+ credentials : 'include'
177+ } ) ;
178+ const data = await response . json ( ) ;
179+ const keycloakLogoutUrl = data . logout_url ;
180+
181+ // Create a function to create an iframe and return a promise that resolves when it loads or times out
182+ const createIframeLoader = ( url : string , debugName : string ) : Promise < void > => {
183+ return new Promise < void > ( ( resolve ) => {
184+ const iframe = document . createElement ( "iframe" ) ;
185+ iframe . style . display = "none" ;
186+ iframe . src = url ;
187+ console . debug ( `[pad.ws] (Silently) Priming ${ debugName } logout for ${ url } ` ) ;
188+
189+ const cleanup = ( ) => {
190+ if ( iframe . parentNode ) iframe . parentNode . removeChild ( iframe ) ;
191+ resolve ( ) ;
192+ } ;
193+
194+ iframe . onload = cleanup ;
195+ // Fallback: remove iframe after 2s if onload doesn't fire
196+ const timeoutId = window . setTimeout ( cleanup , 2000 ) ;
197+
198+ // Also clean up if the iframe errors
199+ iframe . onerror = ( ) => {
200+ clearTimeout ( timeoutId ) ;
201+ cleanup ( ) ;
202+ } ;
203+
204+ // Add the iframe to the DOM
205+ document . body . appendChild ( iframe ) ;
206+ } ) ;
207+ } ;
208+
209+ // Create a promise for Keycloak logout iframe
210+ const promises = [ ] ;
211+
212+ // Add Keycloak logout iframe
213+ promises . push ( createIframeLoader ( keycloakLogoutUrl , "Keycloak" ) ) ;
214+
215+ // Wait for both iframes to complete
216+ await Promise . all ( promises ) ;
217+
218+ // Wait for the iframe to complete
219+ await Promise . all ( promises ) ;
220+
221+ // Invalidate auth query to show the AuthModal
222+ queryClient . invalidateQueries ( { queryKey : [ 'auth' ] } ) ;
223+ queryClient . invalidateQueries ( { queryKey : [ 'userProfile' ] } ) ;
224+
225+ // No need to redirect to the logout URL since we're already handling it via iframe
226+ console . log ( "Logged out successfully" ) ;
227+ } catch ( error ) {
228+ console . error ( "Logout failed:" , error ) ;
229+ }
230+ } ;
231+
169232 return (
170233 < >
171234 { showAccountModal && (
@@ -282,33 +345,7 @@ export const MainMenuConfig: React.FC<MainMenuConfigProps> = ({
282345
283346 < MainMenu . Item
284347 icon = { < LogOut /> }
285- onClick = { async ( ) => {
286- capture ( 'logout_clicked' ) ;
287-
288- try {
289- // Call the logout endpoint and get the session_id
290- const response = await fetch ( '/auth/logout' , {
291- method : 'GET' ,
292- credentials : 'include'
293- } ) ;
294- const data = await response . json ( ) ;
295- const logoutUrl = data . logout_url ;
296-
297- // Clear the session_id cookie client-side
298- document . cookie = "session_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;" ;
299-
300- // Invalidate auth query to show the AuthModal
301- queryClient . invalidateQueries ( { queryKey : [ 'auth' ] } ) ;
302- queryClient . invalidateQueries ( { queryKey : [ 'userProfile' ] } ) ;
303-
304- // Redirect to the logout URL
305- window . location . replace ( logoutUrl ) ;
306-
307- console . log ( "Logged out successfully" ) ;
308- } catch ( error ) {
309- console . error ( "Logout failed:" , error ) ;
310- }
311- } }
348+ onClick = { handleLogout }
312349 >
313350 Logout
314351 </ MainMenu . Item >
0 commit comments