The HaApiV5 which inherit from ApiV5Client which itself is composed of the OAuth2Api is supposed to handled the token refreshing itself.
In ApiV5Client.call:
def call(
self,
sub_path: str,
params: dict = None,
method: str = "GET",
data: dict = None,
json: dict = None,
headers: dict = None,
include_auth: bool = True,
) -> Response:
"""Manage all api calls. It also handle re-authentication if necessary."""
self.log.debug(f"Call : {method} : {sub_path}")
url, headers, data, json, params = self.prepare_request(
sub_path, headers, data, json, params, include_auth
)
try:
result = self.execute_request(url, method, headers, data, json, params)
return result
except ApiV5Unauthorized:
self.log.warning("401 Unauthorized response to API request.")
if self.oauth.access_token:
self.log.info("Refreshing access token")
self.oauth.refresh_tokens()
else:
self.log.info("Get access token")
self.oauth.get_token()
return self.call(
sub_path, params=params, method=method, data=data, headers=headers
)
A call is tryed and if an Exception is raised it refresh/get the token it if it exists/is None.
However it returns itself, recursively, after refreshing, but with the old headers which embeds the old token, since the line
...
url, headers, data, json, params = self.prepare_request(
sub_path, headers, data, json, params, include_auth
)
...
because prepare_request returns the token in the headers object.
Now in prepare_request the new token is gotten in
self.auth = (
{"Authorization": f"Bearer {self.oauth.access_token}"}
if include_auth
else {}
)
but then overwritten by the old one stored in headers
all_headers = {**self.header(), **self.auth, **headers}
thus the old token is used the header request and it fails its task of handling token refresh
This diff should fix the problem:
self.log.debug(f"Call : {method} : {sub_path}")
- url, headers, data, json, params = self.prepare_request(
+ url, all_headers, data, json, params = self.prepare_request(
sub_path, headers, data, json, params, include_auth
)
try:
- result = self.execute_request(url, method, headers, data, json, params)
+ result = self.execute_request(url, method, all_headers, data, json, params)
return result
except ApiV5Unauthorized:
self.log.warning("401 Unauthorized response to API request.")
if self.oauth.access_token:
self.log.info("Refreshing access token")
self.oauth.refresh_tokens()
else:
self.log.info("Get access token")
self.oauth.get_token()
return self.call(
sub_path, params=params, method=method, data=data, headers=headers
)
because it doesn't overwrite the header var and then when return recursively it returns the user-defined headers, not embedding the old token.
The
HaApiV5which inherit fromApiV5Clientwhich itself is composed of theOAuth2Apiis supposed to handled the token refreshing itself.In
ApiV5Client.call:A call is
tryed and if an Exception is raised it refresh/get the token it if it exists/is None.However it returns itself, recursively, after refreshing, but with the old headers which embeds the old token, since the line
... url, headers, data, json, params = self.prepare_request( sub_path, headers, data, json, params, include_auth ) ...because
prepare_requestreturns the token in theheadersobject.Now in
prepare_requestthe new token is gotten inbut then overwritten by the old one stored in
headersthus the old token is used the header request and it fails its task of handling token refresh
This diff should fix the problem:
self.log.debug(f"Call : {method} : {sub_path}") - url, headers, data, json, params = self.prepare_request( + url, all_headers, data, json, params = self.prepare_request( sub_path, headers, data, json, params, include_auth ) try: - result = self.execute_request(url, method, headers, data, json, params) + result = self.execute_request(url, method, all_headers, data, json, params) return result except ApiV5Unauthorized: self.log.warning("401 Unauthorized response to API request.") if self.oauth.access_token: self.log.info("Refreshing access token") self.oauth.refresh_tokens() else: self.log.info("Get access token") self.oauth.get_token() return self.call( sub_path, params=params, method=method, data=data, headers=headers )because it doesn't overwrite the
headervar and then when return recursively it returns the user-defined headers, not embedding the old token.