XMLHttpRequest handling unauthorized HTTP access

Discussion in 'Mac Programming' started by aforty, May 10, 2008.

  1. aforty macrumors 65816

    aforty

    Joined:
    Nov 27, 2007
    Location:
    Brooklyn, NY
    #1
    Hey guys, yea I suppose this could be asked at any old javascript forum but maybe someone here has come across this.

    I'm writing a Twitter widget with Dashboard. Twitter requires basic HTTP authentication and with the XMLHttpRequest() class that can be done in one of two ways:

    Code:
    [COLOR="red"]var feedURL = "http://" + username + ":" + password + "@twitter.com/statuses/friends_timeline.xml";[/COLOR]
    var xmlRequest = new XMLHttpRequest();
    xmlRequest.onreadystatechange = function() { xmlLoaded(xmlRequest); };
    xmlRequest.open("GET", feedURL, true);
    xmlRequest.send(null);
    
    Or, instead of the URL:
    Code:
    var feedURL = "http://twitter.com/statuses/friends_timeline.xml";
    var xmlRequest = new XMLHttpRequest();
    xmlRequest.onreadystatechange = function() { xmlLoaded(xmlRequest); };
    [COLOR="Red"]xmlRequest.open("GET", feedURL, true, username, password);[/COLOR]
    xmlRequest.send(null);
    

    So both of these methods work fine, when the authentication is successful. When it isn't it seems to thing just skips back after send() with no exception to catch, and no status code to indicate this.

    Anyone have any ideas on how to do this correctly?
     
  2. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #2
    In your anonymous function that is assigned to onreadystatechange, you don't seem to do anything to check this.status to see what HTTP response was given. I have no idea how the twitter API works but I hope it replies with something other than 200 OK when authentication fails.

    If it does respond with 200 OK, i would hope that in the response HTML or XML there is some way to discern that the request to login failed.

    -Lee

    P.S. In the handler (anonymous function assigned to onreadystatechange) you should be able to use this instead of the object's name.
     
  3. aforty thread starter macrumors 65816

    aforty

    Joined:
    Nov 27, 2007
    Location:
    Brooklyn, NY
    #3
    That's the problem, I think it is more of a problem with Safari/Webkit because the readyState never gets past 1 and the status never changes. It then sits there, presumably expecting another try at the login in hopes of getting it right.

    It's really quite bizarre.
     
  4. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #4
    That is quite odd. What happens when you use a "bad" username/password using curl? Do you get a response? Does the request hang?

    Edit:
    I tried this out of curiosity, this is what I got:
    Code:
    curl -v http://bib:stan@twitter.com/statuses/friends_timeline.xml
    * About to connect() to twitter.com port 80 (#0)
    *   Trying 128.121.146.100... connected
    * Connected to twitter.com (128.121.146.100) port 80 (#0)
    * Server auth using Basic with user 'bib'
    > GET /statuses/friends_timeline.xml HTTP/1.1
    > Authorization: Basic YmliOnN0YW4=
    > User-Agent: curl/7.16.3 (powerpc-apple-darwin8.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
    > Host: twitter.com
    > Accept: */*
    > 
    < HTTP/1.1 401 Unauthorized
    < Date: Sun, 11 May 2008 01:27:52 GMT
    < Server: hi
    < Status: 401 Unauthorized
    * Authentication problem. Ignoring this.
    < WWW-Authenticate: Basic realm="Twitter API"
    < P3P: CP="NOI DSP COR NID ADMa OPTa OUR NOR"
    < X-Runtime: 0.00823
    < Cache-Control: no-cache, max-age=300
    < Content-Type: text/html; charset=UTF-8
    < Content-Length: 27
    < Set-Cookie: _twitter_sess=BAh7CDoJdXNlcjA6B2lkIiU4Y2QyOTk2ZDc4YzQzYzAyMTNlNDhlYzJjN2E5%250AM2IzMiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo%250ASGFzaHsABjoKQHVzZWR7AA%253D%253D--56ab1819827b87ff2a7426539252f1e6c4e10e37; domain=.twitter.com; path=/
    < Via: 1.1 twitter-web019.twitter.com
    < Expires: Sun, 11 May 2008 01:32:52 GMT
    < Vary: Accept-Encoding
    < Connection: close
    < 
    * Closing connection #0
    It seems like you should get a response, and it should have a status of 401... so if you aren't there may be something wrong. I honestly am not familiar enough with the XMLHttpRequest to say if there's some issue with what you're trying.

    -Lee
     
  5. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #5
    Double post, sorry. I got interested and wanted to play with this.

    I set this up to test in safari:
    Code:
    <html>
      <body onload="trythis();">
        <head>
          <script language="JavaScript">
          function trythis() {
            var feedURL = "http://bib:stan@twitter.com/statuses/friends_timeline.xml";
            var xmlRequest = new XMLHttpRequest();
            alert(feedURL);
            xmlRequest.onreadystatechange = function() { alert(this.readyState); alert(this.status);};
            xmlRequest.open("GET", feedURL, true);
            alert("After open");
            xmlRequest.send(null)
            alert("After xmlReq");
          }
          </script>
        </head>
      </body>
    </html>
    I don't know what a widget will do, but I got a readyState of 1 with no status, then safari asked me to re-authenticate. Once i chose cancel I got readyState of 2 with 401 status, then a readyState of 4 with a 401 status. I'm afraid, as I said, I'm not sure how the dashboard will treat this. As far as i know things run in the same engine as safari, but maybe there are rules for authentication that require special handling.

    -Lee
     
  6. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #6
    Triple post, FTW!

    So after I did a bit of reading here:
    http://developer.apple.com/internet/webcontent/xmlhttpreq.html

    I tried changing:
    Code:
    xmlRequest.open("GET", feedURL, true);
    to
    Code:
    xmlRequest.open("GET", feedURL, false);
    There was no request from safari for re-authentication, and I got the onreadystatechange runs with 1:none, 2:401, 4:401 for readyState:status.

    The risk here is if there is no network connection, this may hang forever and there'd be nothing you can do in your code to handle that. Maybe try an asynchronous request first, and only if it works make the synchronous request? I really can't speak to best practices here, but wanted to share what I came up with.

    -Lee
     
  7. aforty thread starter macrumors 65816

    aforty

    Joined:
    Nov 27, 2007
    Location:
    Brooklyn, NY
    #7
    Wow great investigative work.

    Yea I figure it'd a bug of some sort, because a dashboard will suppress the login dialogue on failure but then cause it to never return a response and hang forever.

    Twitter does have a page that will confirm a login:pass combo as authentic, so I'm using that now in lieu of blindly attempting to login. Works OK, only problem in general I have now is that a user stays logged in indefinitely until the Widget is restarted, so changing the login/pass in the preferences will have no effect.

    Thanks again!
     

Share This Page