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> [](https://go.dosu.dev/discord-bot) [](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]