Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-20415] Android: HttpClient Basic Auth: Infinite loop with bad credentials

GitHub Issuen/a
TypeBug
PriorityHigh
StatusClosed
ResolutionFixed
Resolution Date2016-06-16T20:25:10.000+0000
Affected Version/sRelease 5.1.2, Release 5.2.2
Fix Version/sRelease 6.0.0, Release 5.4.0
ComponentsAndroid
Labels5.1.2, android, httpclient, network
ReporterJorge Macias Garcia
AssigneeAshraf Abu
Created2016-02-16T13:27:19.000+0000
Updated2016-06-21T22:42:19.000+0000

Description

Steps to Reproduce

- Set bad credentials on Basic Auth Run attatched project with Android and you can see in console readyState = 1. In our log server we register infinite requests and the request never ends. request => response 401 => request => response 401 With no error callback. In iOS its OK.

Actual Result

Infinite Loop with no callback to 401 http error.

Expected Result

Error callback with 3 attempts like browsers or like iOS.

Attachments

FileDateSize
app.zip2016-02-16T13:29:52.000+000010427729

Comments

  1. Jorge Macias Garcia 2016-02-16

    Test App
  2. Jorge Macias Garcia 2016-03-04

    What happen with this? When do you planning to fix this?
  3. Ashraf Abu 2016-03-07

    [~jormagar] Thanks for reporting this ticket. Will be investigating this.
  4. Stefan Schüller 2016-03-24

    Please add a unit test for http auth, it is kind of an important core feature. For those who need a fix now, don't use the built-in withCredentials, username, password feature in createHTTPClient. Instead set the authorization header by hand like so. Do this after xhr.open() but before xhr.send().
       xhr.setRequestHeader("Authorization", "Basic " + Titanium.Utils.base64encode(username + ':' + password));
       
  5. Ashraf Abu 2016-03-28

    [~sschueller] Thanks for the info. (y)
  6. Blackbaud MobileDev 2016-05-13

    This bug is affecting Blackbaud's primary mobile application and we need a fix ASAP! This bug is causing our users to get locked out repeatedly and is affecting our user experience rating. Please let us know when this will be fixed, I do not see a planned fixed in version yet. Thank You.
  7. Ashraf Abu 2016-06-01

    [~jormagar] would you have the logs for the server when this happens? I'm trying to see if it would help in figuring out what's happening.
  8. Jorge Macias Garcia 2016-06-01

    Yes, i do. When I report the JIRA ticket someone of your team test it. Here the logs: 2016-02-17 08:13:34 W3SVC62 FENABLO 158.42.250.160 POST /error401/ platform=android 443 - 115.127.6.99 HTTP/1.1 Appcelerator+Titanium/5.1.2+(Google+Nexus+7+-+5.1.0+-+API+22+-+800x1280;+Android+API+Level:+22;+en-US; ) - - mimaen.webs.upv.es 401 5 0 389 401 811 2016-02-17 08:13:34 W3SVC62 FENABLO 158.42.250.160 POST /error401/ platform=android 443 - 115.127.6.99 HTTP/1.1 Appcelerator+Titanium/5.1.2+(Google+Nexus+7+-+5.1.0+-+API+22+-+800x1280;+Android+API+Level:+22;+en-US; ) ASPSESSIONIDCWSQRDST=PHKHNFBCPBFPBLNAHHAGIBEG - mimaen.webs.upv.es 401 5 0 389 495 218 ... 2016-02-17 11:01:38 W3SVC62 FENABLO 158.42.250.160 POST /error401/ platform=android 443 - 115.127.6.99 HTTP/1.1 Appcelerator+Titanium/5.1.2+(Google+Nexus+7+-+5.1.0+-+API+22+-+800x1280;+Android+API+Level:+22;+en-US; ) ASPSESSIONIDCWSQRDST=OINLOFBCBNOMGEIHGJKMMCJA - mimaen.webs.upv.es 401 5 0 389 495 218 2016-02-17 11:01:38 W3SVC62 FENABLO 158.42.250.160 POST /error401/ platform=android 443 - 115.127.6.99 HTTP/1.1 Appcelerator+Titanium/5.1.2+(Google+Nexus+7+-+5.1.0+-+API+22+-+800x1280;+Android+API+Level:+22;+en-US; ) ASPSESSIONIDCWSQRDST=PINLOFBCIJIOAJGCCGFNMIIN - mimaen.webs.upv.es 401 5 0 389 495 218 *82706 requests from 115.127.6.99 (Bangladesh) after 3 hours* 2016-02-18 02:28:50 W3SVC62 FENABLO 158.42.250.160 POST /error401/ platform=android 443 - 122.11.137.25 HTTP/1.1 Appcelerator+Titanium/6.0.0+(Nexus+6;+Android+API+Level:+23;+en-US; ) - - mimaen.webs.upv.es 401 5 0 389 366 795 2016-02-18 02:28:50 W3SVC62 FENABLO 158.42.250.160 POST /error401/ platform=android 443 - 122.11.137.25 HTTP/1.1 Appcelerator+Titanium/6.0.0+(Nexus+6;+Android+API+Level:+23;+en-US; ) ASPSESSIONIDAWSSSBTS=EMABDGJCEMCDODGLLKIDDJCM - mimaen.webs.upv.es 401 5 0 389 460 421 ... 2016-02-18 02:30:46 W3SVC62 FENABLO 158.42.250.160 POST /error401/ platform=android 443 - 122.11.137.25 HTTP/1.1 Appcelerator+Titanium/5.1.2+(Nexus+6;+Android+API+Level:+23;+en-US; ) ASPSESSIONIDAWSSSBTS=LOABDGJCOAJEDGMKOFCMJNHH - mimaen.webs.upv.es 401 5 0 389 460 421 2016-02-18 02:30:46 W3SVC62 FENABLO 158.42.250.160 POST /error401/ platform=android 443 - 122.11.137.25 HTTP/1.1 Appcelerator+Titanium/5.1.2+(Nexus+6;+Android+API+Level:+23;+en-US; ) ASPSESSIONIDAWSSSBTS=MOABDGJCCLELCGBKDBDKPADP - mimaen.webs.upv.es 401 5 0 389 460 405 *42 requests from 122.11.137.25 (Singapore) after 2 minutes*
  9. Ashraf Abu 2016-06-01

    Thank you for the logs.
  10. Ashraf Abu 2016-06-01

    This seems to be quite a bad bug in Android on certain version when setting the usage of withCredentials property. Here's a workaround. Please use this:-
        function request(event) {
        
            var client,
                httpConfig,
                url,
                data,
                credentials,
                forceBadCredentials;
        
            forceBadCredentials = true; //Set value to true to force bug on Android
            url = "put ur url here";
            credentials = {
                username : (forceBadCredentials) ? "badUser" : "user",
                password : "p$wd"
            };
        
            //Dummy post param
            data = {
                dummy : 1
            };
        
            httpConfig = {
                onload : onLoad,
                onerror : onError,
                onreadystatechange : onReadyStateChange,
                timeout : 3000
            };
        
            client = Ti.Network.createHTTPClient(httpConfig);
        
            client.open("POST", url);
            client.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            client.setRequestHeader("Authorization", "Basic "+ Titanium.Utils.base64encode(credentials.username +':' +credentials.password));
            client.send(data);
        }
        
    Instead of relying on the client to set the headers, we set the Authorization headers. This prevents the Infinite loop with bad credentials issue. client.setRequestHeader("Authorization", "Basic "+ Titanium.Utils.base64encode(credentials.username +':' +credentials.password)); [~jormagar] Please let me know if this helps in solving your issue.
  11. Ashraf Abu 2016-06-01

    This workaround was mentioned earlier by [~sschueller]
  12. Ashraf Abu 2016-06-09

    PR Master: https://github.com/appcelerator/titanium_mobile/pull/8050 PR 5_4_X: https://github.com/appcelerator/titanium_mobile/pull/8051 For the url, you can use either the above or run a local node server https://github.com/http-auth/http-auth/blob/master/examples/basic.js, in which case, the url is the ip of your computer. Please test on a device running Android 4.x. Classic app for test:
        /**
         * onReadyStateChange
         * @description Debug readyState
         * @param {Object} event
         */
        function onReadyStateChange(event) {
            Ti.API.info("onReadyStateChange");
        
            var readyState = this.readyState;
        
            switch(readyState) {
            case 0:
                // after HTTPClient declared, prior to open()
                // though Ti won't actually report on this readyState
                Ti.API.info('case 0, readyState = ' + readyState);
                break;
            case 1:
                // open() has been called, now is the time to set headers
                Ti.API.info('case 1, readyState = ' + readyState);
                break;
            case 2:
                // headers received, xhr.status should be available now
                Ti.API.info('case 2, readyState = ' + readyState);
                break;
            case 3:
                // data is being received, onsendstream/ondatastream being called now
                Ti.API.info('case 3, readyState = ' + readyState);
                break;
            case 4:
                // done, onload or onerror should be called now
                Ti.API.info('case 4, readyState = ' + readyState);
                Ti.API.info("HTTP DONE WITH STATUS TEXT: " + this.getStatusText());
                break;
            }
        }
        
        /**
         * onLoad
         * @description Http Success Callback
         * @param {Object} event
         */
        function onLoad(event) {
            Ti.API.info("onLoad");
            var statusCode,
                statusText,
                responseText,
                result;
        
            statusCode = this.getStatus();
            statusText = this.getStatusText();
            responseText = this.getResponseText();
        
            result = "onLoad Callback - Status Code: " + statusCode + " - " + "Status Text: " + statusText + " - " + "Response Text: " + responseText;
        
            Ti.API.info(result);
        }
        
        /**
         * onError
         * @description Http Error callback
         * @param {Object} event
         */
        function onError(event) {
            Ti.API.info("onError");
        
            var statusCode,
                statusText,
                result;
        
            statusCode = this.getStatus();
            statusText = this.getStatusText();
        
            result = "onError Callback - Status Code: " + statusCode + " - " + "Status Text: " + statusText;
        
            Ti.API.info(result);
        }
        
        function request(event) {
         
            var client,
                httpConfig,
                url,
                data,
                credentials,
                forceBadCredentials;
         
            forceBadCredentials = true; //Set value to true to force bug on Android
            url = "http://192.168.0.113:1337";
            credentials = {
                username : (forceBadCredentials) ? "badUser" : "user",
                password : "p$wd"
            };
         
            //Dummy post param
            data = {
                dummy : 1
            };
         
            httpConfig = {
                onload : onLoad,
                onerror : onError,
                onreadystatechange : onReadyStateChange,
                withCredentials : true,
                username : credentials.username,
                password : credentials.password,
                timeout : 3000
            };
         
            client = Ti.Network.createHTTPClient(httpConfig);
         
            client.open("POST", url);
            client.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            //client.setRequestHeader("Authorization", "Basic "+ Titanium.Utils.base64encode(credentials.username +':' +credentials.password));
            client.send(data);
        }
        
        var win = Ti.UI.createWindow({
          layout: 'vertical',
          backgroundColor: '#fff'
        });
        
        win.open();
        request();
        
  13. Lokesh Choudhary 2016-06-21

    Verified the fix. We get a 401 : Unauthorized callback. Closing. Environment: Appc Studio : 4.7.0.201606150733 Ti SDK : 5.4.0.v20160617074028, 6.0.0.v20160621124613 Ti CLI : 5.0.9 Alloy : 1.9.0 MAC El Capitan : 10.11.4 Appc NPM : 4.2.7-2 Appc CLI : 5.4.0-18 Node: 4.4.4 Nexus 6 - Android 6.0.1 Samsung Galaxy S3 - Android 4.0.4

JSON Source