benleb / surepy

🐾 Library & CLI to monitor and control the Pet Door & Cat Flap Connect 🚪 the Pet Feeder Connect 🍽 and the Felaqua 💦 sold by Sure Petcare
https://pypi.org/project/surepy/
MIT License
76 stars 36 forks source link

having issues logging in #199

Closed chrisdrackett closed 8 months ago

chrisdrackett commented 8 months ago

Getting the following in HASS that I assume is a surepy issue (well, a sure petcare issue really I would guess)

2023-12-10 13:30:26.544 ERROR (MainThread) [surepy.client] 🐾 �[38;2;255;26;102m·�[0m GET app.api.surehub.io/api/me/start: 401 | <ClientResponse(https://app.api.surehub.io/api/me/start) [401 Unauthorized]> 2023-12-10 13:30:28.024 DEBUG (MainThread) [surepy.client] 🐾 �[38;2;0;255;0m·�[0m GET app.api.surehub.io/api/me/start | 6 2023-12-10 13:30:28.025 DEBUG (MainThread) [homeassistant.components.surepetcare] Finished fetching surepetcare data in 1.623 seconds (success: False) 2023-12-10 13:30:28.025 WARNING (MainThread) [homeassistant.config_entries] Config entry 'Sure Petcare' for surepetcare integration could not authenticate: Invalid username/password

chrisdrackett commented 8 months ago

looks like this URL might have changed to app-api.blue.production.surehub.io

chrisdrackett commented 8 months ago

ok, I was able to login using the following URL: https://app-api.blue.production.surehub.io/api/auth/login and also use https://app-api.blue.production.surehub.io/api/device successfully so I think this is a pretty easy change. I'll submit a PR

ivo-toby commented 8 months ago

Is there anyway to force this change in hassos (eg; where can I find the integration)? I'm about to go on vacation and I really want Home-assistant to control the catflap instead of the Sure App

Hoefnix commented 8 months ago

changing just the urls don’t do the trick, at least for me. Gives me a415 error as result.

{'type': 'https://tools.ietf.org/html/rfc7231#section-6.5.13', 'title': 'Unsupported Media Type', 'status': 415, 'traceId ': '00-f9df16e657295223a3f7043349f5b64f-2199f73a6430e892-00'}

DevinRouth commented 8 months ago

Same for me—updating the URL's on the client.py and const.py files still results in the same errors (surepy.exceptions.SurePetcareError: ERROR (UN)LOCKING DEVICE - PLEASE CHECK IMMEDIATELY! or 401 errors).

Trying both the URL suggested in this issue (app-api.blue.production.surehub.io) as well as the URL suggested here (https://app-api.production.surehub.io/api/start) both continue to throw errors.

rjclocks commented 8 months ago

I turned on some of the logging info and found that the login request is returning an error: [415 Unsupported Media Type] This is consistent across all of the URLs suggested above.

Here is the detail I see from the debug logger:

DEBUG:asyncio:Using proactor: IocpProactor

DEBUG:surepy.client:initialization completed | vars(): 
{'self': <surepy.client.SureAPIClient object at 0x00000>, 
'email': 'AccountEmail', 
'password': 'AccountPassword', 
'auth_token': None, 
'api_timeout': 45, 
'session': None, 
'surepy_version': '0.8.0', 
'token': None}

DEBUG:surepy:initialization completed | vars(): 
{'self': <surepy.Surepy object at 0x00000>, 
'email': 'AccountEmail', 
'password': 'AccountPassword', 
'auth_token': None, 
'api_timeout': 45, 
'session': None}

DEBUG:surepy.client:

DEBUG:surepy.client:🐾 GET call to: https://app.api.surehub.io/api/me/start

DEBUG:surepy.client:Response from https://app.api.surehub.io/api/auth/login: 
<ClientResponse(https://app.api.surehub.io/api/auth/login) [415 Unsupported Media Type]>
<CIMultiDictProxy('Date': 'Wed, 13 Dec 2023 16:10:35 GMT',
 'Content-Type': 'application/json; charset=utf-8', 
 'Transfer-Encoding': 'chunked', 
 'Connection': 'keep-alive', 
 'Server': 'nginx', 
 'Access-Control-Allow-Origin': '*', 
 'Strict-Transport-Security': 'max-age=31536000; includeSubdomains; preload', 
 'X-Frame-Options': 'DENY', 
 'X-Content-Type-Options': 'nosniff', 
 'X-XSS-Protection': '1; mode=block')>

Following all this, I get the usual python exception error

chrisdrackett commented 8 months ago

So it looks like there are a couple things going on here that I missed:

  1. sure looks to be using blue/green deployment. This isn't something I'm super familiar with, so I'm not sure the best way to have this library deal with it.
  2. The API is expecting application/json requests. I missed this as I was using postman for my testing and this is the encoding I defaulted to.
blair287 commented 8 months ago

Think this is the end of the road? Or can it be fixed @benleb ?

cs12ag commented 8 months ago

It can definitely be fixed - I'm not sure where benleb is or what he's up to right now though. This stuff is open-source though, so others can suggest changes via PRs.

SurePetcare have always taken the line that their API isn't public, so it's going to be challenging to work out which end-point should be used. From what I've seen so far, that's the only stumbling block. The rest can probably be reverse-engineered.

chrisdrackett commented 8 months ago

might just be a matter of trying to hit blue first, and if it fails, try green ¯_(ツ)_/¯

flyize commented 8 months ago

There has to be some sort of DNS redirection to green or blue. Probably something they're doing with Route53. I don't see any other domain that's obvious in the replies though.

Hoefnix commented 8 months ago

What does this mean?

HTTPSConn ectionPool(host='app-api.green.production.surehub.io', port=443): Max retries exceeded with url: /api/auth/login (Caused by SSLError(CertificateError("hostname 'app-api.green.product ion.surehub.io' doesn't match either of 'blue.production.surehub.io', '.blue.production.su rehub.io', 'surehub.io', '.production.surehub.io', '.surehub.io', 'surepetcare.io', '.ap i.surehub.io', '*.surepetcare.io'")))

flyize commented 8 months ago

BTW, looks like blue/green isn't necessary anyway. I get a full response from this command (on Windows, hence the caret):

curl -v https://app-api.production.surehub.io/api/me/start ^
-H "Authorization: Bearer ***yourtokenhere**"
benleb commented 8 months ago

sorry guys, will have a look at this at the weekend... but from first look here, its a problem on their side where a workaround has to be built?!

About API is expecting application/json requests... the accept header is already set and when also adding content-type I get a 400 bad request :D will dig deeper at the weekend :)

cs12ag commented 8 months ago

@flyize I spent about 30 seconds trawling through JS on the surepetcare.io website to see if I could determine which API end-point was used, but the code is massively obfuscated so I gave up on that immediately.

I'm sticking with my theory that the end-point without the colours in it is the primary end-point, and some kind of load-balancing will redirect requests to blue and green end-points as required. I also think that the swagger docs on all three end-points aren't necessarily 100%; from my testing earlier on this week, the primary end-point looked like it was the only one that can issue auth tokens.

I'm also not ruling out surepetcare taking a buggy/unfinished end-point live. This isn't a below-the-belt blow targeted at surepetcare themselves; we've all pushed stuff to production that may have resulted in bugs revealing themselves. ;)

benleb commented 8 months ago

@flyize I spent about 30 seconds trawling through JS on the surepetcare.io website to see if I could determine which API end-point was used, but the code is massively obfuscated so I gave up on that immediately.

probably just a typo, but the (for us) relevant page is https://surehub.io (js is also obfuscated here)

AlexC commented 8 months ago

I've just taken a look at the HTTP traffic through a proxy, and the endpoint to use is https://app-api.production.surehub.io/api/. Anything related to blue/green would be a blue/green deployment of their API and not endpoints that should be used directly (normally).

For the issue that I've been facing (unable to set Pet location in Home Assistant, see https://github.com/home-assistant/core/issues/105730), here is the request that the application makes to do the same:

METHOD: POST
URL: https://app-api.production.surehub.io/api/pet/376871/position
HEADERS
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Authorization: Bearer ....
Connection: keep-alive
Content-Length: 41
Content-Type: application/json
Host: app-api.production.surehub.io
Origin: http://localhost
Referer: http://localhost/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Linux; Android 14; sdk_gphone64_x86_64 Build/UE1A.230829.036; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.136 Mobile Safari/537.36
X-App-Version: 3.3.4
X-Device-Id: aae2836efd73a6d1
X-Requested-With: com.sureflap.surepetcare

So we can see https://app-api.production.surehub.io/api/ vs https://app.api.surehub.io/ (this library). However, that may not be the full underlying issue here as the old URL still works.

The issue that I have been facing with Home Assistant is that I'm getting back a HTTP 415 response, which indicates an issue with the Content-Type header. Well, looking at the code for set_pet_location the Content-Type header is never set, which is the problem.

Here's an example using no Content-Type and the old endpoint:

curl --location 'https://app.api.surehub.io/api/pet/376871/position' \
--header 'Authorization: Bearer ...' \
--data '{
  "where": 2,
  "since": "2023-12-14 16:07:38"
}'

{"type":"https://tools.ietf.org/html/rfc7231#section-6.5.13","title":"Unsupported Media Type","status":415,"traceId":"00-25b716e8dfdafaa45e99b517096c7646-461a978e865db0b2-00"}

And with the correct Content-Type:

curl --location 'https://app.api.surehub.io/api/pet/376871/position' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ...' \
--data '{
  "where": 2,
  "since": "2023-12-14 16:07:38"
}'

{"data":{"id":3024813240,"pet_id":376871,"tag_id":409270,"user_id":1234,"where":2,"since":"2023-12-14T16:07:38+00:00"}}

As you can see, it works just fine (even with the assumed old URL). I can work on a little PR for this to get it fixed up if you like?

AlexC commented 8 months ago

@benleb I'm unfamiliar with what the older API looked like, but from what I can see it did not expect a JSON payload before, is that correct? If so, the issue here appears to be that the newer version of the Surepet API speaks JSON, and should be rather trivial to use json instead of data kwarg in such calls:

raw_response: aiohttp.ClientResponse = await session.post(
                url=AUTH_RESOURCE, json=authentication_data, headers=self._generate_headers()
            )
benleb commented 8 months ago

ah nice @AlexC - can confirm that works for me too! 🎉 👍 (btw, just adding the content-type header and sending data leads to the 400er error I got before -_-)

and what the API really expects or expected... we actually never really knew :D We never got in good contact with sure petcare (we think they dont want to due to some reason :/) and just "reverse engineered" the calls from the app and so on... afaik the headers initially were a copy of what the app sent, but that was years ago...

and about a MR, sure :) 👍 if nobody does it until the weekend, I will :) 👍

benleb commented 8 months ago

ah there already is one :D https://github.com/benleb/surepy/pull/201 - from sure petcare itself <3 thanks!

but content-type header is missing in that one

AlexC commented 8 months ago

@benleb Yeah, I wasn't aware that the Python lib was not sending a JSON payload, so just setting the header to application/json would not be the only fix as the payload would also need to be JSON instead of formdata.

I did receive a notification/message from the official Surepet application the other day, saying that I must update the application to the latest otherwise it will stop working. That ties into the failures that we're seeing here now, so I can only assume they've made some breaking changes to their API (so that it now only speaks JSON, most likely)

The linked PR looks good to me, as the json kwarg would also add in the Content-Type: application/json header so no further changes needed there

rjclocks commented 8 months ago

I know you have closed this, but I also had to initialise the json object to get it to work (shown below).

async def call(
    self,
    method: str,
    resource: str,
    data: dict[str, Any] | None = None,
    json: dict[str, Any] | None = None,
    second_try: bool = False,
    **_: Any,
) -> dict[str, Any] | None:
chrisdrackett commented 7 months ago

Just a heads up this still isn't working for me either, I'm still getting a 401:

2023-12-27 08:47:12.514 ERROR (MainThread) [surepy.client] 🐾 · GET app.api.surehub.io/api/me/start: 401 | <ClientResponse(https://app.api.surehub.io/api/me/start) [401 Unauthorized]>
rjclocks commented 7 months ago

@chrisdrackett - I would double check that your credentials work correctly on the main Sure Petcare app webpage. Since the update, all my issues with login have been corrected.

chrisannen commented 7 months ago

@chrisdrackett

I had the same problem. Delete the integration on HA and reinstall it. After that it should work.

But don't forget to update to 2023.12.4 first!