Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Superset Async Query Bug - Websocket Connection Failed JWT not present #27293

Open
3 tasks done
Haoran-Qi opened this issue Feb 28, 2024 · 6 comments
Open
3 tasks done

Comments

@Haoran-Qi
Copy link

Haoran-Qi commented Feb 28, 2024

Bug description

Hi

I find a bug in the Async Query Websocket code while I'm trying to enable the Async query "ws" mode
I followed the instructions and docs to set up all the config values
https://github.com/apache/superset/blob/master/CONTRIBUTING.md#async-chart-queries
#9190

In the websocket code, it is looking for the channel_id from cookie encrypted by JWT

const cookies = cookie.parse(request.headers.cookie || '');

However in the sueprset_app's ws connection, we are not provbiding proper JWT token to establish WS connection

Which lead to a JWT not present error.

I try to connects to the Websocket conatainer locally with following code, and it works

const jwt = require('jsonwebtoken');
const WebSocket = require('ws');


// Establish WebSocket connection
const jwts = "*******"
const token = jwt.sign(
    { "channel": "1234122341234" },
    jwts,
)
const socket = new WebSocket('ws://127.0.0.1:8080/', {
    headers: {
        cookie: "async-token=" + token
    }
});

// Event listener for when the connection is established
socket.onopen = function (event) {
    console.log('WebSocket connection established.');
};

I notice the we create the channel_id and set the cookie in async_mananger, but I don't think it reflects on websocket container

How to reproduce the bug

  1. follow the instruction to enable Async query
  2. docker compose up
  3. you can see the superset_app cannot connect to websocket container because of the JWT token missing

image

my config
GLOBAL_ASYNC_QUERIES_JWT_SECRET = "***********"
GLOBAL_ASYNC_QUERIES_TRANSPORT = "ws"
GLOBAL_ASYNC_QUERIES_JWT_COOKIE_SECURE = True
GLOBAL_ASYNC_QUERIES_REDIS_CONFIG = {
"port": REDIS_PORT,
"host": REDIS_HOST,
"password": "",
"db": 0,
"ssl": False,
}

Screenshots/recordings

No response

Superset version

master / latest-dev

Python version

3.9

Node version

16

Browser

Chrome

Additional context

No response

Checklist

  • I have searched Superset docs and Slack and didn't find a solution to my problem.
  • I have searched the GitHub issue tracker and didn't find a similar bug report.
  • I have checked Superset's logs for errors and if I found a relevant Python stacktrace, I included it here as text in the "additional context" section.
@Haoran-Qi Haoran-Qi changed the title Superset Async Query Websocket JWT not present Superset Async Query Bug - Websocket Connection Failed JWT not present Feb 28, 2024
@ericho-bbai
Copy link

ericho-bbai commented Feb 28, 2024

Did you make a copy of superset-websocket/config.example.json as superset-websocket/config.json and change the jwtSecret value? You can use https://jwt.io/ to generate a valid token. I don't think it matters what the payload is, just have to be a valid JWT.

@Haoran-Qi
Copy link
Author

Haoran-Qi commented Feb 29, 2024

Yeah, I did make the superset-websocket/config.json file with 32 bytes secret key, same key as the GLOBAL_ASYNC_QUERIES_JWT_SECRET I set up in docker/pythonpath_dev/superset_config.py.

In superset app container

I don't think it provids the JWT in the header, that's why websocket container is giving a JWT not present error

If the token is invalid, it should throw an error with the jwt verification failure in the following line.

const jwtPayload = jwt.verify(token, opts.jwtSecret, {

@amitdixit
Copy link

I am facing the same issue with Kubernetes deployment. JWT

@rusackas
Copy link
Member

Is anyone still facing this? It seems to have gone silent, so may eventually be closed as stale. In the meantime, the websocket connection is being addressed in a forthcoming SIP by @villebro and subject to architecture redesign.

For good measure, I'll also ping @dosu-bot in case it's helpful here.

Copy link

dosubot bot commented Jul 30, 2024

Hey @rusackas! I'm here to help you with any bugs, questions, or contributions. I'm Dosu, a bot designed to assist you while you wait for a human maintainer. Let me know how I can help!

To address the "JWT not present" error in the WebSocket connection for Superset's async query feature, ensure the following configurations and steps are correctly implemented:

  1. Configuration Settings:

    • Ensure that the GLOBAL_ASYNC_QUERIES_JWT_SECRET and GLOBAL_ASYNC_QUERIES_JWT_COOKIE_NAME are set correctly in your superset_config.py and match the values in superset-websocket/config.json.
  2. Environment Variables:

    • Set the JWT_SECRET and JWT_COOKIE_NAME environment variables to match the values used in your configuration.
  3. WebSocket Configuration:

    • Ensure the superset-websocket/config.json file is correctly configured with the jwtSecret and jwtCookieName values. Here is an example configuration:
      {
        "port": 8080,
        "logLevel": "info",
        "logToFile": false,
        "logFilename": "app.log",
        "statsd": {
          "host": "127.0.0.1",
          "port": 8125,
          "globalTags": []
        },
        "redis": {
          "port": 6379,
          "host": "127.0.0.1",
          "password": "",
          "db": 0,
          "ssl": false
        },
        "redisStreamPrefix": "async-events-",
        "jwtAlgorithms": ["HS256"],
        "jwtSecret": "YOUR_32_BYTE_SECRET_KEY",
        "jwtCookieName": "async-token"
      }
  4. Session and Cookie Handling:

    • The async_query_manager.py file should correctly create the channel_id and set the JWT cookie. Ensure the validate_session method is properly setting the cookie:
      def register_request_handlers(self, app: Flask) -> None:
          @app.after_request
          def validate_session(response: Response) -> Response:
              user_id = get_user_id()
              reset_token = (
                  not request.cookies.get(self._jwt_cookie_name)
                  or "async_channel_id" not in session
                  or "async_user_id" not in session
                  or user_id != session["async_user_id"]
              )
              if reset_token:
                  async_channel_id = str(uuid.uuid4())
                  session["async_channel_id"] = async_channel_id
                  session["async_user_id"] = user_id
                  sub = str(user_id) if user_id else None
                  token = jwt.encode(
                      {"channel": async_channel_id, "sub": sub},
                      self._jwt_secret,
                      algorithm="HS256",
                  )
                  response.set_cookie(
                      self._jwt_cookie_name,
                      value=token,
                      httponly=True,
                      secure=self._jwt_cookie_secure,
                      domain=self._jwt_cookie_domain,
                      samesite=self._jwt_cookie_samesite,
                  )
              return response
  5. WebSocket Connection:

    • Ensure the WebSocket connection is established with the correct JWT token in the headers. Here is an example of how to manually create a JWT token and establish a WebSocket connection:
      const jwt = require('jsonwebtoken');
      const WebSocket = require('ws');
      const token = jwt.sign(
          { "channel": "1234122341234" },
          "YOUR_32_BYTE_SECRET_KEY"
      );
      const socket = new WebSocket('ws://127.0.0.1:8080/', {
          headers: {
              cookie: "async-token=" + token
          }
      });
      socket.onopen = function (event) {
          console.log('WebSocket connection established.');
      };
  6. Check for Updates:

    • There have been updates related to WebSocket connections and JWT tokens in Superset. Refer to the pull request #25891 for changes that support different JWT CSRF cookie names, which might address related issues.

By ensuring these configurations and steps are correctly implemented, the "JWT not present" error should be resolved [1][2][3].

To continue talking to Dosu, mention @dosu.

@ptdtan
Copy link

ptdtan commented Oct 15, 2024

I'm still having the same problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants