alionar opened a new issue, #43550:
URL: https://github.com/apache/superset/issues/43550

   ### Bug description
   
   A production incident occurred on Superset 4.0.2 when a user saved a dataset 
after the cached CSRF token had expired.
   
   The JSON API mutation returned HTTP 400 with `GENERIC_BACKEND_ERROR` and 
Issue 1011. `SupersetClient` did not refresh the token or retry the request. 
The user saw a generic error and had to reload the page before saving again.
   
   Source inspection confirms that the same behavior remains in the latest 
`master` branch:
   
   1. `SupersetClient` caches and reuses the CSRF token.
   2. The default token lifetime is one week.
   3. Flask-WTF raises `CSRFError` after expiration.
   4. JSON requests are converted into a generic HTTP 400 response.
   5. `SupersetClient.request()` handles HTTP 401 but not CSRF-related HTTP 400 
responses.
   
   ### Steps to reproduce
   
   1. Configure a short CSRF lifetime:
   
      ```python
      WTF_CSRF_TIME_LIMIT = 5
      ```
   
   2. Restart Superset.
   3. Log in and open the dataset editor.
   4. Wait longer than five seconds.
   5. Modify and save the dataset.
   6. Inspect the browser network requests.
   
   ### Actual behavior
   
   The save request returns HTTP 400:
   
   ```json
   {
     "errors": [
       {
         "error_type": "GENERIC_BACKEND_ERROR",
         "level": "error",
         "extra": {
           "issue_codes": [
             {
               "code": 1011
             }
           ]
         }
       }
     ]
   }
   ```
   
   The frontend does not request a new CSRF token or retry the save. Reloading 
the page obtains a new token and allows the user to save again.
   
   ### Expected behavior
   
   Superset should recover from an expired CSRF token without requiring a page 
reload:
   
   1. Return a machine-readable CSRF error type.
   2. Fetch a new token from `/api/v1/security/csrf_token/`.
   3. Retry the original request once.
   4. Prevent refresh and retry loops.
   5. Share one refresh request when several requests fail concurrently.
   
   This does not bypass CSRF validation. The client obtains a new token for the 
existing authenticated session after Flask-WTF rejects the original request.
   
   ### Related work
   
   PR #14675 added login redirection for expired CSRF tokens. The redirect 
applies to non-JSON requests. The JSON branch continues to return a generic 
HTTP 400 response.
   
   Issue #16565 asked which status code is returned when a token expires, but 
it did not address frontend recovery.
   
   ### Environment
   
   - Incident environment: Superset 4.0.2
   - Current status: behavior confirmed by source inspection on `master`
   - Browser: Chrome
   - CSRF lifetime: default one week
   - Runtime reproduction on `master`: not performed
   
   ### Additional context
   
   Relevant source:
   
   - Superset 4.0.2 handler:
     
https://github.com/apache/superset/blob/4.0.2/superset/views/base.py#L460-L490
   
   - Superset 4.0.2 client:
     
https://github.com/apache/superset/blob/4.0.2/superset-frontend/packages/superset-ui-core/src/connection/SupersetClientClass.ts#L203-L215
   
   - Current master handler:
     
https://github.com/apache/superset/blob/686245a905a60d8ea49b84afacfd6977a65cb5ed/superset/views/error_handling.py#L206-L240
   
   - Current master client:
     
https://github.com/apache/superset/blob/686245a905a60d8ea49b84afacfd6977a65cb5ed/superset-frontend/packages/superset-ui-core/src/connection/SupersetClientClass.ts#L214-L240
   
   Suggested implementation:
   
   - Return a dedicated CSRF error type.
   - Detect it in `SupersetClient`.
   - Refresh through `reAuthenticate()`.
   - Use a shared refresh promise.
   - Retry the original request once.
   - Preserve the existing non-JSON login redirect.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to