10REM / php-garmin-connect

A PHP adapter for interrogating the Garmin Connect "API"
MIT License
84 stars 41 forks source link

Authentication failed #51

Closed LEstefano closed 3 years ago

LEstefano commented 3 years ago

Hi,

it looks like the class GarminConnect is not working anymore for me. The authorize function returns "Authentication failed - please check your credentials".

Any other is receiving this error since today?

Thank you

Kind regards, Luis Estéfano

AnneWielis commented 3 years ago

+1, no longer working since today

e7andy commented 3 years ago

I'm getting "Authentication failed - please check your credentials" as well. Garmin recently added two factor authentication: https://www.garmin.com/en-GB/account/security/mfa/ Maybe they also made some other changes.

I've tried both with and without 2FA and I get the same error. One interesting thing was that even after I activated 2FA yesterday it still worked to use php-garmin-connect. I would've thought it should start to fail.

AnneWielis commented 3 years ago

I looked up what actually comes back from Garmin and it says error code: 1020

LEstefano commented 3 years ago

It could be Garmin denying access when the request comes from sites they do not know.

AnneWielis commented 3 years ago

Maybe. Any idea what the message means?

LEstefano commented 3 years ago

What does Error 1020 Access Denied mean?

The error messages that start with 1xxx refer to connection problems on sites that use the Cloudflare proxy. One of them is the Error 1020 Access Denied. This indicates that you’ve violated a firewall rule and your request is blocked.

Before going into detail about what the error 1020 means, let’s talk briefly about using CDNs. They are a resource used to improve the performance of access to the website.

In practice, there are copies of the website’s files spread over the various servers that are part of the CDN. Thus, the user will be connected to the server that is closest to their location.

Besides increasing performance, Cloudflare also offers some security features that help protect the website from cyber attacks. Among them, the DDoS attack, when several simultaneous accesses to the page occur, either by real users or by so-called zombie computers.

Source: https://rockcontent.com/blog/error-1020-access-denied/#:~:text=The%20error%20messages%20that%20start,and%20your%20request%20is%20blocked.&text=They%20are%20a%20resource%20used,of%20access%20to%20the%20website.

DaveWilcock commented 3 years ago

Hi all. Thanks for the investigations. I'll do some myself over the next few days.

The success of this package has always been dependent on Garmin's relatively lazy security. In light of their recent supposed hacks, it comes as no surprise that they have tightened up some security checks.

That said, all this package really does is emulate a web browser logging in to their site by using cookie storage to maintain the session. With that in mind, I suspect there might be a workaround here, but I'll need to take a look at the login form at connect.garmin.com and see if it has any new prerequisites. Perhaps we need to ... spoof tweak ... some HTTP headers or something like that.

I should have some spare time to look into this later.

yihong0618 commented 3 years ago

@dawguk

Yes I worked on it for a few hours, no luck, the security is based the Cloudflare And I tried the Cookie works well. So I think I can add something like Cookie login

https://github.com/yihong0618/running_page/issues/129

yihong0618 commented 3 years ago

FYI,

Python version solved. by using sometools to bypass cloudflare. I don't know if PHP has this kind of tool. https://github.com/cyberjunky/python-garminconnect/issues/46 https://github.com/cyberjunky/python-garminconnect/commit/f9fda0120db40446d55ba9e4498a8c210b5824e4

DaveWilcock commented 3 years ago

I have found some pure PHP cloudflare bypass tools, but they appear to have been abandoned due to cloudflare making updates that render them useless.

I have found some middleware for guzzle (that we don't currently use in this package) that looks like it does the job (https://github.com/jaymoulin/guzzlehttp-cloudflare) but to use this would require a fairly large rewrite of the library to start using guzzle. Not something I am either a fan of or currently have time for!

I have also found a tool that uses the v8js extension in PHP, that looks like it could work (https://github.com/yani/PHP-v8js-CloudFlare-bypass/blob/master/cf-bypass.php) however, there's a few problems with this approach:

... so ... some interesting information, but not great news at the minute.

AnneWielis commented 3 years ago

Another Repo with the same history https://github.com/petergardfjall/garminexport/issues/79

LEstefano commented 3 years ago

I have to review, but maybe some of these packages could help?

AnneWielis commented 3 years ago

Cloudscrape Client is deprecated, but dexi might work

AnneWielis commented 3 years ago

@LEstefano why did you close?

AnneWielis commented 3 years ago

Cloudscrape Client is deprecated, but dexi might work

Dexi seems outdated as well :-(

DaveWilcock commented 3 years ago

Is anyone here in the position to test something? I have made some changes here, and I believe that I have something working, but I'm not convinced that my environment here is entirely unbiased!

I have just added two new default curl options to the array at the top of the Connector:

CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0', CURLOPT_ENCODING => 'gzip',

This appears to allow the software to continue working.

ltcomd commented 3 years ago

Thanks, I'll try later.

BebeMischa commented 3 years ago

I'm already testing this for 20 hours and it looks fine: https://github.com/home-assistant/core/issues/50822

AnneWielis commented 3 years ago

Seems to work.

AnneWielis commented 3 years ago

Yes, works fine. Downloaded all the stuff now.

LEstefano commented 3 years ago

It is working also for me, @dawguk !! Thanks a lot!

AnneWielis commented 3 years ago

Yeah, many thanks for looking into it!

DaveWilcock commented 3 years ago

I'm unsure why this by itself is enough to resolve the login issue, but I'll take a deep dive when I get home in about an hour, and hopefully raise a pull request and get a new version tagged for release later today.

On Wed, 19 May 2021, 15:10 LEstefano, @.***> wrote:

It is working also for me, @dawguk https://github.com/dawguk !! Thanks a lot!

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/10REM/php-garmin-connect/issues/51#issuecomment-844142974, or unsubscribe https://github.com/notifications/unsubscribe-auth/AADHRHWAC4D4QQZKLOQ7GADTOPBEVANCNFSM45BJFIFA .

DaveWilcock commented 3 years ago

Thanks all for your patience. The latest changes from master including this fix have now gone out in v1.7.0 :+1:

jakkarth commented 3 years ago

Installed updated version via composer to 1.7.0, still getting authentication error. Confirmed that the user agent string etc are present inside my vendor/ folder.

DaveWilcock commented 3 years ago

Installed updated version via composer to 1.7.0, still getting authentication error. Confirmed that the user agent string etc are present inside my vendor/ folder.

Try rm /tmp/GarminCookie* and then go again

jakkarth commented 3 years ago

Try rm /tmp/GarminCookie* and then go again

rm: cannot remove '/tmp/GarminCookie': No such file or directory rm: cannot remove '/tmp/GarminCookie*': No such file or directory

DaveWilcock commented 3 years ago

I assume your system temporary directory is defined as /tmp/?

What version of PHP are you using?

jakkarth commented 3 years ago

php -v PHP 7.2.24-0ubuntu0.18.04.7 (cli) (built: Oct 7 2020 15:24:25) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies with Zend OPcache v7.2.24-0ubuntu0.18.04.7, Copyright (c) 1999-2018, by Zend Technologies

echo '<?php var_dump(sys_get_temp_dir());' |php string(4) "/tmp"

/tmp is my temporary directory. I've also done a find / -xdev -name 'GarminCookie*' in case it was somewhere else; no dice.

DaveWilcock commented 3 years ago

Very odd. When I kill my previous cookie, comment out the two new curl options, my authentication fails. When I uncomment the lines again and re-run the example, the authentication works fine.

DaveWilcock commented 3 years ago
✘-1 ~/Development/php-garmin-connect [master ↑·2|✚ 1] 
17:25 $ php -v
PHP 7.4.9 (cli) (built: Oct 26 2020 15:17:14) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.9, Copyright (c), by Zend Technologies
jakkarth commented 3 years ago

The cookie file exists and appears to have some reasonable contents. I thought perhaps it wasn't being created at all, so I changed the directory and removed the unlink and confirmed that the file is there and has reasonable contents, but the login still fails. Unlinked it and verified it's recreated from scratch, login still fails. Honestly not sure what else to try.

jakkarth commented 3 years ago

Tried again with PHP 7.4.3 (cli) (built: Oct 6 2020 15:47:56) ( NTS ) on a completely fresh install. Just changed the username and password in example.php to something I've just confirmed works in my browser. No dice. I've also confirmed that the two headers in the MR are present when I changed the URL to my own server so I could observe the request.

Tried commenting out and running and uncommenting and running. Output is different; without headers it's just the exception text, with headers it's a bunch of html too.

DaveWilcock commented 3 years ago

Very strange. You say on a fresh install - did you bring up a docker container or something similar? I wonder if I can try the same, but use my credentials. Would like to rule out as many possibilities as ... possible.

jakkarth commented 3 years ago

It's an ubuntu 20.04 vm I had lying around. I had to install composer to get the package. I could try it in a docker container if that would be helpful.

jakkarth commented 3 years ago

Interesting turn of events. Running example.php from inside a php7.4 docker container worked! My usual hosts are ubuntu but the php docker image is based on debian 10.9. Clearly some kind of difference, maybe in the curl version?

DaveWilcock commented 3 years ago

Ooh OK. So I'm using Ubuntu 20.10 natively as my desktop

19:06 $ curl --version
curl 7.68.0 (x86_64-pc-linux-gnu) libcurl/7.68.0 OpenSSL/1.1.1f zlib/1.2.11 brotli/1.0.9 libidn2/2.3.0 libpsl/0.21.0 (+libidn2/2.3.0) libssh/0.9.3/openssl/zlib nghttp2/1.41.0 librtmp/2.3
Release-Date: 2020-01-08
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp 
Features: AsynchDNS brotli GSS-API HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM NTLM_WB PSL SPNEGO SSL TLS-SRP UnixSockets

Also openssl, just in case we're going down that path:

19:07 $ openssl version
OpenSSL 1.1.1f  31 Mar 2020
jakkarth commented 3 years ago

Tried it in an ubuntu docker image that matches the host. php7.4.3 in both, libcurl4 7.68.0 in both. Works in the container and not on the host. Sorry I'm not much help. It sounds like it's a local issue for me, especially since the docker containers can execute fine and you and others in this thread can execute fine. I'd appreciate any assistance in tracking it down, but I don't want to bother people unnecessarily.

DaveWilcock commented 3 years ago

Can you run php -m on both the host and in the docker container, and let me know what output you get?

One of the changes that I added was gzip compression to all the requests. I wonder if you are missing a module on your host.

DaveWilcock commented 3 years ago

(This is just a massive punt here, I have no idea if gzip support comes out of the box with PHP)

jakkarth commented 3 years ago

Just saw your reply, yeah I think it's openssl. I had made some changes to the vm's openssl.cnf file. Reverting those changes and it works in the vm. It's on 1.1.1f. The other host I was trying with is 1.1.1 (no f). IIRC there was a default change that disable sslv1 or something like that, and that may have happened between these versions.

zlib is present on both the working and non-working machines. I think it's an ssl issue.

DaveWilcock commented 3 years ago

I seem to recall that TLS compatibility updates were applied to openssl > 1.1.1 - I wonder if garmin.com is now enforcing a TLS v1.3 connection.

DaveWilcock commented 3 years ago

If you have it working in the vm now, can I ask you to try this? Setting the CURLOPT_SSLVERSION to 6 will tell PHP to use a TLS v1.2 handshake. In the Connector, can you drop the following into the default config array and see if it stops working?

CURLOPT_SSLVERSION => 6,
AnneWielis commented 3 years ago

...which would in fact be a reasonable decision.

-----Original Message----- From: David Wilcock @.> To: 10REM/php-garmin-connect @.> Cc: Anne W @.>, Comment @.> Sent: Mi., 19 Mai 2021 20:25 Subject: Re: [10REM/php-garmin-connect] Authentication failed (#51)

I seem to recall that TLS compatibility updates were applied to openssl > 1.1.1 - I wonder if garmin.com is now enforcing a TLS v1.3 connection.

-- You are receiving this because you commented. Reply to this email directly or view it on GitHub: https://github.com/10REM/php-garmin-connect/issues/51#issuecomment-844358514

jakkarth commented 3 years ago

Including CURLOPT_SSLVERSION => 6, in the config array has not changed the behavior in the working case. It continues to work.

I am all for increasing security and upping the tls version. And the box that is experiencing the problem has no issues connecting to other sites over tlsv1.3. The issue I think is that during negotiation, the older openssl version is offering tls1.2 or some other version or protocol or cypher, and cloudeflare is refusing it because it was offered, not because something higher wasn't available.

I would upgrade the old box if it were mine to tamper with fix, but sadly that's not currently an option.

jakkarth commented 3 years ago

The box maintainer has compiled 1.1.1j from source and install it. Output from php -i:

OpenSSL Library Version => OpenSSL 1.1.1j 16 Feb 2021

I've also copied the openssl.cnf from the working box to the problematic one. The problem persists. I suspect that to make further progress I would have to compile php from source. Unfortunately that is beyond our resources at the moment, so I'll use containers to work around it until the next OS upgrade rolls out to the problematic box.

Thank you all very much for your assistance, and a wonderful library. :)

DaveWilcock commented 3 years ago

If you have time, I'd like you to just try one more thing for me (sorry):

php examples/example.php 2>&1 | grep SSL

* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
*  SSL certificate verify ok.
* old SSL session ID is stale, removing
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
*  SSL certificate verify ok.
* old SSL session ID is stale, removing
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
*  SSL certificate verify ok.
* old SSL session ID is stale, removing

Let me know what you get.

DaveWilcock commented 3 years ago

If you have time, I'd like you to just try one more thing for me (sorry):

php examples/example.php 2>&1 | grep SSL

* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
*  SSL certificate verify ok.
* old SSL session ID is stale, removing
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
*  SSL certificate verify ok.
* old SSL session ID is stale, removing
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
*  SSL certificate verify ok.
* old SSL session ID is stale, removing

Let me know what you get.

(Oh you'll need to set CURLOPT_VERBOSE to true in the Connector)

jakkarth commented 3 years ago

Like I said, I get lots of html back. It's not that it can't negotiate. It's that having a lower version even offered seems to be causing the problem. At least that's my interpretation.

DaveWilcock commented 3 years ago

How frustrating. Sorry that I wasn't able to solve this for you!