Skip to content

Commit 833d978

Browse files
committed
read access token expiration date from config and update it
1 parent 71ab084 commit 833d978

File tree

2 files changed

+23
-16
lines changed

2 files changed

+23
-16
lines changed

airbyte-cdk/python/airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,6 @@ def __init__(
104104
connector_config: Mapping[str, Any],
105105
token_refresh_endpoint: str,
106106
scopes: List[str] = None,
107-
token_expiry_date: pendulum.DateTime = None,
108-
token_expiry_date_format: str = None,
109107
access_token_name: str = "access_token",
110108
expires_in_name: str = "expires_in",
111109
refresh_token_name: str = "refresh_token",
@@ -115,14 +113,14 @@ def __init__(
115113
client_secret_config_path: Sequence[str] = ("credentials", "client_secret"),
116114
access_token_config_path: Sequence[str] = ("credentials", "access_token"),
117115
refresh_token_config_path: Sequence[str] = ("credentials", "refresh_token"),
116+
access_token_expiration_datetime_config_path: Sequence[str] = ("credentials", "access_token_expiration_datetime"),
118117
):
119118
"""
120119
121120
Args:
122121
connector_config (Mapping[str, Any]): The full connector configuration
123122
token_refresh_endpoint (str): Full URL to the token refresh endpoint
124123
scopes (List[str], optional): List of OAuth scopes to pass in the refresh token request body. Defaults to None.
125-
token_expiry_date (pendulum.DateTime, optional): Datetime at which the current token will expire. Defaults to None.
126124
access_token_name (str, optional): Name of the access token field, used to parse the refresh token response. Defaults to "access_token".
127125
expires_in_name (str, optional): Name of the name of the field that characterizes when the current access token will expire, used to parse the refresh token response. Defaults to "expires_in".
128126
refresh_token_name (str, optional): Name of the name of the refresh token field, used to parse the refresh token response. Defaults to "refresh_token".
@@ -132,11 +130,13 @@ def __init__(
132130
client_secret_config_path (Sequence[str]): Dpath to the client_secret field in the connector configuration. Defaults to ("credentials", "client_secret").
133131
access_token_config_path (Sequence[str]): Dpath to the access_token field in the connector configuration. Defaults to ("credentials", "access_token").
134132
refresh_token_config_path (Sequence[str]): Dpath to the refresh_token field in the connector configuration. Defaults to ("credentials", "refresh_token").
133+
access_token_expiration_datetime_config_path (Sequence[str]): Dpath to the access_token_expiration_datetime field in the connector configuration. Defaults to ("credentials", "access_token_expiration_datetime").
135134
"""
136135
self._client_id_config_path = client_id_config_path
137136
self._client_secret_config_path = client_secret_config_path
138137
self._access_token_config_path = access_token_config_path
139138
self._refresh_token_config_path = refresh_token_config_path
139+
self._access_token_expiration_datetime_config_path = access_token_expiration_datetime_config_path
140140
self._refresh_token_name = refresh_token_name
141141
self._connector_config = observe_connector_config(connector_config)
142142
self._validate_connector_config()
@@ -145,13 +145,12 @@ def __init__(
145145
self.get_client_id(),
146146
self.get_client_secret(),
147147
self.get_refresh_token(),
148-
scopes,
149-
token_expiry_date,
150-
token_expiry_date_format,
151-
access_token_name,
152-
expires_in_name,
153-
refresh_request_body,
154-
grant_type,
148+
scopes=scopes,
149+
token_expiry_date=self.get_access_token_expiration_datetime(),
150+
access_token_name=access_token_name,
151+
expires_in_name=expires_in_name,
152+
refresh_request_body=refresh_request_body,
153+
grant_type=grant_type
155154
)
156155

157156
def _validate_connector_config(self):
@@ -164,6 +163,7 @@ def _validate_connector_config(self):
164163
(self._client_id_config_path, self.get_client_id, "client_id_config_path"),
165164
(self._client_secret_config_path, self.get_client_secret, "client_secret_config_path"),
166165
(self._refresh_token_config_path, self.get_refresh_token, "refresh_token_config_path"),
166+
(self._access_token_expiration_datetime_config_path, self.get_access_token_expiration_datetime, "access_token_expiration_datetime_config_path"),
167167
]:
168168
try:
169169
assert getter()
@@ -184,19 +184,24 @@ def get_client_secret(self) -> str:
184184
def get_refresh_token(self) -> str:
185185
return dpath.util.get(self._connector_config, self._refresh_token_config_path)
186186

187+
def get_access_token_expiration_datetime(self) -> pendulum.DateTime:
188+
return pendulum.parse(dpath.util.get(self._connector_config, self._access_token_expiration_datetime_config_path))
189+
187190

188-
def _update_config_with_access_and_refresh_tokens(self, new_access_token: str, new_refresh_token: str):
191+
def _update_config_with_access_and_refresh_tokens(self, new_access_token: str, new_refresh_token: str, new_access_token_expiration_datetime: pendulum.DateTime):
189192
"""Update the connector configuration with new access and refresh token values.
190193
The mutation of the connector_config object will emit Airbyte control messages.
191194
192195
Args:
193196
new_access_token (str): The new access token value.
194197
new_refresh_token (str): The new refresh token value.
198+
new_access_token_expiration_datetime (pendulum.DateTime): The new access token expiration date.
195199
"""
196-
# TODO alafanechere this will sequentially emit two control messages.
200+
# TODO alafanechere this will sequentially emit three control messages.
197201
# We should rework the observer/config mutation logic if we want to have atomic config updates in a single control message.
198202
dpath.util.set(self._connector_config, self._access_token_config_path, new_access_token)
199203
dpath.util.set(self._connector_config, self._refresh_token_config_path, new_refresh_token)
204+
dpath.util.set(self._connector_config, self._access_token_expiration_datetime_config_path, new_access_token_expiration_datetime)
200205

201206

202207
def get_access_token(self) -> str:
@@ -206,11 +211,10 @@ def get_access_token(self) -> str:
206211
str: The current access_token, updated if it was previously expired.
207212
"""
208213
if self.token_has_expired():
209-
t0 = pendulum.now()
210214
new_access_token, access_token_expires_in, new_refresh_token = self.refresh_access_token()
211215
self.access_token = new_access_token
212-
self.set_token_expiry_date(t0, access_token_expires_in)
213-
self._update_config_with_access_and_refresh_tokens(new_access_token, new_refresh_token)
216+
self.set_token_expiry_date(pendulum.now("UTC"), access_token_expires_in)
217+
self._update_config_with_access_and_refresh_tokens(new_access_token, new_refresh_token, self.get_token_expiry_date())
214218
return self.access_token
215219

216220
def refresh_access_token(self) -> Tuple[str, int, str]:

airbyte-cdk/python/unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import json
66
import logging
77

8+
import freezegun
89
import pendulum
910
import pytest
1011
import requests
@@ -188,6 +189,7 @@ def connector_config(self):
188189
"refresh_token": "my_refresh_token",
189190
"client_id": "my_client_id",
190191
"client_secret": "my_client_secret",
192+
"access_token_expiration_datetime": "2022-12-31T00:00:00+00:00"
191193
}
192194
}
193195

@@ -209,6 +211,7 @@ def test_init_with_invalid_config(self, invalid_connector_config):
209211
token_refresh_endpoint="foobar",
210212
)
211213

214+
@freezegun.freeze_time("2022-12-31")
212215
def test_get_access_token(self, capsys, mocker, connector_config):
213216
authenticator = SingleUseRefreshTokenOauth2Authenticator(
214217
connector_config,
@@ -222,7 +225,7 @@ def test_get_access_token(self, capsys, mocker, connector_config):
222225
expected_new_config = connector_config.copy()
223226
expected_new_config["credentials"]["access_token"] = "new_access_token"
224227
expected_new_config["credentials"]["refresh_token"] = "new_refresh_token"
225-
228+
expected_new_config["credentials"]["access_token_expiration_datetime"] = "2022-12-31T00:00:42+00:00"
226229
assert airbyte_message["control"]["connectorConfig"]["config"] == expected_new_config
227230
assert authenticator.access_token == access_token == "new_access_token"
228231
assert authenticator.get_refresh_token() == "new_refresh_token"

0 commit comments

Comments
 (0)