saurabhdamle11 opened a new pull request, #43655:
URL: https://github.com/apache/superset/pull/43655

   ### SUMMARY
   Fixes #43550.
   
   When a cached CSRF token expired, JSON API mutations failed with an HTTP 400
   `GENERIC_BACKEND_ERROR` (issue code 1011). `SupersetClient` handled HTTP 401 
but
   not CSRF-related 400s, so it neither refreshed the token nor retried the 
request.
   The user saw a generic error and had to reload the page before the action 
(e.g.
   saving a dataset) would succeed.
   
   This change lets Superset recover from an expired CSRF token in JSON requests
   without a page reload:
   
   - `views/error_handling.py` now returns a dedicated, machine-readable
     `CSRF_ERROR` type for JSON requests instead of a generic backend error. The
     existing non-JSON login redirect (from #14675) is unchanged. `CSRF_ERROR` 
is
     added to `errors.py` and to `SAFE_ERROR_TYPES` in `error_sanitization.py` 
so
     the type survives guest-user sanitization.
   - `SupersetClientClass` detects that error type, fetches a fresh token from
     `/api/v1/security/csrf_token/`, and retries the original request once.
   - A shared refresh promise ensures that when several requests fail
     concurrently, only one token refresh is issued rather than one per request.
     Retry is capped at a single attempt, so a request that fails again cannot
     trigger a refresh/retry loop.
   - Added a `CsrfErrorMessage` component, registered in `setupErrorMessages`, 
for
     the case where recovery is not possible — the replay was rejected too, most
     often because the session itself is gone.
   
   **Why a new `CSRF_ERROR` rather than the existing `FRONTEND_CSRF_ERROR`:**
   `FRONTEND_CSRF_ERROR` is a client-originated marker and is never emitted by 
the
   backend. This is a server-emitted rejection from Flask-WTF, so it needs its 
own
   type; reusing the frontend one would conflate two different origins.
   
   **Security note:** this does not bypass or weaken CSRF validation. The 
refresh
   only runs *after* Flask-WTF has already rejected the request, and only when 
the
   server explicitly identifies the failure as `CSRF_ERROR`. The new token is
   issued against the existing authenticated session, the refresh runs in the
   same-origin application context (a cross-site attacker cannot execute it or 
read
   the response), and the single-retry cap means a genuinely forged request 
fails
   after one replay rather than looping.
   
   ### BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
   N/A. The recovery is transparent; the visible change is the absence of an
   error. The one new visual surface is `CsrfErrorMessage`, shown only when the
   token refresh itself fails.
   
   ### TESTING INSTRUCTIONS
   1. Set a short CSRF lifetime in `superset_config.py`:
      ```python
      WTF_CSRF_TIME_LIMIT = 5
   2. Restart Superset, log in, and open the dataset editor.
   3. Wait longer than 5 seconds, then modify and save the dataset.
   4. Confirm the save succeeds without a page reload. In the Network tab, 
verify a
      request to /api/v1/security/csrf_token/ is issued and the original save
      request is retried once and succeeds.
   5. Trigger several mutations concurrently after expiry and confirm only one
      token refresh request is issued.
   6. Confirm non-JSON requests still redirect to login on CSRF expiry 
(unchanged).
   7. Run the added tests:
   pytest tests/unit_tests/views/test_error_handling.py
   
   cd superset-frontend
   npm run test -- 
packages/superset-ui-core/test/connection/SupersetClientCsrfRecovery.test.ts
   npm run test -- src/components/ErrorMessage/CsrfErrorMessage.test.tsx
   
   ADDITIONAL INFORMATION
   
   - [x] Has associated issue: #43550
   - [ ] Required feature flags:
   - [x] Changes UI
   - [ ] Includes DB Migration (follow approval process in SIP-59)
     - [ ] Migration is atomic, supports rollback & is backwards-compatible
     - [ ] Confirm DB migration upgrade and downgrade tested
     - [ ] Runtime estimates and downtime expectations provided
   - [ ] Introduces new feature or API
   - [ ] Removes existing feature or API
   
   Note for reviewers: this adds a public refreshCSRFToken() method on
   SupersetClientClass and a new CSRF_ERROR member to the exported
   SupersetApiErrorType / ErrorTypeEnum in @superset-ui/core. Both are
   additive and backwards-compatible, but they are consumable surface for 
plugins.


-- 
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