jlouder / garmin-connect-perl

Perl module for accessing Garmin Connect data
2 stars 3 forks source link

Can't make search request: 403 Forbidden #5

Closed noduck closed 11 months ago

noduck commented 11 months ago

Hi,

I don't know if you are still actively using and developing this module. I am :-D

It looks like Garmin Connect again changed the login process, and the module no longer works. It looks like it changed on 9/27 around 12:00 UTC.

I have no investigated further yet.

Thanks!

jlouder commented 11 months ago

I am still maintaining and using this, and thanks for reporting this. I'll look into it and hopefully have it fixed soon.

jlouder commented 11 months ago

This is pretty ugly. I've been reading other GitHub issues for projects that have the same problem, and here is a good thread on this:

https://github.com/pe-st/garmin-connect-export/issues/95

From the looks of that, even if you update the calls to what the web site is now doing, Cloudflare will detect it's not a browser and require a captcha challenge.

I've looked at several projects with this problem, and everyone seems to be adopting this Python project to connect to Garmin Connect:

https://github.com/matin/garth

This developer has apparently reverse engineered what the Garmin Connect mobile app does to authenticate and is doing that (OAuth with the key the app uses).

I'm going to try to implement that in Perl, but this is going to take some time. If you aren't tied to Perl, you might want to consider just switching to Python and using Garth directly. The next time something changes on the Garmin side, that project will probably get updated faster since a lot of people are using it.

noduck commented 11 months ago

Attached is my mock up. Results in ticket and cookies. That might be enough to get GarminConnect up an running? Ticket is saved as a local cookie. Cookies are saved in a file. These may be enough not to login again. Hope to work on it further in the next days sso.txt

jlouder commented 11 months ago

Thanks for helping with this. I have the OAuth method mostly working (in the oauth branch, linked to this issue). I just need a little more time to finish, and I'm hoping tomorrow I'll be able to get it completely done and released.

Hopefully this method will be less susceptible to breaking when Garmin makes website changes. It is going to introduce a dependency on the WWW:OAuth module.

jlouder commented 11 months ago

The code in the oauth branch can actually make an activity search request and get back real data now. However, I must have hit some rate-limiting because I'm getting 429 Too Many Requests now. I'll just need to wait a bit to finish up testing.

jlouder commented 11 months ago

I just uploaded a new working version (1.1.0) to CPAN. It usually takes a while (a few hours?) to see it, but the code in this repo works now.

noduck commented 11 months ago

Great, thanks for the update! I hope to test later today.

Just like Garth, GarminConnect.pm could save the oauth values, so that repeated logins are not required (and don't trigger the dreaded 429).

If you don't mind, I will try to add some code.

jlouder commented 11 months ago

Sure, the module is pretty dumb about tokens right now. It assumes that the first time you call activities it needs to log in and get a token, and then it assumes that's good forever (well, the lifetime of whatever script is using the module) and it'll never try to refresh the token again, using it with every API call.

Some enhancements I had been thinking about were:

In the interest of time, I didn't do any of this in version 1.1.0 -- I just wanted to get it working again. But maybe I can add some of these features. Or if you'd like to contribute, feel free to make a fork and send a pull request. I suspect you and I are the only people using this module, so you're 50% of the user base!

noduck commented 11 months ago

The stored token approach in my code is very simple: load it back, setup the Perl object, try to do a /userprofile-service/socialProfile API call, if that works _login is done, and if not it goes through the regular authentication process. If the token is expired, the API call should fail, and the regular login happens (and also stores the new token).