From 6b144d408610d56e38664960f9b9b196b3144ac1 Mon Sep 17 00:00:00 2001 From: maxi297 Date: Mon, 28 Apr 2025 23:07:52 -0400 Subject: [PATCH 1/2] [ISSUE #27605] support json as auth token refresh query --- .../requests_native_auth/abstract_oauth.py | 31 +++++++++++++---- .../sources/declarative/auth/test_oauth.py | 34 ++++++++++++++++++- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py b/airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py index 108055f1d..8c5b2fb37 100644 --- a/airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py +++ b/airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py @@ -211,12 +211,21 @@ def _make_handled_request(self) -> Any: Exception: For any other exceptions that occur during the request. """ try: - response = requests.request( - method="POST", - url=self.get_token_refresh_endpoint(), # type: ignore # returns None, if not provided, but str | bytes is expected. - data=self.build_refresh_request_body(), - headers=self.build_refresh_request_headers(), - ) + headers = self.build_refresh_request_headers() + if self._is_application_json(headers): + response = requests.request( + method="POST", + url=self.get_token_refresh_endpoint(), # type: ignore # returns None, if not provided, but str | bytes is expected. + json=self.build_refresh_request_body(), + headers=headers, + ) + else: + response = requests.request( + method="POST", + url=self.get_token_refresh_endpoint(), # type: ignore # returns None, if not provided, but str | bytes is expected. + data=self.build_refresh_request_body(), + headers=headers, + ) # log the response even if the request failed for troubleshooting purposes self._log_response(response) response.raise_for_status() @@ -234,6 +243,16 @@ def _make_handled_request(self) -> Any: except Exception as e: raise Exception(f"Error while refreshing access token: {e}") from e + @staticmethod + def _is_application_json(headers: Mapping[str, Any] | None) -> bool: + if not headers: + return False + + for key, value in headers.items(): + if key.lower() == "content-type" and value.lower() == "application/json": + return True + return False + def _ensure_access_token_in_response(self, response_data: Mapping[str, Any]) -> None: """ Ensures that the access token is present in the response data. diff --git a/unit_tests/sources/declarative/auth/test_oauth.py b/unit_tests/sources/declarative/auth/test_oauth.py index 077aa4573..10e14f97f 100644 --- a/unit_tests/sources/declarative/auth/test_oauth.py +++ b/unit_tests/sources/declarative/auth/test_oauth.py @@ -5,7 +5,7 @@ import base64 import json import logging -from datetime import timedelta, timezone +from datetime import timedelta from unittest.mock import Mock import freezegun @@ -400,6 +400,38 @@ def test_no_expiry_date_provided_by_auth_server( assert oauth.access_token == expected_access_token assert oauth._token_expiry_date == expected_new_expiry_date + @freezegun.freeze_time("2022-01-01") + def test_given_content_type_application_json_when_refresh_token_then_send_request_as_json( + self, + ) -> None: + oauth = DeclarativeOauth2Authenticator( + token_refresh_endpoint="https://refresh_endpoint.com/", + refresh_request_headers={"Content-type": "application/json"}, + client_id="some_client_id", + client_secret="some_client_secret", + refresh_token="some_refresh_token", + config={}, + parameters={}, + grant_type="client", + ) + + with HttpMocker() as http_mocker: + http_mocker.post( + HttpRequest( + url="https://refresh_endpoint.com/", + body=json.dumps({ + "grant_type": "client", + "client_id": "some_client_id", + "client_secret": "some_client_secret", + "refresh_token": "some_refresh_token", + }), + ), + HttpResponse(body=json.dumps({"access_token": "new_access_token"})), + ) + oauth.get_access_token() + + assert oauth.access_token == "new_access_token" + @pytest.mark.parametrize( "expires_in_response, token_expiry_date_format", [ From 1c3b7cd5c295f11397ae45939d27ac048e93d9e2 Mon Sep 17 00:00:00 2001 From: octavia-squidington-iii Date: Tue, 29 Apr 2025 03:19:14 +0000 Subject: [PATCH 2/2] Auto-fix lint and format issues --- unit_tests/sources/declarative/auth/test_oauth.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/unit_tests/sources/declarative/auth/test_oauth.py b/unit_tests/sources/declarative/auth/test_oauth.py index 10e14f97f..f6fadf44e 100644 --- a/unit_tests/sources/declarative/auth/test_oauth.py +++ b/unit_tests/sources/declarative/auth/test_oauth.py @@ -419,12 +419,14 @@ def test_given_content_type_application_json_when_refresh_token_then_send_reques http_mocker.post( HttpRequest( url="https://refresh_endpoint.com/", - body=json.dumps({ - "grant_type": "client", - "client_id": "some_client_id", - "client_secret": "some_client_secret", - "refresh_token": "some_refresh_token", - }), + body=json.dumps( + { + "grant_type": "client", + "client_id": "some_client_id", + "client_secret": "some_client_secret", + "refresh_token": "some_refresh_token", + } + ), ), HttpResponse(body=json.dumps({"access_token": "new_access_token"})), )