petergardfjall / garminexport

Garmin Connect activity exporter and backup tool
Apache License 2.0
507 stars 83 forks source link

Fetch activities returning HTTP error code 402 #89

Closed bobgott closed 1 year ago

bobgott commented 1 year ago

I have been using garminclient.py for 2 years and as of yesterday starting receiving HTTP error code 402. I believe this is related to closed issue #71.

Log level output to DEBUG follows:

2022-12-17 06:54:25,961 (DEBUG) using session factory: cloudscraper
2022-12-17 06:54:25,995 (INFO) authenticating user ...
2022-12-17 06:54:25,995 (INFO) fetching CSRF token ...
2022-12-17 06:54:27,221 (DEBUG) got auth response: <!DOCTYPE html>
<html class="no-js">
    <head>
        <title>Success</title>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge;" />
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta http-equiv="cleartype" content="on">
        <script type="text/javascript" src="/sso/js/jquery/3.1.1/jquery.min.js?20210319"></script>
        <script type="text/javascript">jQuery.noConflict();</script>
        <script type="text/javascript" src="/sso/js/json2.js"></script>
        <script type="text/javascript" src="/sso/js/consoleUtils.js?20210319"></script>
        <script type="text/javascript" src="/sso/js/postmessage.js?20210319"></script>
        <script type="text/javascript">
            var redirectAfterAccountLoginUrl      = "";
            var redirectAfterAccountCreationUrl = "";
            var consumeServiceTicket              = "true";
            var service_url                       = "https:\/\/connect.garmin.com\/modern\/";
            var parent_url                        = "";
            var response_url                      = "https:\/\/connect.garmin.com\/modern\/?ticket=ST-0663869-qMpzM5cswJcKeNi5E7dz-cas";
            var logintoken                        = "";
            var socialLogin                       = "";
            var performMFACheck                 = "";

            // Decode url if it's encoded unnecessarily (which is happening when SSO GAuth logins redisplay the login page due to session timeouts.)
            if (response_url.indexOf('%3A%2F%2F') != -1) {
                response_url = decodeURIComponent(response_url);
            }
            response_url = response_url.replace(new RegExp("&amp;", 'g'),"&");

            var service_ticket = response_url.substring(response_url.indexOf('ticket=') + 7, response_url.length);

            if (redirectAfterAccountLoginUrl) {
                consoleInfo('casEmbedSuccess.html: redirectAfterAccountLoginUrl: [' + redirectAfterAccountLoginUrl + ']');
            }
            consoleInfo('casEmbedSuccess.html: consumeServiceTicket:         [' + consumeServiceTicket + ']');
            consoleInfo('casEmbedSuccess.html: service_url:                  [' + service_url + ']');
            consoleInfo('casEmbedSuccess.html: parent_url:                   [' + parent_url + ']');
            consoleInfo('casEmbedSuccess.html: response_url:                 [' + response_url + ']');
            consoleInfo('casEmbedSuccess.html: service_ticket:               [' + service_ticket + ']');
            if (logintoken) {
                consoleInfo('casEmbedSuccess.html: logintoken:                   [' + logintoken + ']');
            }
            if (socialLogin) {
                consoleInfo('casEmbedSuccess.html: socialLogin:                   [' + socialLogin + ']');
            }

            function send(msg) {
                consoleInfo('casEmbedSuccess.html: send(): Calling XD.postMessage(msg:[' + JSON.stringify(msg) + '], target_url:[' + parent_url + '])...');
                XD.postMessage(msg, parent_url, parent);
                return false;
            }

            function redirect(target){
                var embedWidget = "";
                if (embedWidget != 'true') {
                    if (logintoken || socialLogin) {
                        //  Tell parent to close opened Gauth lite box.
                        send({'closeLiteBox':'1'});
                    } else {
                        // Tell parent to resize Gauth widget's height to just that of the loading icon.
                        send({'gauthHeight':jQuery("#GAuth-component").height()});
                    }
                }

                if (window.opener != null) {
                    var iframeParent = window.opener.parent;
                    iframeParent.location.href = target;
                    window.close();
                } else {
                    // If the "redirectAfterAccountLoginUrl" GAuth config parameter was specified,
                    // a full page refresh upon logon was requested, so redirect the parent window upon login.
                    if (redirectAfterAccountLoginUrl || redirectAfterAccountCreationUrl || (socialLogin && socialLogin === 'true') || (performMFACheck && performMFACheck === 'true')) {

                        if (embedWidget != 'true') {
                            send({'status':'SUCCESS', 'successDetails':'Login Successful'});
                        }

                        // Redirect parent of login iframe to requested service URL with ?ST=<Service Ticket ID> appended,
                        // so the webapp can validate the service ticket and log the user on to the webapp.
                        consoleInfo('casEmbedSuccess.html: Calling parent.location.href = [' + response_url + '];...');
                        top.location.href = response_url;

                    // Else if GAuth was configured not to consume the service ticket, send the service ticket and service url.
                    // It's then up to the parent page to hide the GAuth widget and validate the service ticket.
                    } else if (consumeServiceTicket == 'false') {

                        send({'status':'SUCCESS', 'successDetails':'Login Successful', 'serviceTicket':service_ticket, 'serviceUrl':service_url});

                    } else {
                        // Else consume the service ticket and log the user into the parent webapp, by making a JSONP request to the response url.
                        // This service ticket request to the parent webapp will return a small amount of JSON that we
                        // can send in the success event to the parent page to notify them of successful logon
                        // It's then up to the parent page to hide the GAuth widget and do whatever it wants.
                        consoleInfo('casEmbedSuccess.html: Loading ajax jsonp URL: [' + response_url + ']');
                        jQuery.ajax({
                            type: "REDIRECT",
                            url: response_url,
                            dataType: 'jsonp',
                            error: function(xhr, status, error) {
                                consoleError('casEmbedSuccess.html: Error loading ajax jsonp URL: [' + response_url + ']! Error: ' + error);
                            },
                            success: function( data, status, xhr ) {
                                consoleInfo('casEmbedSuccess.html: success loading ajax jsonp url. data: [' + data + ']');
                                var userdata = data;
                                if (typeof(userdata) === 'string') {
                                    userdata = JSON.parse(data);
                                }
                                consoleInfo('casGenericRedirect.jsp: customerId: ['            + userdata.customerId
                                                                                            + '], username: ['               + userdata.username
                                                                                            + '], rememberMe: ['             + userdata.rememberMe
                                                                                            + '], password: ['               + userdata.password
                                                                                            + '], email: ['                  + userdata.email
                                                                                            + '], displayName: ['            + userdata.displayName
                                                                                            + '], firstName: ['              + userdata.firstName
                                                                                            + '], lastName: ['               + userdata.lastName
                                                                                            + '], localePreference: ['       + userdata.localePreference
                                                                                            + '], addressLine1: ['           + userdata.addressLine1
                                                                                            + '], addressLine2: ['           + userdata.addressLine2
                                                                                            + '], cityName: ['               + userdata.cityName
                                                                                            + '], state: ['                  + userdata.state
                                                                                            + '], postalCode: ['             + userdata.postalCode
                                                                                            + '], country: ['                + userdata.country
                                                                                            + '], passwordChangeRequired: [' + userdata.passwordChangeRequired
                                                                                            + '], lastLogin: ['              + userdata.lastLogin
                                                                                            + '], erpCustomerNumber: ['      + userdata.erpCustomerNumber
                                                                                            + ']');
                                send({'status'                 : 'SUCCESS',
                                            'successDetails'         : 'Login Successful',
                                            'customerId'             : userdata.customerId,
                                            'username'               : userdata.username,
                                            'rememberMe'             : userdata.rememberMe,
                                            'password'               : userdata.password,
                                            'email'                  : userdata.email,
                                            'displayName'            : userdata.displayName,
                                            'firstName'              : userdata.firstName,
                                            'lastName'               : userdata.lastName,
                                            'localePreference'       : userdata.localePreference,
                                            'addressLine1'           : userdata.addressLine1,
                                            'addressLine2'           : userdata.addressLine2,
                                            'cityName'               : userdata.cityName,
                                            'state'                  : userdata.state,
                                            'postalCode'             : userdata.postalCode,
                                            'country'                : userdata.country,
                                            'passwordChangeRequired' : userdata.passwordChangeRequired,
                                            'lastLogin'              : userdata.lastLogin,
                                            'erpCustomerNumber'      : userdata.erpCustomerNumber
                                });
                            }
                        });
                    }
                }
            }
        </script>
    </head>
    <body>
        <div id="GAuth-component">
            <img src='/sso/images/ajax-loader.gif' class="loaderImage"/>
        </div>
        <script type="text/javascript">
            jQuery(document).ready(function(){
                var service = "https:\/\/connect.garmin.com\/modern\/";
                consoleInfo("casEmbedSuccess.html: ready, calling redirect('" + service + "')...");
                redirect(service);
            });
        </script>
    </body>
</html>

2022-12-17 06:54:27,226 (DEBUG) auth ticket url: 'https://connect.garmin.com/modern/?ticket=ST-0663869-qMpzM5cswJcKeNi5E7dz-cas'
2022-12-17 06:54:27,226 (INFO) claiming auth ticket ...
2022-12-17 06:54:31,540 (INFO) Client 'Garmin Activities' to server 'Google Sheet: RGG Garmin Hiking'
2022-12-17 06:54:31,540 (INFO) Reading 10 activities from Garmin Connect API
2022-12-17 06:54:31,540 (DEBUG) fetching activities 0 through 9 ...

Traceback (most recent call last):
  File "garminclient.py", line 73, in check_session
    return client_function(*args, **kwargs)
  File "garminclient.py", line 254, in _fetch_activity_ids_and_ts
    raise Exception(
Exception: failed to fetch activities 0 to 9 types: 402
bobgott commented 1 year ago

The PR #92 posted by @nowster (see: issue #90) seems to fixed my problem. Will do more tests.

petergardfjall commented 1 year ago

Should be fixed by 188e97e26ca2bac86d13fbe11d929a1cabf987f5 on master.

petergardfjall commented 1 year ago

New release (0.4.2) published to PyPi.