GitHub user dosubot[bot] added a comment to the discussion: ### Bug: SSO Logout 
Not Working with Keycloak + Iframe Embedded Superset

<!-- Answer -->
Since your logout function and the Superset iframe are on different pages, you 
can't use direct postMessage communication between them at the moment of 
logout. Instead, you need a way for your logout action to signal to the 
QueryEditor page (where the Superset iframe lives) that it should trigger the 
Superset logout the next time it loads.

A common approach is to use browser storage (like localStorage or 
sessionStorage) as a "flag" to coordinate logout across pages. Here’s how you 
can implement this in your Next.js app:

1. **Set a logout flag in localStorage when logging out:**

In your logout function (anywhere in your app), before redirecting to Keycloak 
logout, set a flag:

```js
const logout = async (): Promise<void> => {
  // Set a flag to indicate Superset should be logged out
  localStorage.setItem('supersetShouldLogout', 'true');
  window.location.href = 
`${process.env.NEXT_PUBLIC_KEYCLOAK_URL}realms/${process.env.NEXT_PUBLIC_KEYCLOAK_REALM}/protocol/openid-connect/logout`;
};
```

2. **Check the flag in your QueryEditor page and trigger Superset logout if 
needed:**

In your `QueryEditor` component, use a useEffect to check for the flag and, if 
present, trigger the Superset logout by navigating the iframe to the logout 
URL. Afterward, clear the flag.

```js
import React, { useEffect, useRef } from 'react';
import { useAuth } from '@/authentication/AuthContext';
import Layout from '@/components/Layout';
import { useRouter } from 'next/router';

const QueryEditor = () => {
  const router = useRouter();
  const iframeRef = useRef(null);
  const { user } = useAuth();
  const isAdmin = 
user?.resource_access?.[`${process.env.NEXT_PUBLIC_KEYCLOAK_CLIENTID}`]?.roles.includes("client_admin");
  const isAuditor = 
user?.resource_access?.[`${process.env.NEXT_PUBLIC_KEYCLOAK_CLIENTID}`]?.roles[0]
 === 'auditor';

  useEffect(() => {
    if (!isAdmin && isAuditor) {
      router.push('/404');
    }
  }, [isAdmin, isAuditor, router]);

  useEffect(() => {
    // Check if we need to log out Superset
    if (localStorage.getItem('supersetShouldLogout') === 'true' && 
iframeRef.current) {
      // Navigate the iframe to the Superset logout URL
      iframeRef.current.src = `${process.env.SUPERSET_BASE_URL}/logout/`;
      // Clear the flag
      localStorage.removeItem('supersetShouldLogout');
    }
  }, []);

  return (
    <Layout>
      <iframe
        ref={iframeRef}
        src={`${process.env.SUPERSET_BASE_URL}`}
        sandbox="allow-same-origin allow-scripts allow-forms allow-popups 
allow-top-navigation"
        allow="autoplay; fullscreen"
        width="1400"
        height="800"
      />
    </Layout>
  );
};

export default QueryEditor;
```

**How this works:**  
When you call your logout function, it sets a flag in localStorage. The next 
time the QueryEditor page loads, it sees the flag and immediately navigates the 
Superset iframe to the `/logout/` endpoint, logging the user out of Superset. 
The flag is then cleared to avoid repeated logouts.

**Important notes:**  
- Make sure the Superset logout URL is correct for your deployment (e.g., 
`/logout/` or `/superset/logout/`).
- This approach works even if the logout and iframe are on different pages, as 
localStorage is shared across tabs/pages of the same origin.
- If your Superset instance is on a different origin, ensure your browser and 
Superset config allow the iframe to load the logout page (see [embedding and 
X-Frame-Options 
considerations](https://github.com/apache/superset/discussions/31442)).
- If you want the logout to happen immediately (without waiting for the user to 
visit the QueryEditor page), you would need to open the Superset logout URL in 
a hidden iframe or popup at logout time, but this can be blocked by browser 
popup blockers and is less reliable.

This method is widely used for coordinating actions across different pages in 
single-page applications.


<!-- Dosu Comment Footer -->
*To reply, just mention my name [@dosu](https://go.dosu.dev/dosubot).*

---
<sup>How did I do? 
[Good](https://app.dosu.dev/response-feedback/8b9ce948-b407-4c42-a5b0-159e1a8ee2c2?feedback_type=great_response)
 | 
[Irrelevant](https://app.dosu.dev/response-feedback/8b9ce948-b407-4c42-a5b0-159e1a8ee2c2?feedback_type=irrelevant_answer)
 | 
[Incorrect](https://app.dosu.dev/response-feedback/8b9ce948-b407-4c42-a5b0-159e1a8ee2c2?feedback_type=incorrect_sources)
 | 
[Verbose](https://app.dosu.dev/response-feedback/8b9ce948-b407-4c42-a5b0-159e1a8ee2c2?feedback_type=too_verbose)
 | 
[Hallucination](https://app.dosu.dev/response-feedback/8b9ce948-b407-4c42-a5b0-159e1a8ee2c2?feedback_type=hallucination)
 | [Report 
🐛](https://app.dosu.dev/response-feedback/8b9ce948-b407-4c42-a5b0-159e1a8ee2c2?feedback_type=bug_report)
 | 
[Other](https://app.dosu.dev/response-feedback/8b9ce948-b407-4c42-a5b0-159e1a8ee2c2?feedback_type=other)</sup>&nbsp;&nbsp;[![Join
 
Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Sh
 are on 
X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/apache/superset/discussions/33685)

GitHub link: 
https://github.com/apache/superset/discussions/33685#discussioncomment-13365134

----
This is an automatically sent email for [email protected].
To unsubscribe, please send an email to: 
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to