2

I've encountered some odd behaviour with a jQuery ajax call to a resource in a parent directory when using HTTP Basic authentication.

It seems like there's something going on here which I don't understand, but I've not been able to find a good reference source which gives me enough detail to know what I'm doing wrong.

Can anyone point out what I'm missing, or point me at some source which will help me understand how browsers decide to send (or not to send) a WWW-Authenticate header on Ajax requests?


My setup looks something like the following.

Page: https://site.com/d1/d2/page.html (contains a jQuery ajax call to load 'Ajax')
Ajax: https://site.com/d3/some_resource.json

Both require the same HTTP Basic authentication (with the same realm)

Walking through the behaviour I see...

When requesting 'Page' in a new browser session, the user is challenged with an HTTP basic a login prompt, and if they give the right details 'Page' is loaded.

If the user tries to access 'Ajax' directly in a new browser session they are also challenged with a HTTP basic login, using the same realm. If the same details as above are used, the json content is loaded.

In the first case, I expect that loading 'Page' successfully should allow Javascript code within 'Page' to load 'Ajax', even though it's in a parent directory.

RFC 2617 - HTTP Authentication

The realm value (case-sensitive), in combination with the canonical root URL (the absoluteURI for the server whose abs_path is empty; see section 5.1.2 of [2]) of the server being accessed, defines the protection space.

Instead, what I see is that the ajax call fails (401 Unauthorized) and looking at the request through Firefox's console, it appears no 'WWW-Authenticate' header was sent.

However - If the user then loads 'Ajax' directly in the browser, the json content is loaded (no login prompt), and subsequent requests to 'Page' now successfully load the 'Ajax' resource. In this case I would have expected that loading 'Ajax' directly in the browser would have no impact.

(If it's important I'm using Firefox 30.0 and jQuery 1.10.2)

Community
  • 1
  • 1
Matt Sheppard
  • 116,545
  • 46
  • 111
  • 131
  • possible duplicate of [How to use Basic Auth and Jquery and Ajax](http://stackoverflow.com/questions/5507234/how-to-use-basic-auth-and-jquery-and-ajax) – Giacomo1968 Jun 23 '14 at 02:51
  • I marked this as a duplicate, but the link I am suggesting is a dupe of this topic suggests using `xhr.setRequestHeader ("Authorization", "Basic XXXXXX");` in a `beforeSend: function (xhr) {}` function when making the Ajax call. My gut tells me this might have more to do with quirks in how browsers handle authentication & the best way to solve is to send basic auth via the Ajax. – Giacomo1968 Jun 23 '14 at 02:52
  • I don't think its the same thing - In that question the caller wants to provide a username and password with the ajax request. In my case I want the authentication the browser already has to be used (and in fact, unless I can get the value for the header from the browser somehow, I wouldn't have the info necessary to populate it) – Matt Sheppard Jun 23 '14 at 03:00
  • “In my case I want the authentication the browser already has to be used.” Agreed. But that is why I say, “My gut tells me this might have more to do with quirks in how browsers handle authentication & the best way to solve is to send basic auth via the Ajax.” So this might be a question better suited for Super User? Maybe not, but do you see what I am saying… It’s a browser behavior issue not as much a script behavior issue. – Giacomo1968 Jun 23 '14 at 03:02
  • 1
    Yes, I see what you mean, but given I don't have the info to populate the header my only solution may be to change the URL layout (or to put some sort of proxy under d2). – Matt Sheppard Jun 23 '14 at 03:08
  • Perhaps. Or maybe just have the basic auth cover `https://site.com/`? Also, is your basic auth based on the `` or is it based on ``. I think that would play a factor in a case like this if you think about it. – Giacomo1968 Jun 23 '14 at 03:19
  • Any idea how, in general, I would make it cover the whole site? I'm using different contexts in a Jetty web server, not Apache, so it's not a case of putting Directory or Location directives, but if there's something concrete that's different between putting those at the top or on individual directories then understanding that might help me make sense of it. – Matt Sheppard Jun 23 '14 at 03:37
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/56079/discussion-between-matt-sheppard-and-jakegould). – Matt Sheppard Jun 23 '14 at 03:41
  • I don't do chats & I don't know how basic auth works in Jetty. I proxy anything like Jetty & Tomact through an Apache Reverse Proxy. So that is a whole other can if worms. If you wish do a search here & in Server Fault for my Apache Reverse Proxy tips. Past that, nothing else to add. Good luck! – Giacomo1968 Jun 23 '14 at 03:54
  • What is the directory structure and what HTTP server are you using? I am trying to reproduce your problem but I can not, I did test with Apache HTTP server with BASIC auth and "d3" alias mapped to /d1/d2/d3 but all works as expected. – vzamanillo Jul 03 '14 at 09:30

3 Answers3

3

Your problem here seems to be that the Authentication isn't sent along with the ajax request made by jQuery, this might be easily fixed with the xhrFields option on the ajax call....tough normally they are sent by default if you aren't working cross-domain. (proxys might cause that).

$.ajax({
   xhrFields: {
      withCredentials: true
   }
});

Another problem might be that the ajax call doesn't use the browers session. to check if that is the case, take a look on the request header sent by jQuery. The links in this answer might be of help in this case: https://stackoverflow.com/a/7189502/1821215

Community
  • 1
  • 1
Mr.Manhattan
  • 5,315
  • 3
  • 22
  • 34
1

HTTP Basic authentication is a challenge/response based authentication protocol; that's why this

When requesting 'Page' in a new browser session, the user is challenged with an HTTP basic a login prompt

or this happens

If the user tries to access 'Ajax' directly in a new browser session they are also challenged with a HTTP basic login

Where you can provide the credentials and if credentials are valid you get an OK response.

Regarding

I expect that loading 'Page' successfully should allow Javascript code within 'Page' to load 'Ajax', even though it's in a parent directory.

Even though your page loads successfully, the ajax call is a separate request for another resource and that's why you get this reponse:

(From the RFC 2617 - HTTP Authentication page 2)

The 401 (Unauthorized) response message is used by an origin server to challenge the authorization of a user agent. This response MUST include a WWW-Authenticate header field containing at least one challenge applicable to the requested resource.

Notice 'WWW-Authenticate:' is a response header sent by the server which tells the user agent the authentication method and the realm being accessed.

(From the RFC 2617 - HTTP Authentication page 3)

A user agent that wishes to authenticate itself with an origin server--usually, but not necessarily, after receiving a 401 (Unauthorized)--MAY do so by including an Authorization header field with the request.

And on page 4,5 it says:

To receive authorization, the client sends the userid and password, separated by a single colon (":") character, within a base64 encoded string in the credentials.

If the user agent wishes to send the userid "Aladdin" and password "open sesame", it would use the following header field:

  Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

Hope that makes sense.

And the suggested link https://stackoverflow.com/a/5507289/463478 should help you with the auhtentication of your ajax call.

Community
  • 1
  • 1
Only You
  • 2,051
  • 1
  • 21
  • 34
  • Thanks - Yes, I understand that. The issue is that I want the browser to populate the XHR object's WWW-Authenticate header - The Javascript in my page can't do so (because it doesn't know the username/password - Unless there's some way to get it I don't know about). The thing that's extra odd is that whether the browser does this or not is altered by manually loading the target page in the browser (not via XHR), so it's clearly possible :) – Matt Sheppard Jul 03 '14 at 03:49
  • @Matt OK I see. Then here's an idea, what about having the browser make that request for that some_resource.json and see if that adds the Authorization header? I don't have experience with client side script programming but could you do something like: var request = new XMLHttpRequest(); request.open("GET", "d3/some_resource.json", false); request.send(null); The GET request will look for the resource on the server that the current page came from. – Only You Jul 03 '14 at 12:48
0

You can use this function to authenticate via Ajax. In Opera and the oldest versions of IE the username and password not must be encoded.

I use this library https://github.com/ded/bowser To detect the browser

function http_auth(user_verified, password)

    http = new XMLHttpRequest();
    if (bowser.opera || bowser.msie) {
        http.open("get", url, false, user_verified, password);
    } else {
        http.open("get", url, false, encodeURIComponent(user_verified), encodeURIComponent(password));
    }

    http.send("");
    if (http.status == 200) {
        return true
    } else {
        return false;
    }
}

To prevent appearing of browser Login dialog you must ensure that user and password are correct.

torrentalle
  • 643
  • 4
  • 10
  • Thanks - My javascript code doesn't have the username and password to pass in - They were entered by the user into the initial request, and my expectation is that the browser should populate the authentication header automatically. – Matt Sheppard Jul 03 '14 at 03:51