You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by va...@apache.org on 2026/03/03 17:28:52 UTC
(superset) branch feat/oauth-single-refresh-tokens created (now 9b934f37b71)
This is an automated email from the ASF dual-hosted git repository.
vavila pushed a change to branch feat/oauth-single-refresh-tokens
in repository https://gitbox.apache.org/repos/asf/superset.git
at 9b934f37b71 feat: Support OAuth2 single-use refresh tokens
This branch includes the following new commits:
new 9b934f37b71 feat: Support OAuth2 single-use refresh tokens
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(superset) 01/01: feat: Support OAuth2 single-use refresh tokens
Posted by va...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
vavila pushed a commit to branch feat/oauth-single-refresh-tokens
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 9b934f37b716bcdf9b7f6ad531d7bb0857548486
Author: Vitor Avila <vi...@gmail.com>
AuthorDate: Tue Mar 3 14:24:55 2026 -0300
feat: Support OAuth2 single-use refresh tokens
---
superset/utils/oauth2.py | 4 +++
tests/unit_tests/utils/oauth2_tests.py | 56 ++++++++++++++++++++++++++++++++++
2 files changed, 60 insertions(+)
diff --git a/superset/utils/oauth2.py b/superset/utils/oauth2.py
index 57cc0a25ce9..4978c0af5c5 100644
--- a/superset/utils/oauth2.py
+++ b/superset/utils/oauth2.py
@@ -167,6 +167,10 @@ def refresh_oauth2_token(
token.access_token_expiration = datetime.now() + timedelta(
seconds=token_response["expires_in"]
)
+ # Support single-use refresh tokens
+ if new_refresh_token := token_response.get("refresh_token"):
+ token.refresh_token = new_refresh_token
+
db.session.add(token)
return token.access_token
diff --git a/tests/unit_tests/utils/oauth2_tests.py b/tests/unit_tests/utils/oauth2_tests.py
index 08b7cc9c6e7..f04ae26e7c2 100644
--- a/tests/unit_tests/utils/oauth2_tests.py
+++ b/tests/unit_tests/utils/oauth2_tests.py
@@ -188,6 +188,62 @@ def test_refresh_oauth2_token_no_access_token_in_response(
assert result is None
+def test_refresh_oauth2_token_updates_refresh_token(
+ mocker: MockerFixture,
+) -> None:
+ """
+ Test that refresh_oauth2_token updates the refresh token when a new one is returned.
+
+ Some OAuth2 providers issue single-use refresh tokens, where each token refresh
+ response includes a new refresh token that replaces the previous one.
+ """
+ db = mocker.patch("superset.utils.oauth2.db")
+ mocker.patch("superset.utils.oauth2.DistributedLock")
+ db_engine_spec = mocker.MagicMock()
+ db_engine_spec.get_oauth2_fresh_token.return_value = {
+ "access_token": "new-access-token",
+ "expires_in": 3600,
+ "refresh_token": "new-refresh-token",
+ }
+ token = mocker.MagicMock()
+ token.refresh_token = "old-refresh-token" # noqa: S105
+
+ with freeze_time("2024-01-01"):
+ refresh_oauth2_token(DUMMY_OAUTH2_CONFIG, 1, 1, db_engine_spec, token)
+
+ assert token.access_token == "new-access-token" # noqa: S105
+ assert token.access_token_expiration == datetime(2024, 1, 1, 1)
+ assert token.refresh_token == "new-refresh-token" # noqa: S105
+ db.session.add.assert_called_with(token)
+
+
+def test_refresh_oauth2_token_keeps_refresh_token(
+ mocker: MockerFixture,
+) -> None:
+ """
+ Test that refresh_oauth2_token keeps the existing refresh token when none returned.
+
+ When the OAuth2 provider does not issue a new refresh token in the response,
+ the original refresh token should be preserved.
+ """
+ db = mocker.patch("superset.utils.oauth2.db")
+ mocker.patch("superset.utils.oauth2.DistributedLock")
+ db_engine_spec = mocker.MagicMock()
+ db_engine_spec.get_oauth2_fresh_token.return_value = {
+ "access_token": "new-access-token",
+ "expires_in": 3600,
+ }
+ token = mocker.MagicMock()
+ token.refresh_token = "original-refresh-token" # noqa: S105
+
+ with freeze_time("2024-01-01"):
+ refresh_oauth2_token(DUMMY_OAUTH2_CONFIG, 1, 1, db_engine_spec, token)
+
+ assert token.access_token == "new-access-token" # noqa: S105
+ assert token.refresh_token == "original-refresh-token" # noqa: S105
+ db.session.add.assert_called_with(token)
+
+
def test_generate_code_verifier_length() -> None:
"""
Test that generate_code_verifier produces a string of valid length (RFC 7636).