Skip to content

Commit c4be6d3

Browse files
Offer access to refresh credentals outside the request authenticator (#26)
* Updating request authenticator to provide an option telling it to refresh the credential when pulling it from the authenticator object. This provides a way to obtain a refreshed token without depending on using the authenticator to make an application call. Drive-by improvements to type hinting.
1 parent 1a84956 commit c4be6d3

File tree

3 files changed

+33
-15
lines changed

3 files changed

+33
-15
lines changed

src/planet_auth/oidc/request_authenticator.py

+24-8
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import jwt
1616
import time
17-
from typing import Dict
17+
from typing import Dict, Optional
1818

1919
import planet_auth.logging.auth_logger
2020
from planet_auth.credential import Credential
@@ -79,45 +79,55 @@ def _load(self):
7979

8080
def _refresh(self):
8181
if self._auth_client:
82+
# TODO: cleanup? We did this before we wrote "update_credential_data()"
83+
# We maybe should just be using that now. Taking that clean-up slow
84+
# since it may have interactions with derived code.
85+
# It seems to me we should be able to collapse the credential.save()
86+
# calls in this file with the superclass.
8287
new_credentials = self._auth_client.refresh(self._credential.refresh_token())
8388
new_credentials.set_path(self._credential.path())
8489
new_credentials.set_storage_provider(self._credential.storage_provider())
8590
new_credentials.save()
8691

92+
# self.update_credential(new_credential=new_credentials)
8793
self._credential = new_credentials
8894
self._load()
8995

90-
def pre_request_hook(self):
96+
def _refresh_if_needed(self):
9197
# Reload the file before refreshing. Another process might
9298
# have done it for us, and save us the network call.
9399
#
94100
# Also, if refresh tokens are configured to be one time use,
95-
# we want a fresh refresh token. Stale refresh tokens are
96-
# invalid in this case.
101+
# we want a fresh refresh token. Stale refresh tokens may be
102+
# invalid depending on server configuration.
97103
#
98104
# Also, it's possible that we have a valid refresh token,
99105
# but not an access token. When that's true, we should
100106
# try to cash in the refresh token.
101107
#
102108
# If everything fails, continue with what we have. Let the API
103109
# we are calling decide if it's good enough.
104-
if int(time.time()) > self._refresh_at:
110+
now = int(time.time())
111+
if now > self._refresh_at:
105112
try:
106113
self._load()
107114
except Exception as e: # pylint: disable=broad-exception-caught
108115
auth_logger.warning(
109116
msg="Error loading auth token. Continuing with old auth token. Load error: " + str(e)
110117
)
111-
if int(time.time()) > self._refresh_at:
118+
if now > self._refresh_at:
112119
try:
113120
self._refresh()
114121
except Exception as e: # pylint: disable=broad-exception-caught
115122
auth_logger.warning(
116123
msg="Error refreshing auth token. Continuing with old auth token. Refresh error: " + str(e)
117124
)
125+
126+
def pre_request_hook(self):
127+
self._refresh_if_needed()
118128
super().pre_request_hook()
119129

120-
def update_credential(self, new_credential: Credential):
130+
def update_credential(self, new_credential: Credential) -> None:
121131
if not isinstance(new_credential, FileBackedOidcCredential):
122132
raise TypeError(
123133
f"{type(self).__name__} does not support {type(new_credential)} credentials. Use FileBackedOidcCredential."
@@ -126,7 +136,7 @@ def update_credential(self, new_credential: Credential):
126136
self._refresh_at = 0
127137
# self._load() # Mimic __init__. Don't load, let that happen JIT.
128138

129-
def update_credential_data(self, new_credential_data: Dict):
139+
def update_credential_data(self, new_credential_data: Dict) -> None:
130140
# This is more different than update_credential() than it may
131141
# appear. Inherent in being passed a Credential in update_credential()
132142
# is that it may not yet be loaded from disk, and so deferring
@@ -137,6 +147,11 @@ def update_credential_data(self, new_credential_data: Dict):
137147
super().update_credential_data(new_credential_data=new_credential_data)
138148
self._load()
139149

150+
def credential(self, refresh_if_needed: bool = False) -> Optional[Credential]:
151+
if refresh_if_needed:
152+
self._refresh_if_needed()
153+
return super().credential()
154+
140155

141156
class RefreshOrReloginOidcTokenRequestAuthenticator(RefreshingOidcTokenRequestAuthenticator):
142157
"""
@@ -171,5 +186,6 @@ def _refresh(self):
171186
new_credentials.set_storage_provider(self._credential.storage_provider())
172187
new_credentials.save()
173188

189+
# self.update_credential(new_credential=new_credentials)
174190
self._credential = new_credentials
175191
self._load()

src/planet_auth/planet_legacy/request_authenticator.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from planet_auth.credential import Credential
1516
from planet_auth.planet_legacy.legacy_api_key import FileBackedPlanetLegacyApiKey
1617
from planet_auth.request_authenticator import CredentialRequestAuthenticator
1718

@@ -32,7 +33,7 @@ def __init__(self, planet_legacy_credential: FileBackedPlanetLegacyApiKey, **kwa
3233
def pre_request_hook(self):
3334
self._token_body = self._api_key_file.legacy_api_key()
3435

35-
def update_credential(self, new_credential):
36+
def update_credential(self, new_credential: Credential) -> None:
3637
if not isinstance(new_credential, FileBackedPlanetLegacyApiKey):
3738
raise TypeError(
3839
f"{type(self).__name__} does not support {type(new_credential)} credentials. Use FileBackedPlanetLegacyApiKey."

src/planet_auth/request_authenticator.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
from abc import abstractmethod, ABC
16-
from typing import Dict
16+
from typing import Dict, Optional
1717

1818
import httpx
1919
import requests.auth
@@ -94,7 +94,7 @@ def __init__(self, credential: Credential = None, **kwargs):
9494
super().__init__(**kwargs)
9595
self._credential = credential
9696

97-
def update_credential(self, new_credential: Credential):
97+
def update_credential(self, new_credential: Credential) -> None:
9898
"""
9999
Update the request authenticator with a new credential.
100100
It is the job of a derived class to know how to map the contents
@@ -108,7 +108,7 @@ def update_credential(self, new_credential: Credential):
108108
# requests.
109109
self._token_body = None
110110

111-
def update_credential_data(self, new_credential_data: Dict):
111+
def update_credential_data(self, new_credential_data: Dict) -> None:
112112
"""
113113
Provide raw data that should be used to update the Credential
114114
object used to authenticate requests. This information will
@@ -130,13 +130,14 @@ def update_credential_data(self, new_credential_data: Dict):
130130
# requests.
131131
self._token_body = None
132132

133-
def credential(self):
133+
def credential(self, refresh_if_needed: bool = False) -> Optional[Credential]:
134134
"""
135135
Return the current credential.
136136
137137
This may not be the credential the authenticator was constructed with.
138138
Request Authenticators are free to refresh credentials depending in the
139-
needs of the implementation.
139+
needs of the implementation. This may happen upon this request,
140+
or may happen as a side effect of RequestAuthenticator operations.
140141
"""
141142
return self._credential
142143

@@ -162,7 +163,7 @@ class SimpleInMemoryRequestAuthenticator(CredentialRequestAuthenticator):
162163
def pre_request_hook(self):
163164
pass
164165

165-
def update_credential(self, new_credential: Credential):
166+
def update_credential(self, new_credential: Credential) -> None:
166167
auth_logger.warning(msg="Generic SimpleInMemoryRequestAuthenticator ignores update_credential()")
167168

168169

0 commit comments

Comments
 (0)