nordicopen / pyeasee

Easee EV charger API python library
MIT License
39 stars 11 forks source link

Authorization issues #60

Closed ztamas83 closed 2 years ago

ztamas83 commented 2 years ago

Hello! I have an automation in Home Assistant that should turn off/on the Easee box based on other equipments status in the house. The HA integration uses this pyeasee library and the automation fails for time to time.

Issue 1: 2022-01-04 14:31:55 DEBUG (MainThread) [pyeasee.easee] POST: /api/chargers/<masked>/settings ({'json': {'enabled': True}}) 2022-01-04 14:31:55 DEBUG (MainThread) [pyeasee.easee] verify_updated_token: 2022-01-04 13:47:25.169271, 2022-01-04 14:31:55.149989, True 2022-01-04 14:31:55 DEBUG (MainThread) [pyeasee.easee] Refreshing access token 2022-01-04 14:31:57 DEBUG (MainThread) [pyeasee.easee] TOKEN: {'accessToken': '<masked>', 'expiresIn': 86400, 'accessClaims': ['User'], 'tokenType': 'Bearer', 'refreshToken': '<masked>'} 2022-01-04 14:31:57 DEBUG (MainThread) [pyeasee.easee] Unautorized (401: None https://api.easee.cloud/api/chargers/<masked>/settings) 2022-01-04 14:31:57 DEBUG (MainThread) [pyeasee.easee] Re authorizing due to 401 2022-01-04 14:31:57 DEBUG (MainThread) [pyeasee.easee] getting token for user: <masked>@gmail.com 2022-01-04 14:31:57 DEBUG (MainThread) [pyeasee.easee] TOKEN: {'accessToken': '<masked>', 'expiresIn': 86400, 'accessClaims': ['User'], 'tokenType': 'Bearer', 'refreshToken': '<masked>'} 2022-01-04 14:31:57 DEBUG (MainThread) [pyeasee.easee] Unautorized (401: None https://api.easee.cloud/api/chargers/<masked>/settings)

I have verified the tokens via jwt.io:

"nbf": 1641303117, "exp": 1641389517, "iat": 1641303117,

So they were valid. Despite this the Easee service seems to reply with 401 - Unauthorized. I assume this is a problem with the Easee API. Do you have any contact with them?

olalid commented 2 years ago

Yes, we are aware of this and we do have contacts whom we can discuss it with. To me it seems like when a token is refreshed there is some delay until the API accepts it as valid. I can see the same thing in my logs from time to time, and I can see that the next time the token is used (about 20 seconds later), it is accepted.

olalid commented 2 years ago

Actually it might just be so that the refresh_token API is simply not working at all.

ztamas83 commented 2 years ago

The API is probably working, because after the refresh the automation works again using the previously fetched token. Do you think it is worth a shot to trigger full authentication instead of refresh? Is that even possible?

2022-01-04 16:47:54 INFO (MainThread) [homeassistant.components.automation.turn_on_charger_when_dishwasher_ready] Turn on charger when dishwasher ready: Running automation actions
2022-01-04 16:47:54 INFO (MainThread) [homeassistant.components.automation.turn_on_charger_when_dishwasher_ready] Turn on charger when dishwasher ready: Executing step call service
2022-01-04 16:47:54 DEBUG (MainThread) [pyeasee.easee] POST: /api/chargers/EHxxxx/settings ({'json': {'enabled': True}})
2022-01-04 16:47:54 DEBUG (MainThread) [pyeasee.easee] verify_updated_token: 2022-01-05 14:31:57.268733, 2022-01-04 16:47:54.412977, False
2022-01-04 16:47:54 INFO (MainThread) [homeassistant.components.automation.turn_on_charger_when_dishwasher_ready] Turn on charger when dishwasher ready: Executing step call service
2022-01-04 16:47:54 DEBUG (MainThread) [pyeasee.easee] POST: /api/chargers/EHxxxx/settings ({'json': {'smartCharging': True}})
2022-01-04 16:47:54 DEBUG (MainThread) [pyeasee.easee] verify_updated_token: 2022-01-05 14:31:57.268733, 2022-01-04 16:47:54.874703, False
olalid commented 2 years ago

Well, what actually happens in our code is that we first call the refresh_token API because the token has expired. It returns a new token, but that token is not accepted when we try to call the API you wanted to call (/api/chargers//settings in your case). That API returns 401 indicating that it is not accepting the token, our code then falls back to using the login API to generate a new token, to make sure a valid token is available for future calls. So the token used the next time is not the token from the refresh_token API, it is from the new login.

I did some manual testing as well, and it looks like the token returned from refresh_token is not accepted even long time after the API was called, and the old token is still valid (unless it has expired, I assume). So to me refresh_token seems broken...

I want to hear Easees feeback on this before we make any changes, but simply using the login API (i.e. full authentication) as you suggest could be a workaround. There is no way to trigger that from user code though, it must be changed in the lib. I prefer if Easee fixes the refresh_token API on their side though.

ztamas83 commented 2 years ago

Well, what actually happens in our code is that we first call the refresh_token API because the token has expired. It returns a new token, but that token is not accepted when we try to call the API you wanted to call (/api/chargers//settings in your case). That API returns 401 indicating that it is not accepting the token, our code then falls back to using the login API to generate a new token, to make sure a valid token is available for future calls. So the token used the next time is not the token from the refresh_token API, it is from the new login.

I did some manual testing as well, and it looks like the token returned from refresh_token is not accepted even long time after the API was called, and the old token is still valid (unless it has expired, I assume). So to me refresh_token seems broken...

I want to hear Easees feeback on this before we make any changes, but simply using the login API (i.e. full authentication) as you suggest could be a workaround. There is no way to trigger that from user code though, it must be changed in the lib. I prefer if Easee fixes the refresh_token API on their side though.

I actually did not check that there is a fallback to full authentication. However as per my first set of logs it looks like that the new token was not accepted either, only when trying a few hours later.

In any case I close this issue now as the problem is external, unless a change in the lib is needed... But then it can be reopened or referenced. Thank you for the fast responses!

olalid commented 2 years ago

Yes, the log gives you the impression that it tries calling the settings API 2 times, but it is not actually the case, it just prints the error twice.

olalid commented 2 years ago

Actually, we found a bug in our code. https://github.com/fondberg/pyeasee/pull/61

ztamas83 commented 2 years ago

Actually, we found a bug in our code. #61

Nice one :) Was not totally unnecessary to open an issue then :)

olalid commented 2 years ago

It is always good to report bugs. :) New version of the lib is released now, and we are working on releasing a new version of the HA integration, so hopefully soon fixed.