Skip to content

fix: no longer require token expiration from the OAuth resource server #462

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Apr 16, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ def refresh_access_token(self) -> Tuple[str, Union[str, int]]:
# ----------------
# PRIVATE METHODS
# ----------------

def _default_token_expiry_date(self) -> str:
"""
Returns the default token expiry date
"""
if self.token_expiry_is_time_of_expiration:
return str(ab_datetime_now() + timedelta(hours=1))
else:
return "3600"

def _wrap_refresh_token_exception(
self, exception: requests.exceptions.RequestException
Expand Down Expand Up @@ -316,9 +325,15 @@ def _extract_token_expiry_date(self, response_data: Mapping[str, Any]) -> Any:
response_data (Mapping[str, Any]): The response data from which to extract the token_expiry_date.

Returns:
str: The extracted token_expiry_date.
The extracted token_expiry_date or None if not found.
"""
return self._find_and_get_value_from_response(response_data, self.get_expires_in_name())
expires_in = self._find_and_get_value_from_response(response_data, self.get_expires_in_name())
# If the access token expires in is None, we do not know when the token will expire
# 1 hour was chosen as a middle ground to avoid unnecessary frequent refreshes and token expiration
if expires_in is None:
return self._default_token_expiry_date()
else:
return expires_in

def _find_and_get_value_from_response(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ def get_access_token(self) -> str:
self._emit_control_message()
return self.access_token

def refresh_access_token(self) -> Tuple[str, str, str]: # type: ignore[override]
def refresh_access_token(self) -> Tuple[str, Optional[str], str]: # type: ignore[override]
"""
Refreshes the access token by making a handled request and extracting the necessary token information.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import json
import logging
from datetime import timedelta, timezone
from datetime import timedelta
from typing import Optional, Union
from unittest.mock import Mock

Expand Down Expand Up @@ -236,6 +236,7 @@ def test_refresh_request_body_with_keys_override(self):
}
assert body == expected

@freezegun.freeze_time("2022-01-01")
def test_refresh_access_token(self, mocker):
oauth = Oauth2Authenticator(
token_refresh_endpoint="https://refresh_endpoint.com",
Expand Down Expand Up @@ -281,6 +282,15 @@ def test_refresh_access_token(self, mocker):
assert isinstance(expires_in, str)
assert ("access_token", "2022-04-24T00:00:00Z") == (token, expires_in)

# Test with no expires_in
mocker.patch.object(
resp,
"json",
return_value={"access_token": "access_token"},
)
token, expires_in = oauth.refresh_access_token()
assert expires_in == "3600"

# Test with nested access_token and expires_in as str(int)
mocker.patch.object(
resp,
Expand Down Expand Up @@ -393,8 +403,10 @@ def test_refresh_access_token_when_headers_provided(self, mocker):
"YYYY-MM-DDTHH:mm:ss.SSSSSSZ",
AirbyteDateTime(year=2022, month=2, day=12),
),
(None, None, AirbyteDateTime(year=2022, month=1, day=1, hour=1)),
(None, "YYYY-MM-DD", AirbyteDateTime(year=2022, month=1, day=1, hour=1)),
],
ids=["seconds", "string_of_seconds", "simple_date", "simple_datetime"],
ids=["seconds", "string_of_seconds", "simple_date", "simple_datetime", "default_behavior", "default_behavior_with_format"],
)
@freezegun.freeze_time("2022-01-01")
def test_parse_refresh_token_lifespan(
Expand Down
Loading