Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recent garmin API change may have broken sample code expectations and file uploads #73

Closed
cbarbara opened this issue Nov 25, 2024 · 32 comments

Comments

@cbarbara
Copy link

cbarbara commented Nov 25, 2024

I had an app running for a while where I followed your sample code, ex:

    garth.resume("/tmp/garth")
    try:
        garth.client.username
    except GarthException as e:
        print(e)
        print("Session is expired. You'll need to log in again.")
        raise e

And it started breaking with the following error:

[ERROR] AssertionError
Traceback (most recent call last):
  File "/var/task/lambda.py", line 39, in lambda_handler
    garth.client.username
  File "/var/task/garth/http.py", line 109, in username
    return self.profile["userName"]
  File "/var/task/garth/http.py", line 104, in profile
    assert isinstance(self._profile, dict)

I traced that back to the code trying to call the URL https://connect.garmin.com/userprofile-service/userprofile/socialProfile, I curl'd that url myself and it now spits back the error:

curl 'https://connect.garmin.com/userprofile-service/userprofile/socialProfile'  ....... 

{"clientMessage":"Reference Error ID in error logs for further information","errorId":"9feffbc5-26dc-49eb-b8f0-4049aeded9c3","error":"NotFoundException"}

For testing I commented out the line to call garth.client.username, and my app fails to upload the activity to garmin with a new error:

Traceback (most recent call last):
  File "/var/task/lambda.py", line 50, in lambda_handler
    uploaded = garth.client.upload(f)
  File "/var/task/garth/http.py", line 187, in upload
    return self.connectapi(
  File "/var/task/garth/http.py", line 171, in connectapi
    resp = self.request(method, "connectapi", path, api=True, **kwargs)
  File "/var/task/garth/http.py", line 131, in request
    self.last_resp = self.sess.request(
  File "/var/task/requests/sessions.py", line 589, in request
    resp = self.send(prep, **send_kwargs)
  File "/var/task/requests/sessions.py", line 746, in send
    r.content
  File "/var/task/requests/models.py", line 902, in content
    self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b""
  File "/var/task/requests/models.py", line 822, in generate
    raise ChunkedEncodingError(e)
@cbarbara cbarbara changed the title Recent garmin API change may have broken sample code expectations Recent garmin API change may have broken sample code expectations and file uploads Nov 25, 2024
@dgolovach
Copy link

another example during initial authn - looks like the scope of the change isn't small

self.display_name = self.garth.profile["displayName"]
                    ^^^^^^^^^^^^^^^^^^

File "/usr/local/lib/python3.12/site-packages/garth/http.py", line 104, in profile
assert isinstance(self._profile, dict)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError

@kerryshireman
Copy link

Yes, basically everything in that section of the login method, including displayName, fullName, measurementSystem, and even the settings dict aren't being properly set.

@dgolovach
Copy link

yeah looking in the new requests it looks it now requires user uid in the url

@LilDinker
Copy link

Running into the same thing

@app4g
Copy link

app4g commented Nov 26, 2024

yeah looking in the new requests it looks it now requires user uid in the url

I looked at the inspectors in the browser, while there a numeric string attached to the URL, it doesn't seems like it's a User UID. It looks like a running number or state of some kind. It's sequential from all the queries being sent

@app4g
Copy link

app4g commented Nov 26, 2024

can someone confirm if this works on their end?

https://github.com/matin/garth/blob/87abab3cf259620a7cc1f21c5c12d3f21f43dd15/garth/http.py#L15C1-L20C2

From:
USER_AGENT = {
"User-Agent": (
"Mozilla/5.0 (iPhone; CPU iPhone OS 16_5 like Mac OS X) "
"AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148"
),
}

To:
USER_AGENT = {
"User-Agent": ("GCM-iOS-5.7.2.1"),
}

I took this from using HTTPToolKit on IOS Version of Garmin Connect Mobile. When I did this, the connection to
https://connectapi.garmin.com/userprofile-service/socialProfile

it seems to work.

@FabioHyneck
Copy link

New User_Agent string works fine here.

@psdupvi
Copy link

psdupvi commented Nov 26, 2024

Confirming it works for me as well, and no other endpoints appear to have changed (once we get past the login process, the rest of my daily sync works)

@app4g which request did you see this number on? The Garmin endpoints generally appear the same to me. Are you referring to these numbers?
image

I think those numbers have generally always been there, and I don't think they're actually necessary for the endpoints to work

EDIT: I may have misunderstood your response, and you and I may be in agreement that they don't mean anything

@yorkshirelad99
Copy link

making that change works for me too

@cyberjunky
Copy link
Contributor

cyberjunky commented Nov 26, 2024

Confirming it works for me as well, and no other endpoints appear to have changed (once we get past the login process, the rest of my daily sync works)

@app4g which request did you see this number on? The Garmin endpoints generally appear the same to me. Are you referring to these numbers? image

I think those numbers have generally always been there, and I don't think they're actually necessary for the endpoints to work

EDIT: I may have misunderstood your response, and you and I may be in agreement that they don't mean anything

Those numbers are timestamps, time in unix format, with purpose to make them unique i guess.

@reifyhealth-caseybecking

@app4g Your change worked for me as well.

@tcgoetz
Copy link

tcgoetz commented Nov 27, 2024

Also effecting the GarminDB project: tcgoetz/GarminDB#249 Is there an ETA on releasing the fix?

@fatsbrown
Copy link

fatsbrown commented Nov 27, 2024

Changing USER_AGENT does not work for me.

Using garth.resume() and garth.client.username fails with the same AssertionError as the OP.

Using garth.login() and garth.client.upload() sometimes finishes without error but the fit is not uploaded, and sometimes fails like the OP (requests.exceptions.ChunkedEncodingError: Response ended prematurely).

Edit: interestingly this works for me

import garth
import requests

garth.resume('./tokens')
resp = requests.get('https://connectapi.garmin.com/userprofile-service/socialProfile',
    headers={'authorization': garth.client.oauth2_token.__str__()})
print(resp.json()['userName'])
requests.post('https://connectapi.garmin.com/upload-service/upload/.fit',
    files={'file': open('test.fit', "rb")}, headers={'authorization': garth.client.oauth2_token.__str__()})

@chriszuercher
Copy link

Changing the User Agent worked for me. Thanks!

@Harley-L
Copy link

Harley-L commented Dec 1, 2024

Changing the User Agent worked for me as well - Any ETA on this fix being merged and be updated on PyPI @matin?

As a temporary fix, you can use this code before logging in:

import garth
garth.http.USER_AGENT = {"User-Agent": ("GCM-iOS-5.7.2.1")}

@cyberjunky
Copy link
Contributor

Changing USER_AGENT does not work for me.

Using garth.resume() and garth.client.username fails with the same AssertionError as the OP.

Using garth.login() and garth.client.upload() sometimes finishes without error but the fit is not uploaded, and sometimes fails like the OP (requests.exceptions.ChunkedEncodingError: Response ended prematurely).

Edit: interestingly this works for me

import garth
import requests

garth.resume('./tokens')
resp = requests.get('https://connectapi.garmin.com/userprofile-service/socialProfile',
    headers={'authorization': garth.client.oauth2_token.__str__()})
print(resp.json()['userName'])
requests.post('https://connectapi.garmin.com/upload-service/upload/.fit',
    files={'file': open('test.fit', "rb")}, headers={'authorization': garth.client.oauth2_token.__str__()})

So this means you are using the default user agent string that comes with requests I guess.

@pblocz
Copy link

pblocz commented Dec 1, 2024

I have done a quick search out of curiosity and from what I see @matin seems to have been offline for a while in Github and other sites. Hopefully he will see these messages

@matin
Copy link
Owner

matin commented Dec 1, 2024

just seeing this now. will release today

@fatsbrown
Copy link

So this means you are using the default user agent string that comes with requests I guess.

I believe I'm not using any "User-Agent" because it's a value of the "headers" dict, and I'm sending only "authorization". Wondering why "GCM-iOS-5.7.2.1" doesn't work for me, maybe because I never used the iOS client?

@matin
Copy link
Owner

matin commented Dec 1, 2024

merged and released #74 with 0.4.47. thanks @app4g and @cyberjunky!

I apologize for being slow to react to this

@matin matin closed this as completed Dec 1, 2024
@matin
Copy link
Owner

matin commented Dec 1, 2024

@fatsbrown can you let me know if the new version works for you, or if the UA change this doesn't resolve the issue for you

@fatsbrown
Copy link

@fatsbrown can you let me know if the new version works for you, or if the UA change this doesn't resolve the issue for you

Yes, the new version works. Previously I tried garth.http.USER_AGENT = {"User-Agent": "GCM-iOS-5.7.2.1"} as suggested but it didn't work, no idea why. Thank you.

@dgolovach
Copy link

Looks like this problem came back and User Agent workaround isn't working anymore. Is it the case for the community or just me?

@yorkshirelad99
Copy link

Looks like this problem came back and User Agent workaround isn't working anymore. Is it the case for the community or just me?

Still working for me but I never got round to updating garth. Used a couple of times this morning. Still using the manual fix

@psdupvi
Copy link

psdupvi commented Dec 11, 2024

Working for me this morning. I'm using garminconnect, not Garth directly, but it uses the Garth authentication

image

@pblocz
Copy link

pblocz commented Dec 11, 2024

I got the error AttributeError: 'Client' object has no attribute 'profile' on the latest version. Going back to 0.4.47 does work for me.

@app4g
Copy link

app4g commented Dec 12, 2024

Still working for me 🙏 (not using garth directly)

@Harley-L
Copy link

The update/fix is still working for me and I am using Garth directly.

@matin
Copy link
Owner

matin commented Dec 12, 2024

@pblocz

I got the error AttributeError: 'Client' object has no attribute 'profile' on the latest version. Going back to 0.4.47 does work for me.

fixed in 0.5.2

@cyberjunky
Copy link
Contributor

Same for garmin_connect/home-assistant-garmin_connect, version 0.4.47 works fine. Didn't test 0.5.2 yet.

@matin
Copy link
Owner

matin commented Dec 12, 2024

@cyberjunky 0.5.2 finally addresses the issue in #41 and creating the option to break up the login in process when there's MFA (based on #41 (comment))

@cyberjunky
Copy link
Contributor

@cyberjunky 0.5.2 finally addresses the issue in #41 and creating the option to break up the login in process when there's MFA (based on #41 (comment))

Great, that was next thing on my list, I will give it a try soon!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests