You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@superset.apache.org by "rsaleev (via GitHub)" <gi...@apache.org> on 2026/02/27 16:12:40 UTC

[D] localStorage misses last_async_event_id [superset]

GitHub user rsaleev created a discussion: localStorage misses last_async_event_id

I have customized authorization with OAuth and server side session to prevent cookies growing, global async queries are turned on.
Chart page is not loading data while polling, I can see only requests to `/async_event` with empty response. Only after few manual page refreshes data  loads from cache. Main culprit is missing key in storage and I can't understand why, even with turned off Talisman and WTF_CSRF

GitHub link: https://github.com/apache/superset/discussions/38303

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] localStorage misses last_async_event_id [superset]

Posted by "dosubot[bot] (via GitHub)" <gi...@apache.org>.
GitHub user dosubot[bot] edited a comment on the discussion: localStorage misses last_async_event_id

<!-- Answer -->
Now I see what's happening! The logs show the channel ID changing (`605caed7...` → `07902a86...`) between consecutive requests, which confirms the async-token JWT cookie is being **regenerated** on each request.

Your `before_request` code itself isn't the issue - it's what happens in the **after-request handler** ([`validate_session()`](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py#L164-L195)) that's creating new tokens.

The [reset_token condition](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py#L167-L172) triggers when:
```python
reset_token = (
    not request.cookies.get(self._jwt_cookie_name)  # ← THIS IS LIKELY YOUR ISSUE
    or "async_channel_id" not in session
    or "async_user_id" not in session
    or user_id != session["async_user_id"]
)
```

Since `async_channel_id` and `async_user_id` are in your session (you showed that in the logs), the most likely cause is **`request.cookies.get("async-token")` returning None** - meaning the browser isn't sending the cookie back.

**Yes, you can use the session's `async_channel_id` to create a stable token!** Here's how:

Instead of letting the system regenerate, you could modify `validate_session()` or add your own after-request handler that:
1. Checks if `async_channel_id` exists in session
2. Uses that existing channel_id (instead of generating a new UUID) when creating the JWT
3. Ensures the cookie is set with correct attributes

Or simpler - figure out why the async-token cookie isn't being sent back. Check:
- Browser DevTools → Application → Cookies: Is `async-token` present after a response?
- Browser DevTools → Network → Request Headers: Is `Cookie: async-token=...` being sent?
- Cookie attributes: Domain, Path, SameSite, Secure flags matching your setup

The fact that [`parse_channel_id_from_request()`](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py#L197-L206) reads **only from the JWT cookie** (not from session) explains why each poll gets a different channel - each request is getting a freshly minted token with a new UUID.


<!-- Dosu Comment Footer -->
*To reply, just mention [@dosu](https://go.dosu.dev/dosubot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=reply-with-mention&utm_term=apache%2Fsuperset).*

---

Docs are dead. Just use [Dosu](https://dosu.dev?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=static-docs-tagline&utm_term=apache%2Fsuperset).

[![Leave Feedback](https://img.shields.io/badge/Leave%20Feedback-555555?style=flat)](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=static-docs-feedback&utm_term=apache%2Fsuperset&message_id=c79e638f-89f1-48db-aeb2-8ec0a66534bd) [![Ask Dosu about superset](https://img.shields.io/badge/Ask%20Dosu%20about%20superset-2f7b3f?style=flat&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iODYiIGhlaWdodD0iODkiIHZpZXdCb3g9IjAgMCA4NiA4OSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNS4yOTIzNiAxMi43OTI4TDE3Ljc1OTMgNi42ODE4OFY3Mi41NjY3TDUuMjkyMzYgODQuMDYxOFYxMi43OTI4WiIgZmlsbD0iI0I0QkI5MSIvPjxwYXRoIGQ9Ik0xOC4yNTc1IDczLjExOTZMNTkuMTMyOSA3Mi43NDhMNTEuNzAxMSA4Mi40MDk1TDI5LjAzMzggODYuMjkxTDYuMjM5NjIgODUuMTU1NEwxOC4yNTc1IDczLjExOTZaIiBmaWxsPSIjNzc4NTYxIi8%2BPHBhdGggZD0iTTE3LjQ5MTYgMy43MzYzM0wzLjU4NTU3IDEyLjcwOTlWODMuNTc5MkMzLjU4NTU3IDg0Ljc1NDIgNC45ODU2MyA4NS4zNjUyIDUuODQ3MDUg
 ODQuNTY2TDE5LjYyOTYgNzEuNzgwMSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSI2LjQyODQ0IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48bWFzayBpZD0iZG9zdS1kLWN1dG91dCIgZmlsbD0id2hpdGUiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNDAuNzA0IDAuNTE4MDY2SDE3LjA0MzlWNzYuMjIyMUg0MC43MDRINDIuNTgwNUg0Ny44MDEzQzY4LjcwNjQgNzYuMjIyMSA4NS42NTMzIDU5LjI3NTIgODUuNjUzMyAzOC4zNzAxQzg1LjY1MzMgMTcuNDY1IDY4LjcwNjMgMC41MTgwNjYgNDcuODAxMyAwLjUxODA2Nkg0Mi41ODA1SDQwLjcwNFoiLz48L21hc2s%2BPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00MC43MDQgMC41MTgwNjZIMTcuMDQzOVY3Ni4yMjIxSDQwLjcwNEg0Mi41ODA1SDQ3LjgwMTNDNjguNzA2NCA3Ni4yMjIxIDg1LjY1MzMgNTkuMjc1MiA4NS42NTMzIDM4LjM3MDFDODUuNjUzMyAxNy40NjUgNjguNzA2MyAwLjUxODA2NiA0Ny44MDEzIDAuNTE4MDY2SDQyLjU4MDVINDAuNzA0WiIgZmlsbD0iI0YzRjZGMSIvPjxwYXRoIGQ9Ik0xNy4wNDM5IDAuNTE4MDY2Vi02LjU3OTE5SDkuOTQ2NjlWMC41MTgwNjZIMTcuMDQzOVpNMTcuMDQzOSA3Ni4yMjIxSDkuOTQ2NjlWODMuMzE5NEgxNy4wNDM5Vjc2LjIyMjFaTTE3LjA0MzkgNy42MTUzMkg0MC43MDRWLTYuNTc5MTlIMTcuMDQzOVY3LjYxNTMyWk0
 yNC4xNDEyIDc2LjIyMjFWMC41MTgwNjZIOS45NDY2OVY3Ni4yMjIxSDI0LjE0MTJaTTQwLjcwNCA2OS4xMjQ5SDE3LjA0MzlWODMuMzE5NEg0MC43MDRWNjkuMTI0OVpNNDIuNTgwNSA2OS4xMjQ5SDQwLjcwNFY4My4zMTk0SDQyLjU4MDVWNjkuMTI0OVpNNDcuODAxMyA2OS4xMjQ5SDQyLjU4MDVWODMuMzE5NEg0Ny44MDEzVjY5LjEyNDlaTTc4LjU1NiAzOC4zNzAxQzc4LjU1NiA1NS4zNTU1IDY0Ljc4NjcgNjkuMTI0OSA0Ny44MDEzIDY5LjEyNDlWODMuMzE5NEM3Mi42MjYxIDgzLjMxOTQgOTIuNzUwNSA2My4xOTQ5IDkyLjc1MDUgMzguMzcwMUg3OC41NTZaTTQ3LjgwMTMgNy42MTUzMkM2NC43ODY2IDcuNjE1MzIgNzguNTU2IDIxLjM4NDcgNzguNTU2IDM4LjM3MDFIOTIuNzUwNUM5Mi43NTA1IDEzLjU0NTMgNzIuNjI2IC02LjU3OTE5IDQ3LjgwMTMgLTYuNTc5MTlWNy42MTUzMlpNNDIuNTgwNSA3LjYxNTMySDQ3LjgwMTNWLTYuNTc5MTlINDIuNTgwNVY3LjYxNTMyWk00MC43MDQgNy42MTUzMkg0Mi41ODA1Vi02LjU3OTE5SDQwLjcwNFY3LjYxNTMyWiIgZmlsbD0iYmxhY2siIG1hc2s9InVybCgjZG9zdS1kLWN1dG91dCkiLz48cGF0aCBkPSJNNjguOTIxNSAzNi4wMTM1QzY4LjkyMTUgMzYuMDEzNSA2NS43MzY5IDQ5LjQ3MzggNTEuNDIzMSA0OS40NzM4QzM3LjEwOTMgNDkuNDczOCAzMi41Nzg3IDM3LjM1OTYgMzIuNTc4NyAzNi4wMTM1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjcuNjkxNjEi
 IHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0wLjM0ODYzMyA4NS40OTQ2QzAuMzQ4NjMzIDg1LjQ5NDYgMjkuNDg1NiA4NS44MzA5IDM0LjgwOSA4NS42OThDNDQuODMzNyA4NS40NDc3IDUxLjI4NzIgODQuNDAyIDU3LjUyNjkgNzguOTcyNEM2Mi44MTI5IDc0LjM3MjcgNzUuMTM0MiA1OS42ODM2IDc1LjEzNDIgNTkuNjgzNiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSI2LjE2NDgyIi8%2BPC9zdmc%2B)](https://github.dosu.com/apache/superset?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=static-docs-ask-repo&utm_term=apache%2Fsuperset) [![Share Dosu with your team](https://img.shields.io/badge/Share%20Dosu%20with%20your%20team-1f6feb?style=flat)](https://app.dosu.dev/signup?referrer=openSource&source=github-footer&utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=static-docs-share-team&utm_term=apache%2Fsuperset)

GitHub link: https://github.com/apache/superset/discussions/38303#discussioncomment-15954736

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] localStorage misses last_async_event_id [superset]

Posted by "dosubot[bot] (via GitHub)" <gi...@apache.org>.
GitHub user dosubot[bot] added a comment to the discussion: localStorage misses last_async_event_id

<!-- Answer -->
The issue is likely related to how your custom OAuth with server-side sessions interacts with Superset's async event system. Here's what's happening:

**Root Cause:** The `last_async_event_id` in localStorage is only set *after* the first async event is received from the server - it's not initialized on page load. Empty `/async_event` responses mean events aren't being written to Redis at all, not a localStorage problem.

**Most Likely Culprit - Session State:**
The [async-token JWT cookie is created by an after-request handler](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py#L162-L195) that requires Flask session persistence of `async_channel_id` and `async_user_id`. If your custom OAuth clears or resets sessions, these keys get removed and a new token gets generated on every request, causing channel mismatches.

The handler [regenerates the token when these conditions are true](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py#L167-L171):
- No async-token cookie exists
- Session lacks `async_channel_id`
- Session lacks `async_user_id`  
- Current user_id doesn't match session's `async_user_id`

**Debugging Steps:**
1. Check browser DevTools → Application → Cookies for `async-token` cookie
2. Verify the JWT secret is at least 32 bytes per [AsyncQueryManager requirements](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py#L134-L137)
3. Monitor Redis for keys like `async-events-{uuid}` being created
4. Check Celery logs for async job submissions and completions
5. Verify your OAuth implementation preserves Flask session between requests (specifically the `async_channel_id` and `async_user_id` keys)

**Cookie Configuration Check:**
Ensure your cookie settings match your deployment (HTTPS vs HTTP, domain, SameSite). This is a [known silent failure mode](https://github.com/apache/superset/issues/37753) - browsers silently drop cookies with mismatched attributes.

Data loading after manual refreshes suggests the cache is being populated by Celery, but the event notification channel is broken - pointing strongly to the JWT/session mismatch issue.


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

---
<sup>How did I do? [Good](https://app.dosu.dev/response-feedback/6be9ef11-9fe1-476e-8fe3-3b132a14f904?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/6be9ef11-9fe1-476e-8fe3-3b132a14f904?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/6be9ef11-9fe1-476e-8fe3-3b132a14f904?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/6be9ef11-9fe1-476e-8fe3-3b132a14f904?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/6be9ef11-9fe1-476e-8fe3-3b132a14f904?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/6be9ef11-9fe1-476e-8fe3-3b132a14f904?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/6be9ef11-9fe1-476e-8fe3-3b132a14f904?feedback_type=other)</sup>

[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/a28d3c7e-a9d3-459e-9fb6-3a6f9ff4f357/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=The%20issue%20is%20likely%20related%20to%20how%20your%20custom%20OAuth%20with%20server-side%20sessions%20interacts%20with%20Superset%27s%20async%20event%20system.%20Here%27s%20what%27s%20happening%3A%0A%0A%2A%2ARoot%20Cause%3A%2A%2A%20The%20%60last_async_event_id%60%20in%20localStorage%20is%20only%20set%20%2Aafter%2A%20the%20first%20async%20event%20is%20received%20from%20the%20server%20-%20it%27s%20not%20initialized%20on%20page%20load.%20Empty%20%60/async_event%60%20responses%20mean%20events%20aren%27t%20being%20written%20to%20Redis%20at%20all%2C%20not%20a%20localStorage%20problem.%0A%0A%2A%2AMost%20Likely%20Culprit%20-%20Session%20State%3A%2A%2A%0AThe%20%5Basync-token%20JWT%20cookie%20is%20created%20by%20an%20after-request%20handler%5D%28https%3A//gi
 thub.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py%23L162-L195%29%20that%20requires%20Flask%20session%20persistence%20of%20%60async_channel_id%60%20and%20%60async_user_id%60.%20If%20your%20custom%20OAuth%20clears%20or%20resets%20sessions%2C%20these%20keys%20get%20removed%20and%20a%20new%20token%20gets%20generated%20on%20every%20request%2C%20causing%20channel%20mismatches.%0A%0AThe%20handler%20%5Bregenerates%20the%20token%20when%20these%20conditions%20are%20true%5D%28https%3A//github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py%23L167-L171%29%3A%0A-%20No%20async-token%20cookie%20exists%0A-%20Session%20lacks%20%60async_channel_id%60%0A-%20Session%20lacks%20%60async_user_id%60%20%20%0A-%20Current%20user_id%20doesn%27t%20match%20session%27s%20%60async_user_id%60%0A%0A%2A%2ADebugging%20Steps%3A%2A%2A%0A1.%20Check%20browser%20DevTools%20%E2%86%92%20Application
 %20%E2%86%92%20Cookies%20for%20%60async-token%60%20cookie%0A2.%20Verify%20the%20JWT%20secret%20is%20at%20least%2032%20bytes%20per%20%5BAsyncQueryManager%20requirements%5D%28https%3A//github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py%23L134-L137%29%0A3.%20Monitor%20Redis%20for%20keys%20like%20%60async-events-%7Buuid%7D%60%20being%20created%0A4.%20Check%20Celery%20logs%20for%20async%20job%20submissions%20and%20completions%0A5.%20Verify%20your%20OAuth%20implementation%20preserves%20Flask%20session%20between%20requests%20%28specifically%20the%20%60async_channel_id%60%20and%20%60async_user_id%60%20keys%29%0A%0A%2A%2ACookie%20Configuration%20Check%3A%2A%2A%0AEnsure%20your%20cookie%20settings%20match%20your%20deployment%20%28HTTPS%20vs%20HTTP%2C%20domain%2C%20SameSite%29.%20This%20is%20a%20%5Bknown%20silent%20failure%20mode%5D%28https%3A//github.com/apache/superset/issues/37753%29%20-%20browsers%20silently%20drop%20cookies
 %20with%20mismatched%20attributes.%0A%0AData%20loading%20after%20manual%20refreshes%20suggests%20the%20cache%20is%20being%20populated%20by%20Celery%2C%20but%20the%20event%20notification%20channel%20is%20broken%20-%20pointing%20strongly%20to%20the%20JWT/session%20mismatch%20issue.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share 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/38303)

GitHub link: https://github.com/apache/superset/discussions/38303#discussioncomment-15948699

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] localStorage misses last_async_event_id [superset]

Posted by "rsaleev (via GitHub)" <gi...@apache.org>.
GitHub user rsaleev edited a comment on the discussion: localStorage misses last_async_event_id

@dosu 

```
def auth_user_logged_in(self):
        if hasattr(g, "user") and g.user and g.user.is_authenticated:
            return True
        # 2. Check current_user (Flask-Login)
        try:
            if current_user.is_authenticated:
                g.user = current_user
                log.debug(
                    f"✅ [auth_user_logged_in] Set g.user from current_user: {g.user.username}"
                )
                return True
        except Exception as e:
            log.debug(f"[auth_user_logged_in] current_user check failed: {e}")
        # 3. ✅ CRITICAL: If session has _user_id, LOAD THE USER FROM DB
        user_id = session.get("_user_id")
        if user_id:
            try:
                from flask_appbuilder.security.sqla.models import User

                from superset import db

                user = db.session.get(User, int(user_id))
                if user:
                    g.user = user  # ✅ FORCE SET g.user HERE
                    log.debug(
                        f"✅ [auth_user_logged_in] Loaded user from session ID: {user.username}"
                    )
                    login_user(user, force=False, duration=PERMANENT_SESSION_LIFETIME)
                    return True
                else:
                    log.warning(f"User ID {user_id} in session but not found in DB")
                    session.pop("_user_id", None)  # Clean up bad session
            except Exception as e:
                log.error(f"[auth_user_logged_in] Error loading user {user_id}: {e}")
                raise
        return False

    def before_request(self):
        log.debug(f"Session: {session.items()}")
        logged_in = self.auth_user_logged_in()
        redirect_count = session.get("auth_redirect_count", 0)
        if redirect_count > 3:
            log.error("🚨 Auth redirect loop detected, clearing session")
            session.clear()
            return jsonify({"error": "Authentication loop detected"}), 401

        if request.endpoint in [
            "KeycloakOAuthView.login",
            "KeycloakOAuthView.oauth_authorized",
            "KeycloakOAuthView.logout",
        ]:
            log.debug(f"🟢 Allowing auth endpoint: {request.endpoint}")
            return None

        if any(request.path.startswith(path) for path in self.PUBLIC_PATHS):
            log.debug(f"🟢 Public path allowed: {request.path}")
            return None

        if request.endpoint and any(
            request.endpoint.startswith(ep) for ep in self.PUBLIC_ENDPOINTS
        ):
            log.debug(f"🟢 Public endpoint allowed: {request.endpoint}")
            return None

        if request.path.startswith("/api/"):
            if not logged_in:
                log.warning(f"🔐 Unauthenticated API access: {request.endpoint}")
                return jsonify({"error": "Authentication required"}), 401

        if not logged_in:
            log.warning(f"🔐 Unauthenticated access to {request.endpoint}")
            return redirect(
                url_for("KeycloakOAuthView.login", provider=DEFAULT_PROVIDER)
            )
        session.pop("auth_redirect_count", None)
        # Log successful auth for debugging
        username = g.user.username if hasattr(g, "user") and g.user else "unknown"
        log.debug(f"✅ User {username} authenticated for {request.endpoint}")

        return super().before_request()
 ```
 I suppose it triggers channel id refresh, but I don't get it why.
```
2026-02-28 12:39:04,321:DEBUG:customization.oauth_config:✅ [auth_user_logged_in] Set g.user from current_user: test_user

2026-02-28 12:39:04,321:DEBUG:customization.oauth_config:✅ User test_user authenticated for AsyncEventsRestApi.events

2026-02-28 12:39:04,336:DEBUG:superset.async_events.async_query_manager:Parsed JWT token channel: 605caed7-e8e7-4a8a-9fdc-eb45783aa436
```
```
2026-02-28 12:39:04,931:DEBUG:customization.oauth_config:✅ [auth_user_logged_in] Set g.user from current_user: test_user

2026-02-28 12:39:04,931:DEBUG:customization.oauth_config:✅ User test_user authenticated for AsyncEventsRestApi.events

2026-02-28 12:39:04,953:DEBUG:superset.async_events.async_query_manager:Parsed JWT token channel: 07902a86-3752-4e43-a06a-0c837a91ae96
```

But channel ID leaves refreshes in session, maybe I can use it to create proper async-token?



GitHub link: https://github.com/apache/superset/discussions/38303#discussioncomment-15954728

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] localStorage misses last_async_event_id [superset]

Posted by "dosubot[bot] (via GitHub)" <gi...@apache.org>.
GitHub user dosubot[bot] added a comment to the discussion: localStorage misses last_async_event_id

<!-- Answer -->
I see the issue clearly now. Your logs show the **`async_channel_id` is changing between requests** - from `3dba366b-a70c-4ab8-9b86-7aaeb4159775` to `7e6ba64b-d2e7-4d8c-aff9-58e6ab88bd9f`. This is the core problem.

**Why `last_async_event_id` is never set:** The frontend [only calls `setLastId()` when it receives at least one event](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset-frontend/src/middleware/asyncEvent.ts#L169-L183). Since `/async_event` returns empty `[]` (no events exist on the new channel), `processEvents()` is never called and localStorage never gets updated.

**Why events are empty:** Each time `async_channel_id` changes, you're polling a *brand new* Redis stream that has no events. The Celery workers wrote events to the old channel (`3dba366b...`), but your poll is hitting the new one (`7e6ba64b...`).

**The "Token reset" you're logging** happens because the [after-request handler detects a mismatch](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py#L167-L195) between the async-token cookie and session state. Looking at your logs:
- The async-token cookie likely contains the old channel_id from a previous request
- But the session (which you reworked to read from the token) shows the new channel_id
- This mismatch triggers regeneration

**The issue with your workaround:** You said "channel comes from token, because request doesn't have it at all". If the async-token cookie isn't being sent with `/async_event` requests, that's likely a **cookie domain or SameSite issue**. Check your browser DevTools → Network tab → click on the `/async_event` request → check if `Cookie: async-token=...` is in the Request Headers.

**To fix this:**
1. Ensure `GLOBAL_ASYNC_QUERIES_JWT_COOKIE_SAMESITE` is set to `"Lax"` or `"None"` (with `Secure=True` if `None`)
2. Ensure `GLOBAL_ASYNC_QUERIES_JWT_COOKIE_DOMAIN` matches your domain exactly
3. If using HTTP (not HTTPS), set `GLOBAL_ASYNC_QUERIES_JWT_COOKIE_SECURE = False`
4. Make sure your OAuth flow doesn't clear/recreate the Flask session after the async-token cookie is already set

The key is keeping the `async_channel_id` stable across all requests during a session - otherwise events get written to one channel but polled from another.


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

---
<sup>How did I do? [Good](https://app.dosu.dev/response-feedback/e67d6729-50f8-4e5c-8115-813e1aa94344?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/e67d6729-50f8-4e5c-8115-813e1aa94344?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/e67d6729-50f8-4e5c-8115-813e1aa94344?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/e67d6729-50f8-4e5c-8115-813e1aa94344?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/e67d6729-50f8-4e5c-8115-813e1aa94344?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/e67d6729-50f8-4e5c-8115-813e1aa94344?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/e67d6729-50f8-4e5c-8115-813e1aa94344?feedback_type=other)</sup>

[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/a28d3c7e-a9d3-459e-9fb6-3a6f9ff4f357/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=I%20see%20the%20issue%20clearly%20now.%20Your%20logs%20show%20the%20%2A%2A%60async_channel_id%60%20is%20changing%20between%20requests%2A%2A%20-%20from%20%603dba366b-a70c-4ab8-9b86-7aaeb4159775%60%20to%20%607e6ba64b-d2e7-4d8c-aff9-58e6ab88bd9f%60.%20This%20is%20the%20core%20problem.%0A%0A%2A%2AWhy%20%60last_async_event_id%60%20is%20never%20set%3A%2A%2A%20The%20frontend%20%5Bonly%20calls%20%60setLastId%28%29%60%20when%20it%20receives%20at%20least%20one%20event%5D%28https%3A//github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset-frontend/src/middleware/asyncEvent.ts%23L169-L183%29.%20Since%20%60/async_event%60%20returns%20empty%20%60%5B%5D%60%20%28no%20events%20exist%20on%20the%20new%20channel%29%2C%20%60processEvents%28%29%60
 %20is%20never%20called%20and%20localStorage%20never%20gets%20updated.%0A%0A%2A%2AWhy%20events%20are%20empty%3A%2A%2A%20Each%20time%20%60async_channel_id%60%20changes%2C%20you%27re%20polling%20a%20%2Abrand%20new%2A%20Redis%20stream%20that%20has%20no%20events.%20The%20Celery%20workers%20wrote%20events%20to%20the%20old%20channel%20%28%603dba366b...%60%29%2C%20but%20your%20poll%20is%20hitting%20the%20new%20one%20%28%607e6ba64b...%60%29.%0A%0A%2A%2AThe%20%22Token%20reset%22%20you%27re%20logging%2A%2A%20happens%20because%20the%20%5Bafter-request%20handler%20detects%20a%20mismatch%5D%28https%3A//github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py%23L167-L195%29%20between%20the%20async-token%20cookie%20and%20session%20state.%20Looking%20at%20your%20logs%3A%0A-%20The%20async-token%20cookie%20likely%20contains%20the%20old%20channel_id%20from%20a%20previous%20request%0A-%20But%20the%20session%20%28which%20you%20reworked%20to%20r
 ead%20from%20the%20token%29%20shows%20the%20new%20channel_id%0A-%20This%20mismatch%20triggers%20regeneration%0A%0A%2A%2AThe%20issue%20with%20your%20workaround%3A%2A%2A%20You%20said%20%22channel%20comes%20from%20token%2C%20because%20request%20doesn%27t%20have%20it%20at%20all%22.%20If%20the%20async-token%20cookie%20isn%27t%20being%20sent%20with%20%60/async_event%60%20requests%2C%20that%27s%20likely%20a%20%2A%2Acookie%20domain%20or%20SameSite%20issue%2A%2A.%20Check%20your%20browser%20DevTools%20%E2%86%92%20Network%20tab%20%E2%86%92%20click%20on%20the%20%60/async_event%60%20request%20%E2%86%92%20check%20if%20%60Cookie%3A%20async-token%3D...%60%20is%20in%20the%20Request%20Headers.%0A%0A%2A%2ATo%20fix%20this%3A%2A%2A%0A1.%20Ensure%20%60GLOBAL_ASYNC_QUERIES_JWT_COOKIE_SAMESITE%60%20is%20set%20to%20%60%22Lax%22%60%20or%20%60%22None%22%60%20%28with%20%60Secure%3DTrue%60%20if%20%60None%60%29%0A2.%20Ensure%20%60GLOBAL_ASYNC_QUERIES_JWT_COOKIE_DOMAIN%60%20matches%20your%20domain%20exactly%0A3.%
 20If%20using%20HTTP%20%28not%20HTTPS%29%2C%20set%20%60GLOBAL_ASYNC_QUERIES_JWT_COOKIE_SECURE%20%3D%20False%60%0A4.%20Make%20sure%20your%20OAuth%20flow%20doesn%27t%20clear/recreate%20the%20Flask%20session%20after%20the%20async-token%20cookie%20is%20already%20set%0A%0AThe%20key%20is%20keeping%20the%20%60async_channel_id%60%20stable%20across%20all%20requests%20during%20a%20session%20-%20otherwise%20events%20get%20written%20to%20one%20channel%20but%20polled%20from%20another.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share 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/38303)

GitHub link: https://github.com/apache/superset/discussions/38303#discussioncomment-15954693

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] localStorage misses last_async_event_id [superset]

Posted by "rsaleev (via GitHub)" <gi...@apache.org>.
GitHub user rsaleev edited a comment on the discussion: localStorage misses last_async_event_id

@dosu 

```
def auth_user_logged_in(self):
        if hasattr(g, "user") and g.user and g.user.is_authenticated:
            return True
        # 2. Check current_user (Flask-Login)
        try:
            if current_user.is_authenticated:
                g.user = current_user
                log.debug(
                    f"✅ [auth_user_logged_in] Set g.user from current_user: {g.user.username}"
                )
                return True
        except Exception as e:
            log.debug(f"[auth_user_logged_in] current_user check failed: {e}")
        # 3. ✅ CRITICAL: If session has _user_id, LOAD THE USER FROM DB
        user_id = session.get("_user_id")
        if user_id:
            try:
                from flask_appbuilder.security.sqla.models import User

                from superset import db

                user = db.session.get(User, int(user_id))
                if user:
                    g.user = user  # ✅ FORCE SET g.user HERE
                    log.debug(
                        f"✅ [auth_user_logged_in] Loaded user from session ID: {user.username}"
                    )
                    login_user(user, force=False, duration=PERMANENT_SESSION_LIFETIME)
                    return True
                else:
                    log.warning(f"User ID {user_id} in session but not found in DB")
                    session.pop("_user_id", None)  # Clean up bad session
            except Exception as e:
                log.error(f"[auth_user_logged_in] Error loading user {user_id}: {e}")
                raise
        return False

    def before_request(self):
        log.debug(f"Session: {session.items()}")
        logged_in = self.auth_user_logged_in()
        redirect_count = session.get("auth_redirect_count", 0)
        if redirect_count > 3:
            log.error("🚨 Auth redirect loop detected, clearing session")
            session.clear()
            return jsonify({"error": "Authentication loop detected"}), 401

        if request.endpoint in [
            "KeycloakOAuthView.login",
            "KeycloakOAuthView.oauth_authorized",
            "KeycloakOAuthView.logout",
        ]:
            log.debug(f"🟢 Allowing auth endpoint: {request.endpoint}")
            return None

        if any(request.path.startswith(path) for path in self.PUBLIC_PATHS):
            log.debug(f"🟢 Public path allowed: {request.path}")
            return None

        if request.endpoint and any(
            request.endpoint.startswith(ep) for ep in self.PUBLIC_ENDPOINTS
        ):
            log.debug(f"🟢 Public endpoint allowed: {request.endpoint}")
            return None

        if request.path.startswith("/api/"):
            if not logged_in:
                log.warning(f"🔐 Unauthenticated API access: {request.endpoint}")
                return jsonify({"error": "Authentication required"}), 401

        if not logged_in:
            log.warning(f"🔐 Unauthenticated access to {request.endpoint}")
            return redirect(
                url_for("KeycloakOAuthView.login", provider=DEFAULT_PROVIDER)
            )
        session.pop("auth_redirect_count", None)
        # Log successful auth for debugging
        username = g.user.username if hasattr(g, "user") and g.user else "unknown"
        log.debug(f"✅ User {username} authenticated for {request.endpoint}")

        return super().before_request()
 ```
 I suppose it triggers channel id refresh, but I don't get it why.
```
2026-02-28 12:39:04,321:DEBUG:customization.oauth_config:✅ [auth_user_logged_in] Set g.user from current_user: test_user

2026-02-28 12:39:04,321:DEBUG:customization.oauth_config:✅ User test_user authenticated for AsyncEventsRestApi.events

2026-02-28 12:39:04,336:DEBUG:superset.async_events.async_query_manager:Parsed JWT token channel: 605caed7-e8e7-4a8a-9fdc-eb45783aa436
```
```
2026-02-28 12:39:04,931:DEBUG:customization.oauth_config:✅ [auth_user_logged_in] Set g.user from current_user: test_user

2026-02-28 12:39:04,931:DEBUG:customization.oauth_config:✅ User test_user authenticated for AsyncEventsRestApi.events

2026-02-28 12:39:04,953:DEBUG:superset.async_events.async_query_manager:Parsed JWT token channel: 07902a86-3752-4e43-a06a-0c837a91ae96
```

But channel ID outlives refreshes in session, maybe I can use it to create proper async-token?



GitHub link: https://github.com/apache/superset/discussions/38303#discussioncomment-15954728

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] localStorage misses last_async_event_id [superset]

Posted by "rsaleev (via GitHub)" <gi...@apache.org>.
GitHub user rsaleev added a comment to the discussion: localStorage misses last_async_event_id

@dosu
```
2026-02-28 12:19:39,945:DEBUG:customization.oauth_config:Session: dict_items([('_permanent', True), ('async_channel_id', '3dba366b-a70c-4ab8-9b86-7aaeb4159775'), ('async_user_id', '3'), ('_fresh', True), ('oauth', ['eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIydGstck1ydlp5ZXZyX2NCdXRTLUlYeU5ZTzc4M09WVl9Ybm1nNDhqVU9nIn0.eyJleHAiOjE3NzIyOTkxMjMsImlhdCI6MTc3MjI4MTEyMywiYXV0aF90aW1lIjoxNzcyMjgxMTIzLCJqdGkiOiI1YTJjZDhmYS1mODhlLTQzOGItYjYyMy0xOGY5YmE1ZjM2NjYiLCJpc3MiOiJodHRwOi8vMTAuMTI4LjAuMzIvYXV0aC9yZWFsbXMvbGMiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMDBmMzhkYzgtMjRjZS00MzJlLTliZDAtOWMyM2ZlOTYyZDgxIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoibGNfY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjcyZjU3NDIxLTNjMjQtNDQzMS1iODQ5LWRhNDg4OTljYjE1MyIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1sYyIsIm9mZmxpbmVfYWNjZXNzIiwiU1VQRVJfVVNFUiIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIi
 widmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBsY19jbGllbnRfc2NvcGUgcHJvZmlsZSIsInNpZCI6IjcyZjU3NDIxLTNjMjQtNDQzMS1iODQ5LWRhNDg4OTljYjE1MyIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6IlNlcmdleSBTaWJhZGEiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0X3VzZXIiLCJsb2NhbGUiOiJydSIsImdpdmVuX25hbWUiOiJTZXJnZXkiLCJncm91cHNfaWRzIjpbInNndGVzdF9ncm91cCJdLCJmYW1pbHlfbmFtZSI6IlNpYmFkYSIsImVtYWlsIjoicy5zaWJhZGFAc2liYWR1LmNvbSJ9.lEuPtjPbfXY7Ly8jcV6Rb-DZASdTXBV7dP1BbEcHnfT0G8QbPUTtLsV-OxUJOdwlLT4jGF_0Jsg5qCZlVZaOXV8lzRW1AjpUhF4jnJenqKs1wloz0Kg2tMBvji1sK1_0BShrnkAOITsLL-GAUuiJo_2D2nvYG9ksu7qbMyvkGl58i0ctx5IrPC88-nW_-ifjUaatfJ2aYOCd5W4G_tljFwXY11BxXUCfU-Y7FgIjRooBqFCwm9l0yi9df_L1UBt9zBLUKvR8AbxdR2MWktPALwwaUoBuydI5kEP7xuEg11TBbBirrU2nlWR__F8r2OmV1ajDlCt5_jB0k92rLNwc0A', '']), ('oauth_provider', 'keycloak'), ('_user_id', '3'), ('_id', 'b9ab31700f28165c180e10a38d1863279aad9d11957eaee5c8ea6f75eef6edbb74788aa87ea89abbb491a18340aa14fb296eaaeb60c0caab548d7e3f1e9af1bf'), ('locale', 'ru')])

2026-02-28 12:19:39,952:DEBUG:customization.oauth_config:✅ [auth_user_logged_in] Set g.user from current_user: test_user

2026-02-28 12:19:39,952:DEBUG:customization.oauth_config:✅ User test_user authenticated for AsyncEventsRestApi.events

2026-02-28 12:19:39,965:DEBUG:superset.async_events.async_query_manager:🔍 Channel from session (authenticated): 3dba366b-a70c-4ab8-9b86-7aaeb4159775
```
I have reworked it, channel comes from token, because request doesn't have it at all

Now async_event request 

```
2026-02-28 12:19:39,981:DEBUG:superset.async_events.async_query_manager:Token reset

2026-02-28 12:19:39,981:DEBUG:superset.async_events.async_query_manager:Session: dict_items([('_permanent', True), ('async_channel_id', '7e6ba64b-d2e7-4d8c-aff9-58e6ab88bd9f'), ('async_user_id', '3'), ('_fresh', True), ('oauth', ['eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIydGstck1ydlp5ZXZyX2NCdXRTLUlYeU5ZTzc4M09WVl9Ybm1nNDhqVU9nIn0.eyJleHAiOjE3NzIyOTkxMjMsImlhdCI6MTc3MjI4MTEyMywiYXV0aF90aW1lIjoxNzcyMjgxMTIzLCJqdGkiOiI1YTJjZDhmYS1mODhlLTQzOGItYjYyMy0xOGY5YmE1ZjM2NjYiLCJpc3MiOiJodHRwOi8vMTAuMTI4LjAuMzIvYXV0aC9yZWFsbXMvbGMiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMDBmMzhkYzgtMjRjZS00MzJlLTliZDAtOWMyM2ZlOTYyZDgxIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoibGNfY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjcyZjU3NDIxLTNjMjQtNDQzMS1iODQ5LWRhNDg4OTljYjE1MyIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1sYyIsIm9mZmxpbmVfYWNjZXNzIiwiU1VQRVJfVVNFUiIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2N
 vdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBsY19jbGllbnRfc2NvcGUgcHJvZmlsZSIsInNpZCI6IjcyZjU3NDIxLTNjMjQtNDQzMS1iODQ5LWRhNDg4OTljYjE1MyIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6IlNlcmdleSBTaWJhZGEiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0X3VzZXIiLCJsb2NhbGUiOiJydSIsImdpdmVuX25hbWUiOiJTZXJnZXkiLCJncm91cHNfaWRzIjpbInNndGVzdF9ncm91cCJdLCJmYW1pbHlfbmFtZSI6IlNpYmFkYSIsImVtYWlsIjoicy5zaWJhZGFAc2liYWR1LmNvbSJ9.lEuPtjPbfXY7Ly8jcV6Rb-DZASdTXBV7dP1BbEcHnfT0G8QbPUTtLsV-OxUJOdwlLT4jGF_0Jsg5qCZlVZaOXV8lzRW1AjpUhF4jnJenqKs1wloz0Kg2tMBvji1sK1_0BShrnkAOITsLL-GAUuiJo_2D2nvYG9ksu7qbMyvkGl58i0ctx5IrPC88-nW_-ifjUaatfJ2aYOCd5W4G_tljFwXY11BxXUCfU-Y7FgIjRooBqFCwm9l0yi9df_L1UBt9zBLUKvR8AbxdR2MWktPALwwaUoBuydI5kEP7xuEg11TBbBirrU2nlWR__F8r2OmV1ajDlCt5_jB0k92rLNwc0A', '']), ('oauth_provider', 'keycloak'), ('_user_id', '3'), ('_id', 'b9ab31700f28165c180e10a38d1863279aad9d11957eaee5c8ea6f75eef6edbb74788aa87ea89abbb491a18340aa14fb296eaaeb60c0caab548d7e3f1e9af1bf'), ('locale', 'ru')])

172.17.0.1 - - [28/Feb/2026:12:19:39 +0000] "GET /api/v1/async_event/ HTTP/1.1" 200 19 
```
everything in session, async-token presents but no key in storage


GitHub link: https://github.com/apache/superset/discussions/38303#discussioncomment-15954659

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] localStorage misses last_async_event_id [superset]

Posted by "dosubot[bot] (via GitHub)" <gi...@apache.org>.
GitHub user dosubot[bot] edited a comment on the discussion: localStorage misses last_async_event_id

<!-- Answer -->
The issue is likely related to how your custom OAuth with server-side sessions interacts with Superset's async event system. Here's what's happening:

**Root Cause:** The `last_async_event_id` in localStorage is only set *after* the first async event is received from the server - it's not initialized on page load. Empty `/async_event` responses mean events aren't being written to Redis at all, not a localStorage problem.

**Most Likely Culprit - Session State:**
The [async-token JWT cookie is created by an after-request handler](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py#L162-L195) that requires Flask session persistence of `async_channel_id` and `async_user_id`. If your custom OAuth clears or resets sessions, these keys get removed and a new token gets generated on every request, causing channel mismatches.

The handler [regenerates the token when these conditions are true](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py#L167-L171):
- No async-token cookie exists
- Session lacks `async_channel_id`
- Session lacks `async_user_id`  
- Current user_id doesn't match session's `async_user_id`

**Debugging Steps:**
1. Check browser DevTools → Application → Cookies for `async-token` cookie
2. Verify the JWT secret is at least 32 bytes per [AsyncQueryManager requirements](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py#L134-L137)
3. Monitor Redis for keys like `async-events-{uuid}` being created
4. Check Celery logs for async job submissions and completions
5. Verify your OAuth implementation preserves Flask session between requests (specifically the `async_channel_id` and `async_user_id` keys)

**Cookie Configuration Check:**
Ensure your cookie settings match your deployment (HTTPS vs HTTP, domain, SameSite). This is a [known silent failure mode](https://github.com/apache/superset/issues/37753) - browsers silently drop cookies with mismatched attributes.

Data loading after manual refreshes suggests the cache is being populated by Celery, but the event notification channel is broken - pointing strongly to the JWT/session mismatch issue.


<!-- Dosu Comment Footer -->
*To reply, just mention [@dosu](https://go.dosu.dev/dosubot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=reply-with-mention&utm_term=apache%2Fsuperset).*

---

Share context across your team and agents. Try [Dosu](https://dosu.dev?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=knowledge-infrastructure-tagline&utm_term=apache%2Fsuperset).

[![Leave Feedback](https://img.shields.io/badge/Leave%20Feedback-555555?style=flat)](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=knowledge-infrastructure-feedback&utm_term=apache%2Fsuperset&message_id=6be9ef11-9fe1-476e-8fe3-3b132a14f904) [![Learn about superset with Dosu](https://img.shields.io/badge/Learn%20about%20superset%20with%20Dosu-2f7b3f?style=flat&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iODYiIGhlaWdodD0iODkiIHZpZXdCb3g9IjAgMCA4NiA4OSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNS4yOTIzNiAxMi43OTI4TDE3Ljc1OTMgNi42ODE4OFY3Mi41NjY3TDUuMjkyMzYgODQuMDYxOFYxMi43OTI4WiIgZmlsbD0iI0I0QkI5MSIvPjxwYXRoIGQ9Ik0xOC4yNTc1IDczLjExOTZMNTkuMTMyOSA3Mi43NDhMNTEuNzAxMSA4Mi40MDk1TDI5LjAzMzggODYuMjkxTDYuMjM5NjIgODUuMTU1NEwxOC4yNTc1IDczLjExOTZaIiBmaWxsPSIjNzc4NTYxIi8%2BPHBhdGggZD0iTTE3LjQ5MTYgMy43MzYzM0wzLjU4NTU3IDEyLjcwOTlWODMuNTc5MkMzLjU4NTU3IDg0Ljc1NDIgNC4
 5ODU2MyA4NS4zNjUyIDUuODQ3MDUgODQuNTY2TDE5LjYyOTYgNzEuNzgwMSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSI2LjQyODQ0IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48bWFzayBpZD0iZG9zdS1kLWN1dG91dCIgZmlsbD0id2hpdGUiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNDAuNzA0IDAuNTE4MDY2SDE3LjA0MzlWNzYuMjIyMUg0MC43MDRINDIuNTgwNUg0Ny44MDEzQzY4LjcwNjQgNzYuMjIyMSA4NS42NTMzIDU5LjI3NTIgODUuNjUzMyAzOC4zNzAxQzg1LjY1MzMgMTcuNDY1IDY4LjcwNjMgMC41MTgwNjYgNDcuODAxMyAwLjUxODA2Nkg0Mi41ODA1SDQwLjcwNFoiLz48L21hc2s%2BPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00MC43MDQgMC41MTgwNjZIMTcuMDQzOVY3Ni4yMjIxSDQwLjcwNEg0Mi41ODA1SDQ3LjgwMTNDNjguNzA2NCA3Ni4yMjIxIDg1LjY1MzMgNTkuMjc1MiA4NS42NTMzIDM4LjM3MDFDODUuNjUzMyAxNy40NjUgNjguNzA2MyAwLjUxODA2NiA0Ny44MDEzIDAuNTE4MDY2SDQyLjU4MDVINDAuNzA0WiIgZmlsbD0iI0YzRjZGMSIvPjxwYXRoIGQ9Ik0xNy4wNDM5IDAuNTE4MDY2Vi02LjU3OTE5SDkuOTQ2NjlWMC41MTgwNjZIMTcuMDQzOVpNMTcuMDQzOSA3Ni4yMjIxSDkuOTQ2NjlWODMuMzE5NEgxNy4wNDM5Vjc2LjIyMjFaTTE3LjA0MzkgNy42MTUzMkg0MC43MDRWLTYuNT
 c5MTlIMTcuMDQzOVY3LjYxNTMyWk0yNC4xNDEyIDc2LjIyMjFWMC41MTgwNjZIOS45NDY2OVY3Ni4yMjIxSDI0LjE0MTJaTTQwLjcwNCA2OS4xMjQ5SDE3LjA0MzlWODMuMzE5NEg0MC43MDRWNjkuMTI0OVpNNDIuNTgwNSA2OS4xMjQ5SDQwLjcwNFY4My4zMTk0SDQyLjU4MDVWNjkuMTI0OVpNNDcuODAxMyA2OS4xMjQ5SDQyLjU4MDVWODMuMzE5NEg0Ny44MDEzVjY5LjEyNDlaTTc4LjU1NiAzOC4zNzAxQzc4LjU1NiA1NS4zNTU1IDY0Ljc4NjcgNjkuMTI0OSA0Ny44MDEzIDY5LjEyNDlWODMuMzE5NEM3Mi42MjYxIDgzLjMxOTQgOTIuNzUwNSA2My4xOTQ5IDkyLjc1MDUgMzguMzcwMUg3OC41NTZaTTQ3LjgwMTMgNy42MTUzMkM2NC43ODY2IDcuNjE1MzIgNzguNTU2IDIxLjM4NDcgNzguNTU2IDM4LjM3MDFIOTIuNzUwNUM5Mi43NTA1IDEzLjU0NTMgNzIuNjI2IC02LjU3OTE5IDQ3LjgwMTMgLTYuNTc5MTlWNy42MTUzMlpNNDIuNTgwNSA3LjYxNTMySDQ3LjgwMTNWLTYuNTc5MTlINDIuNTgwNVY3LjYxNTMyWk00MC43MDQgNy42MTUzMkg0Mi41ODA1Vi02LjU3OTE5SDQwLjcwNFY3LjYxNTMyWiIgZmlsbD0iYmxhY2siIG1hc2s9InVybCgjZG9zdS1kLWN1dG91dCkiLz48cGF0aCBkPSJNNjguOTIxNSAzNi4wMTM1QzY4LjkyMTUgMzYuMDEzNSA2NS43MzY5IDQ5LjQ3MzggNTEuNDIzMSA0OS40NzM4QzM3LjEwOTMgNDkuNDczOCAzMi41Nzg3IDM3LjM1OTYgMzIuNTc4NyAzNi4wMTM1IiBzdHJva2U9ImJsYWNrIiB
 zdHJva2Utd2lkdGg9IjcuNjkxNjEiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0wLjM0ODYzMyA4NS40OTQ2QzAuMzQ4NjMzIDg1LjQ5NDYgMjkuNDg1NiA4NS44MzA5IDM0LjgwOSA4NS42OThDNDQuODMzNyA4NS40NDc3IDUxLjI4NzIgODQuNDAyIDU3LjUyNjkgNzguOTcyNEM2Mi44MTI5IDc0LjM3MjcgNzUuMTM0MiA1OS42ODM2IDc1LjEzNDIgNTkuNjgzNiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSI2LjE2NDgyIi8%2BPC9zdmc%2B)](https://github.dosu.com/apache/superset?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=knowledge-infrastructure-learn-repo&utm_term=apache%2Fsuperset) [![Add Dosu to your team](https://img.shields.io/badge/Add%20Dosu%20to%20your%20team-1f6feb?style=flat)](https://app.dosu.dev/signup?referrer=openSource&source=github-footer&utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=knowledge-infrastructure-add-team&utm_term=apache%2Fsuperset)

GitHub link: https://github.com/apache/superset/discussions/38303#discussioncomment-15948699

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] localStorage misses last_async_event_id [superset]

Posted by "dosubot[bot] (via GitHub)" <gi...@apache.org>.
GitHub user dosubot[bot] edited a comment on the discussion: localStorage misses last_async_event_id

<!-- Answer -->
I see the issue clearly now. Your logs show the **`async_channel_id` is changing between requests** - from `3dba366b-a70c-4ab8-9b86-7aaeb4159775` to `7e6ba64b-d2e7-4d8c-aff9-58e6ab88bd9f`. This is the core problem.

**Why `last_async_event_id` is never set:** The frontend [only calls `setLastId()` when it receives at least one event](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset-frontend/src/middleware/asyncEvent.ts#L169-L183). Since `/async_event` returns empty `[]` (no events exist on the new channel), `processEvents()` is never called and localStorage never gets updated.

**Why events are empty:** Each time `async_channel_id` changes, you're polling a *brand new* Redis stream that has no events. The Celery workers wrote events to the old channel (`3dba366b...`), but your poll is hitting the new one (`7e6ba64b...`).

**The "Token reset" you're logging** happens because the [after-request handler detects a mismatch](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py#L167-L195) between the async-token cookie and session state. Looking at your logs:
- The async-token cookie likely contains the old channel_id from a previous request
- But the session (which you reworked to read from the token) shows the new channel_id
- This mismatch triggers regeneration

**The issue with your workaround:** You said "channel comes from token, because request doesn't have it at all". If the async-token cookie isn't being sent with `/async_event` requests, that's likely a **cookie domain or SameSite issue**. Check your browser DevTools → Network tab → click on the `/async_event` request → check if `Cookie: async-token=...` is in the Request Headers.

**To fix this:**
1. Ensure `GLOBAL_ASYNC_QUERIES_JWT_COOKIE_SAMESITE` is set to `"Lax"` or `"None"` (with `Secure=True` if `None`)
2. Ensure `GLOBAL_ASYNC_QUERIES_JWT_COOKIE_DOMAIN` matches your domain exactly
3. If using HTTP (not HTTPS), set `GLOBAL_ASYNC_QUERIES_JWT_COOKIE_SECURE = False`
4. Make sure your OAuth flow doesn't clear/recreate the Flask session after the async-token cookie is already set

The key is keeping the `async_channel_id` stable across all requests during a session - otherwise events get written to one channel but polled from another.


<!-- Dosu Comment Footer -->
*To reply, just mention [@dosu](https://go.dosu.dev/dosubot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=reply-with-mention&utm_term=apache%2Fsuperset).*

---

Share context across your team and agents. Try [Dosu](https://dosu.dev?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=knowledge-infrastructure-tagline&utm_term=apache%2Fsuperset).

[![Leave Feedback](https://img.shields.io/badge/Leave%20Feedback-555555?style=flat)](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=knowledge-infrastructure-feedback&utm_term=apache%2Fsuperset&message_id=e67d6729-50f8-4e5c-8115-813e1aa94344) [![Learn about superset with Dosu](https://img.shields.io/badge/Learn%20about%20superset%20with%20Dosu-2f7b3f?style=flat&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iODYiIGhlaWdodD0iODkiIHZpZXdCb3g9IjAgMCA4NiA4OSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNS4yOTIzNiAxMi43OTI4TDE3Ljc1OTMgNi42ODE4OFY3Mi41NjY3TDUuMjkyMzYgODQuMDYxOFYxMi43OTI4WiIgZmlsbD0iI0I0QkI5MSIvPjxwYXRoIGQ9Ik0xOC4yNTc1IDczLjExOTZMNTkuMTMyOSA3Mi43NDhMNTEuNzAxMSA4Mi40MDk1TDI5LjAzMzggODYuMjkxTDYuMjM5NjIgODUuMTU1NEwxOC4yNTc1IDczLjExOTZaIiBmaWxsPSIjNzc4NTYxIi8%2BPHBhdGggZD0iTTE3LjQ5MTYgMy43MzYzM0wzLjU4NTU3IDEyLjcwOTlWODMuNTc5MkMzLjU4NTU3IDg0Ljc1NDIgNC4
 5ODU2MyA4NS4zNjUyIDUuODQ3MDUgODQuNTY2TDE5LjYyOTYgNzEuNzgwMSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSI2LjQyODQ0IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48bWFzayBpZD0iZG9zdS1kLWN1dG91dCIgZmlsbD0id2hpdGUiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNDAuNzA0IDAuNTE4MDY2SDE3LjA0MzlWNzYuMjIyMUg0MC43MDRINDIuNTgwNUg0Ny44MDEzQzY4LjcwNjQgNzYuMjIyMSA4NS42NTMzIDU5LjI3NTIgODUuNjUzMyAzOC4zNzAxQzg1LjY1MzMgMTcuNDY1IDY4LjcwNjMgMC41MTgwNjYgNDcuODAxMyAwLjUxODA2Nkg0Mi41ODA1SDQwLjcwNFoiLz48L21hc2s%2BPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00MC43MDQgMC41MTgwNjZIMTcuMDQzOVY3Ni4yMjIxSDQwLjcwNEg0Mi41ODA1SDQ3LjgwMTNDNjguNzA2NCA3Ni4yMjIxIDg1LjY1MzMgNTkuMjc1MiA4NS42NTMzIDM4LjM3MDFDODUuNjUzMyAxNy40NjUgNjguNzA2MyAwLjUxODA2NiA0Ny44MDEzIDAuNTE4MDY2SDQyLjU4MDVINDAuNzA0WiIgZmlsbD0iI0YzRjZGMSIvPjxwYXRoIGQ9Ik0xNy4wNDM5IDAuNTE4MDY2Vi02LjU3OTE5SDkuOTQ2NjlWMC41MTgwNjZIMTcuMDQzOVpNMTcuMDQzOSA3Ni4yMjIxSDkuOTQ2NjlWODMuMzE5NEgxNy4wNDM5Vjc2LjIyMjFaTTE3LjA0MzkgNy42MTUzMkg0MC43MDRWLTYuNT
 c5MTlIMTcuMDQzOVY3LjYxNTMyWk0yNC4xNDEyIDc2LjIyMjFWMC41MTgwNjZIOS45NDY2OVY3Ni4yMjIxSDI0LjE0MTJaTTQwLjcwNCA2OS4xMjQ5SDE3LjA0MzlWODMuMzE5NEg0MC43MDRWNjkuMTI0OVpNNDIuNTgwNSA2OS4xMjQ5SDQwLjcwNFY4My4zMTk0SDQyLjU4MDVWNjkuMTI0OVpNNDcuODAxMyA2OS4xMjQ5SDQyLjU4MDVWODMuMzE5NEg0Ny44MDEzVjY5LjEyNDlaTTc4LjU1NiAzOC4zNzAxQzc4LjU1NiA1NS4zNTU1IDY0Ljc4NjcgNjkuMTI0OSA0Ny44MDEzIDY5LjEyNDlWODMuMzE5NEM3Mi42MjYxIDgzLjMxOTQgOTIuNzUwNSA2My4xOTQ5IDkyLjc1MDUgMzguMzcwMUg3OC41NTZaTTQ3LjgwMTMgNy42MTUzMkM2NC43ODY2IDcuNjE1MzIgNzguNTU2IDIxLjM4NDcgNzguNTU2IDM4LjM3MDFIOTIuNzUwNUM5Mi43NTA1IDEzLjU0NTMgNzIuNjI2IC02LjU3OTE5IDQ3LjgwMTMgLTYuNTc5MTlWNy42MTUzMlpNNDIuNTgwNSA3LjYxNTMySDQ3LjgwMTNWLTYuNTc5MTlINDIuNTgwNVY3LjYxNTMyWk00MC43MDQgNy42MTUzMkg0Mi41ODA1Vi02LjU3OTE5SDQwLjcwNFY3LjYxNTMyWiIgZmlsbD0iYmxhY2siIG1hc2s9InVybCgjZG9zdS1kLWN1dG91dCkiLz48cGF0aCBkPSJNNjguOTIxNSAzNi4wMTM1QzY4LjkyMTUgMzYuMDEzNSA2NS43MzY5IDQ5LjQ3MzggNTEuNDIzMSA0OS40NzM4QzM3LjEwOTMgNDkuNDczOCAzMi41Nzg3IDM3LjM1OTYgMzIuNTc4NyAzNi4wMTM1IiBzdHJva2U9ImJsYWNrIiB
 zdHJva2Utd2lkdGg9IjcuNjkxNjEiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0wLjM0ODYzMyA4NS40OTQ2QzAuMzQ4NjMzIDg1LjQ5NDYgMjkuNDg1NiA4NS44MzA5IDM0LjgwOSA4NS42OThDNDQuODMzNyA4NS40NDc3IDUxLjI4NzIgODQuNDAyIDU3LjUyNjkgNzguOTcyNEM2Mi44MTI5IDc0LjM3MjcgNzUuMTM0MiA1OS42ODM2IDc1LjEzNDIgNTkuNjgzNiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSI2LjE2NDgyIi8%2BPC9zdmc%2B)](https://github.dosu.com/apache/superset?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=knowledge-infrastructure-learn-repo&utm_term=apache%2Fsuperset) [![Add Dosu to your team](https://img.shields.io/badge/Add%20Dosu%20to%20your%20team-1f6feb?style=flat)](https://app.dosu.dev/signup?referrer=openSource&source=github-footer&utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=knowledge-infrastructure-add-team&utm_term=apache%2Fsuperset)

GitHub link: https://github.com/apache/superset/discussions/38303#discussioncomment-15954693

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] localStorage misses last_async_event_id [superset]

Posted by "dosubot[bot] (via GitHub)" <gi...@apache.org>.
GitHub user dosubot[bot] added a comment to the discussion: localStorage misses last_async_event_id

<!-- Answer -->
Now I see what's happening! The logs show the channel ID changing (`605caed7...` → `07902a86...`) between consecutive requests, which confirms the async-token JWT cookie is being **regenerated** on each request.

Your `before_request` code itself isn't the issue - it's what happens in the **after-request handler** ([`validate_session()`](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py#L164-L195)) that's creating new tokens.

The [reset_token condition](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py#L167-L172) triggers when:
```python
reset_token = (
    not request.cookies.get(self._jwt_cookie_name)  # ← THIS IS LIKELY YOUR ISSUE
    or "async_channel_id" not in session
    or "async_user_id" not in session
    or user_id != session["async_user_id"]
)
```

Since `async_channel_id` and `async_user_id` are in your session (you showed that in the logs), the most likely cause is **`request.cookies.get("async-token")` returning None** - meaning the browser isn't sending the cookie back.

**Yes, you can use the session's `async_channel_id` to create a stable token!** Here's how:

Instead of letting the system regenerate, you could modify `validate_session()` or add your own after-request handler that:
1. Checks if `async_channel_id` exists in session
2. Uses that existing channel_id (instead of generating a new UUID) when creating the JWT
3. Ensures the cookie is set with correct attributes

Or simpler - figure out why the async-token cookie isn't being sent back. Check:
- Browser DevTools → Application → Cookies: Is `async-token` present after a response?
- Browser DevTools → Network → Request Headers: Is `Cookie: async-token=...` being sent?
- Cookie attributes: Domain, Path, SameSite, Secure flags matching your setup

The fact that [`parse_channel_id_from_request()`](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py#L197-L206) reads **only from the JWT cookie** (not from session) explains why each poll gets a different channel - each request is getting a freshly minted token with a new UUID.


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

---
<sup>How did I do? [Good](https://app.dosu.dev/response-feedback/c79e638f-89f1-48db-aeb2-8ec0a66534bd?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/c79e638f-89f1-48db-aeb2-8ec0a66534bd?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/c79e638f-89f1-48db-aeb2-8ec0a66534bd?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/c79e638f-89f1-48db-aeb2-8ec0a66534bd?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/c79e638f-89f1-48db-aeb2-8ec0a66534bd?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/c79e638f-89f1-48db-aeb2-8ec0a66534bd?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/c79e638f-89f1-48db-aeb2-8ec0a66534bd?feedback_type=other)</sup>

[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/a28d3c7e-a9d3-459e-9fb6-3a6f9ff4f357/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=Now%20I%20see%20what%27s%20happening%21%20The%20logs%20show%20the%20channel%20ID%20changing%20%28%60605caed7...%60%20%E2%86%92%20%6007902a86...%60%29%20between%20consecutive%20requests%2C%20which%20confirms%20the%20async-token%20JWT%20cookie%20is%20being%20%2A%2Aregenerated%2A%2A%20on%20each%20request.%0A%0AYour%20%60before_request%60%20code%20itself%20isn%27t%20the%20issue%20-%20it%27s%20what%20happens%20in%20the%20%2A%2Aafter-request%20handler%2A%2A%20%28%5B%60validate_session%28%29%60%5D%28https%3A//github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py%23L164-L195%29%29%20that%27s%20creating%20new%20tokens.%0A%0AThe%20%5Breset_token%20condition%5D%28https%3A//github.com/apache/superset
 /blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py%23L167-L172%29%20triggers%20when%3A%0A%60%60%60python%0Areset_token%20%3D%20%28%0A%20%20%20%20not%20request.cookies.get%28self._jwt_cookie_name%29%20%20%23%20%E2%86%90%20THIS%20IS%20LIKELY%20YOUR%20ISSUE%0A%20%20%20%20or%20%22async_channel_id%22%20not%20in%20session%0A%20%20%20%20or%20%22async_user_id%22%20not%20in%20session%0A%20%20%20%20or%20user_id%20%21%3D%20session%5B%22async_user_id%22%5D%0A%29%0A%60%60%60%0A%0ASince%20%60async_channel_id%60%20and%20%60async_user_id%60%20are%20in%20your%20session%20%28you%20showed%20that%20in%20the%20logs%29%2C%20the%20most%20likely%20cause%20is%20%2A%2A%60request.cookies.get%28%22async-token%22%29%60%20returning%20None%2A%2A%20-%20meaning%20the%20browser%20isn%27t%20sending%20the%20cookie%20back.%0A%0A%2A%2AYes%2C%20you%20can%20use%20the%20session%27s%20%60async_channel_id%60%20to%20create%20a%20stable%20token%21%2A%2A%20Here%27s%20how%3A%0A%0AInstead%
 20of%20letting%20the%20system%20regenerate%2C%20you%20could%20modify%20%60validate_session%28%29%60%20or%20add%20your%20own%20after-request%20handler%20that%3A%0A1.%20Checks%20if%20%60async_channel_id%60%20exists%20in%20session%0A2.%20Uses%20that%20existing%20channel_id%20%28instead%20of%20generating%20a%20new%20UUID%29%20when%20creating%20the%20JWT%0A3.%20Ensures%20the%20cookie%20is%20set%20with%20correct%20attributes%0A%0AOr%20simpler%20-%20figure%20out%20why%20the%20async-token%20cookie%20isn%27t%20being%20sent%20back.%20Check%3A%0A-%20Browser%20DevTools%20%E2%86%92%20Application%20%E2%86%92%20Cookies%3A%20Is%20%60async-token%60%20present%20after%20a%20response%3F%0A-%20Browser%20DevTools%20%E2%86%92%20Network%20%E2%86%92%20Request%20Headers%3A%20Is%20%60Cookie%3A%20async-token%3D...%60%20being%20sent%3F%0A-%20Cookie%20attributes%3A%20Domain%2C%20Path%2C%20SameSite%2C%20Secure%20flags%20matching%20your%20setup%0A%0AThe%20fact%20that%20%5B%60parse_channel_id_from_request%28%29%60%
 5D%28https%3A//github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/async_events/async_query_manager.py%23L197-L206%29%20reads%20%2A%2Aonly%20from%20the%20JWT%20cookie%2A%2A%20%28not%20from%20session%29%20explains%20why%20each%20poll%20gets%20a%20different%20channel%20-%20each%20request%20is%20getting%20a%20freshly%20minted%20token%20with%20a%20new%20UUID.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share 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/38303)

GitHub link: https://github.com/apache/superset/discussions/38303#discussioncomment-15954736

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [D] localStorage misses last_async_event_id [superset]

Posted by "rsaleev (via GitHub)" <gi...@apache.org>.
GitHub user rsaleev added a comment to the discussion: localStorage misses last_async_event_id

@dosu 

```
def auth_user_logged_in(self):
        if hasattr(g, "user") and g.user and g.user.is_authenticated:
            return True
        # 2. Check current_user (Flask-Login)
        try:
            if current_user.is_authenticated:
                g.user = current_user
                log.debug(
                    f"✅ [auth_user_logged_in] Set g.user from current_user: {g.user.username}"
                )
                return True
        except Exception as e:
            log.debug(f"[auth_user_logged_in] current_user check failed: {e}")
        # 3. ✅ CRITICAL: If session has _user_id, LOAD THE USER FROM DB
        user_id = session.get("_user_id")
        if user_id:
            try:
                from flask_appbuilder.security.sqla.models import User

                from superset import db

                user = db.session.get(User, int(user_id))
                if user:
                    g.user = user  # ✅ FORCE SET g.user HERE
                    log.debug(
                        f"✅ [auth_user_logged_in] Loaded user from session ID: {user.username}"
                    )
                    login_user(user, force=False, duration=PERMANENT_SESSION_LIFETIME)
                    return True
                else:
                    log.warning(f"User ID {user_id} in session but not found in DB")
                    session.pop("_user_id", None)  # Clean up bad session
            except Exception as e:
                log.error(f"[auth_user_logged_in] Error loading user {user_id}: {e}")
                raise
        return False

    def before_request(self):
        log.debug(f"Session: {session.items()}")
        logged_in = self.auth_user_logged_in()
        redirect_count = session.get("auth_redirect_count", 0)
        if redirect_count > 3:
            log.error("🚨 Auth redirect loop detected, clearing session")
            session.clear()
            return jsonify({"error": "Authentication loop detected"}), 401

        if request.endpoint in [
            "KeycloakOAuthView.login",
            "KeycloakOAuthView.oauth_authorized",
            "KeycloakOAuthView.logout",
        ]:
            log.debug(f"🟢 Allowing auth endpoint: {request.endpoint}")
            return None

        if any(request.path.startswith(path) for path in self.PUBLIC_PATHS):
            log.debug(f"🟢 Public path allowed: {request.path}")
            return None

        if request.endpoint and any(
            request.endpoint.startswith(ep) for ep in self.PUBLIC_ENDPOINTS
        ):
            log.debug(f"🟢 Public endpoint allowed: {request.endpoint}")
            return None

        if request.path.startswith("/api/"):
            if not logged_in:
                log.warning(f"🔐 Unauthenticated API access: {request.endpoint}")
                return jsonify({"error": "Authentication required"}), 401

        if not logged_in:
            log.warning(f"🔐 Unauthenticated access to {request.endpoint}")
            return redirect(
                url_for("KeycloakOAuthView.login", provider=DEFAULT_PROVIDER)
            )
        session.pop("auth_redirect_count", None)
        # Log successful auth for debugging
        username = g.user.username if hasattr(g, "user") and g.user else "unknown"
        log.debug(f"✅ User {username} authenticated for {request.endpoint}")

        return super().before_request()
        ```
 I suppose it triggers channel id refresh, but I don't get it why.
```
2026-02-28 12:39:04,321:DEBUG:customization.oauth_config:✅ [auth_user_logged_in] Set g.user from current_user: test_user

2026-02-28 12:39:04,321:DEBUG:customization.oauth_config:✅ User test_user authenticated for AsyncEventsRestApi.events

2026-02-28 12:39:04,336:DEBUG:superset.async_events.async_query_manager:Parsed JWT token channel: 605caed7-e8e7-4a8a-9fdc-eb45783aa436
```
```
2026-02-28 12:39:04,931:DEBUG:customization.oauth_config:✅ [auth_user_logged_in] Set g.user from current_user: test_user

2026-02-28 12:39:04,931:DEBUG:customization.oauth_config:✅ User test_user authenticated for AsyncEventsRestApi.events

2026-02-28 12:39:04,953:DEBUG:superset.async_events.async_query_manager:Parsed JWT token channel: 07902a86-3752-4e43-a06a-0c837a91ae96
```

But channel ID leaves refreshes in session, maybe I can use it to create proper async-token?



GitHub link: https://github.com/apache/superset/discussions/38303#discussioncomment-15954728

----
This is an automatically sent email for notifications@superset.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@superset.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org