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

   ### Bug description
   
   With OAuth2-authenticated database connections (SIP-85; e.g. Trino with 
impersonation), when a user's stored refresh token is rejected by the IdP, 
Superset now surfaces a hard `GENERIC_DB_ENGINE_ERROR` to the user during query 
execution, e.g.:
   
   ```
   400 Client Error: Bad Request for url: https://<idp>/oauth2/v1/token
   Issue 1002 - The database returned an unexpected error.
   ```
   
   The user then stays broken on **every** subsequent query, because the 
invalid token is never cleared. Prior to 6.1.0 the same failed refresh was 
handled gracefully — Superset fell back to the interactive OAuth2 dance 
(re-auth) and the user transparently recovered.
   
   ### Root cause
   
   6.1.0 (#37350) added `response.raise_for_status()` to 
`BaseEngineSpec.get_oauth2_fresh_token` (and `get_oauth2_token`):
   
   - `superset/db_engine_specs/base.py:831` — `response.raise_for_status()` in 
`get_oauth2_fresh_token`
   
   So a 4xx from the IdP token endpoint now raises 
`requests.exceptions.HTTPError`.
   
   But the refresh error handling in 
`superset/utils/oauth2.py::refresh_oauth2_token` only special-cases the engine 
spec's `oauth2_exception`:
   
   - `superset/utils/oauth2.py:144` — `except db_engine_spec.oauth2_exception:` 
→ deletes the token and re-raises (this is what triggers a re-dance)
   - `superset/utils/oauth2.py:153` — `except Exception:` → re-raises, 
**without** deleting the token
   
   `oauth2_exception` is `OAuth2RedirectError` 
(`superset/db_engine_specs/base.py:589`), and `get_oauth2_fresh_token` raises a 
plain `requests.exceptions.HTTPError`, which is **not** an 
`OAuth2RedirectError`. So the `HTTPError` falls through to the generic `except 
Exception`, the stale token is **not** deleted, and the exception propagates to 
the query as `GENERIC_DB_ENGINE_ERROR`.
   
   For comparison, the pre-6.1.0 behavior: without `raise_for_status()`, a 4xx 
returned the error body; `refresh_oauth2_token` then hit `if "access_token" not 
in token_response: return None` (`superset/utils/oauth2.py:164`), returning 
`None`, which makes `get_oauth2_access_token` return `None` and the caller 
start the OAuth2 dance.
   
   Net regression: an expired/revoked/superseded refresh token used to trigger 
a silent re-auth; it is now a fatal, sticky error for the affected user.
   
   ### How to reproduce
   
   1. Configure an OAuth2 database connection (`DATABASE_OAUTH2_CLIENTS`) with 
`offline_access` (e.g. Trino + Okta), impersonation enabled.
   2. Authorize the database, then cause the stored refresh token to become 
invalid at the IdP (e.g. revoked/superseded/expired — Okta single-use 
refresh-token rotation or org-auth-server refresh-token expiry will do this).
   3. After the access token expires, run a query against that database.
   
   **Expected:** Superset re-runs the OAuth2 dance (user re-authorizes), as in 
<= 6.0.0.
   
   **Actual:** the query fails with `400 Client Error ... /oauth2/v1/token` 
(Issue 1002/1011), and every subsequent query keeps failing because the stale 
token row in `database_user_oauth2_tokens` is never deleted.
   
   ### Suggested fix
   
   In `refresh_oauth2_token`, treat an IdP token-endpoint failure as an OAuth2 
error rather than a generic one. For example, catch 
`requests.exceptions.HTTPError` (or detect `error == "invalid_grant"` / a 4xx 
in the response), delete the stored token, and surface the spec's 
`oauth2_exception` so the OAuth2 dance restarts — or simply return `None` on 
such failures (the pre-6.1.0 behavior). Today only `oauth2_exception` is 
handled, but `get_oauth2_fresh_token` never raises that type.
   
   ### Superset version
   
   6.1.0 (regression introduced by #37350; 6.0.0 and earlier do not call 
`raise_for_status()` here).
   
   ### Additional context
   
   - DB engine: Trino (impersonation, `DATABASE_OAUTH2`)
   - IdP: Okta (org authorization server)


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