Skip to content

Commit bfd09de

Browse files
committed
Fixed error on connection username setter
Refactored refresh_token method into multiple parts simplifying the code. Now `refresh_token` only refreshes the token as it's expected New `update_session_auth_header` method Fixed `should_refresh_token` as it now accepts the username param. Fixed docstring and type annotations Updated lockable firestore backend to version 2.1 Pending LockableFileSystemTokenBackend
1 parent 42b03ac commit bfd09de

File tree

3 files changed

+210
-187
lines changed

3 files changed

+210
-187
lines changed

O365/connection.py

+75-63
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ def username(self, username: Optional[str]) -> None:
626626
if self.session is not None:
627627
access_token = self.token_backend.get_access_token(username=username)
628628
if access_token is not None:
629-
self.session.headers.update({"Authorization": f"Bearer {access_token}"})
629+
self.update_session_auth_header(access_token=access_token["secret"])
630630
else:
631631
# if we can't find an access token for the current user, then remove the auth header from the session
632632
if "Authorization" in self.session.headers:
@@ -806,8 +806,7 @@ def request_token(
806806

807807
# Update the session headers if the session exists
808808
if self.session is not None:
809-
access_token = result["access_token"]
810-
self.session.headers.update({"Authorization": f"Bearer {access_token}"})
809+
self.update_session_auth_header(access_token=result["access_token"])
811810

812811
if store_token:
813812
self.token_backend.save_token()
@@ -878,80 +877,92 @@ def get_naive_session(self) -> Session:
878877

879878
return naive_session
880879

881-
def refresh_token(self) -> bool:
882-
"""
883-
Refresh the OAuth authorization token.
884-
This will be called automatically when the access token
885-
expires, however, you can manually call this method to
886-
request a new refresh token.
887-
888-
:return bool: Success / Failure
889-
"""
890-
if self.session is None:
891-
self.session = self.get_session(load_token=True)
880+
def update_session_auth_header(self, access_token: Optional[str] = None) -> None:
881+
""" Will update the internal request session auth header with an access token"""
882+
if access_token is None:
883+
# try to get the access_token from the backend
884+
access_token_dict = self.token_backend.get_access_token(
885+
username=self.username
886+
) or {}
887+
access_token = access_token_dict.get("secret")
888+
if access_token is None:
889+
# at this point this is an error.
890+
raise RuntimeError("Tried to update the session auth header but no access "
891+
"token was provided nor found in the token backend.")
892+
log.debug("New access token set into session auth header")
893+
self.session.headers.update(
894+
{"Authorization": f"Bearer {access_token}"}
895+
)
892896

897+
def _try_refresh_token(self) -> bool:
898+
"""Internal method to check try to update the refresh token"""
899+
# first we check if we can acquire a new refresh token
893900
token_refreshed = False
894-
895901
if (
896902
self.token_backend.token_is_long_lived(username=self.username)
897903
or self.auth_flow_type == "credentials"
898904
):
899-
should_rt = self.token_backend.should_refresh_token(self)
905+
# then we ask the token backend if we should refresh the token
906+
log.debug("Asking the token backend if we should refresh the token")
907+
should_rt = self.token_backend.should_refresh_token(con=self, username=self.username)
908+
log.debug(f"Token Backend answered {should_rt}")
900909
if should_rt is True:
901910
# The backend has checked that we can refresh the token
902-
log.debug("Refreshing access token")
903-
904-
# This will set the connection scopes from the scopes set in the stored refresh or access token
905-
scopes = self.token_backend.get_token_scopes(
906-
username=self.username, remove_reserved=True
907-
)
908-
909-
result = self.msal_client.acquire_token_silent_with_error(
910-
scopes=scopes,
911-
account=self.msal_client.get_accounts(username=self.username)[0],
912-
)
913-
if result is None:
914-
raise RuntimeError("There is no refresh token to refresh")
915-
elif "error" in result:
916-
raise RuntimeError(
917-
f'Refresh token operation failed: {result["error"]}'
918-
)
919-
elif "access_token" in result:
920-
# refresh done, update authorization header
921-
token_refreshed = True
922-
self.session.headers.update(
923-
{"Authorization": f'Bearer {result["access_token"]}'}
924-
)
925-
log.debug(
926-
f"New oauth token fetched by refresh method for username: {self.username}"
927-
)
911+
return self.refresh_token()
928912
elif should_rt is False:
929-
# the token was refreshed by another instance and updated into this instance,
930-
# so: update the session token and retry the request again
931-
access_token = self.token_backend.get_access_token(
932-
username=self.username
933-
)
934-
if access_token:
935-
self.session.headers.update(
936-
{"Authorization": f'Bearer {access_token["secret"]}'}
937-
)
938-
else:
939-
raise RuntimeError(
940-
"Can't get access token refreshed by another instance."
941-
)
913+
# The token was refreshed by another instance and 'should_refresh_token' has updated it into the
914+
# backend cache. So, update the session token and retry the request again
915+
self.update_session_auth_header()
916+
return True
942917
else:
943-
# the refresh was performed by the token backend.
944-
pass
918+
# the refresh was performed by the token backend, and it has updated all the data
919+
return True
945920
else:
946921
log.error(
947-
'You can not refresh an access token that has no "refresh_token" available.'
948-
'Include "offline_access" permission to get a "refresh_token"'
922+
"You can not refresh an access token that has no 'refresh_token' available."
923+
"Include 'offline_access' permission to get a 'refresh_token'."
949924
)
950925
return False
951926

952-
if token_refreshed and self.store_token_after_refresh:
953-
self.token_backend.save_token()
954-
return True
927+
def refresh_token(self) -> bool:
928+
"""
929+
Refresh the OAuth authorization token.
930+
This will be called automatically when the access token
931+
expires, however, you can manually call this method to
932+
request a new refresh token.
933+
934+
:return bool: Success / Failure
935+
"""
936+
log.debug("Refreshing access token")
937+
938+
if self.session is None:
939+
self.session = self.get_session(load_token=True)
940+
941+
# This will set the connection scopes from the scopes set in the stored refresh or access token
942+
scopes = self.token_backend.get_token_scopes(
943+
username=self.username, remove_reserved=True
944+
)
945+
946+
# call the refresh!
947+
result = self.msal_client.acquire_token_silent_with_error(
948+
scopes=scopes,
949+
account=self.msal_client.get_accounts(username=self.username)[0],
950+
)
951+
if result is None:
952+
raise RuntimeError("There is no refresh token to refresh")
953+
elif "error" in result:
954+
raise RuntimeError(f"Refresh token operation failed: {result['error']}")
955+
elif "access_token" in result:
956+
log.debug(
957+
f"New oauth token fetched by refresh method for username: {self.username}"
958+
)
959+
# refresh done, update authorization header
960+
self.update_session_auth_header(access_token=result["access_token"])
961+
962+
if self.store_token_after_refresh:
963+
self.token_backend.save_token()
964+
return True
965+
return False
955966

956967
def _check_delay(self) -> None:
957968
"""Checks if a delay is needed between requests and sleeps if True"""
@@ -1117,7 +1128,8 @@ def oauth_request(self, url: str, method: str, **kwargs) -> Response:
11171128
except TokenExpiredError as e:
11181129
# refresh and try again the request!
11191130
try:
1120-
if self.refresh_token():
1131+
# try to refresh the token and/or follow token backend answer on 'should_refresh_token'
1132+
if self._try_refresh_token():
11211133
return self._internal_request(self.session, url, method, **kwargs)
11221134
else:
11231135
raise e

0 commit comments

Comments
 (0)