Bugzilla – Bug 15418
sort out HTTP auth
Last modified: 2013-11-01 17:17:28 UTC
The relationship between XMLHttpRequest and HTTP auth is kind of sketchy at the moment and might not match implementations. There's requirements on the Authorization header, user/password arguments, user agent supplied Authorization headers, and they should really be reconciled somehow.
I have looked at the spec and haven't seen anything that is obviously wrong.
Do you have something specific in mind?
In http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#same-origin-request-steps it is not really explicit what should be done with username/password. In the context of HTTP it probably means creating an Authorization header. That should be explicit I think. But with if an Authorization header has been set via setRequestHeader()? If the user agent has no knowledge of the request URL, should it first do a challenge? Do we need to call that out?
*** Bug 19969 has been marked as a duplicate of this bug. ***
Copying over my opinions from the duplicate bug :-)
IMO we should clarify the following:
1) Add a note (maybe just informative?) saying user name / password from open() method will only be sent to a site if it first uses a 401 response to indicate that authentication is required.
2) Figure out what should happen if a script calls open() with user name/password arguments, then sets an Authorize header with setRequestHeader(). Which wins? Will it depend on whether the site says 401 or not?
(IMO: setRequestHeader() should win if this is compatible with implementations, simplifies things. Whether or not there is a 401 response should make no difference. Hope that's sufficiently aligned with implementations..)
3) I assume that if setRequestHeader() adds an Authorize header, it's sent to the server whether or not a 401 request has been returned. Perhaps this should also be noted.
Okay, open() username/password override URL's username/password now:
I think "HTML fetch" and at some point "Fetch" needs to tie up the loose ends here. I.e. the point where you try to construct an HTTP request from such a URL. This is after all an issue for <img> and such too (although they do not have the conflict with an Authorization header already being set).
And which wins here?
x.open('GET', 'http://foo1:firstname.lastname@example.org/', true, 'foo2', 'bar2');
x.setRequestHeader('Authorization', 'Zm9vMzpiYXIz');// foo3:bar3
Does the spec cover it already? This should be defined here and not in Fetch, I guess.
I think that should be defined in Fetch, because it's up to the networking library. At least as I understand Gecko's implementation, presumably others have copied that.
Having said that, testing it and adding a note to XMLHttpRequest would definitely be useful. And maybe we should address it in XMLHttpRequest as XMLHttpRequest is the only API that hooks into Fetch that can have a custom Authorization header (thus far), and before Fetch is worked on again will be a while.
I was thinking it should be defined here because the question doesn't come up in other contexts (at least not yet). AFAIK specifying a sort of "last one wins" seems to make sense, but it's complicated to say user:pass or "user","pass" adds an Authorize header to the user defined headers list because it will only be sent on a 401 response.
So I think if we let this get handled by Fetch it will include the Authorization header on the first try and then either you will get a 200 or a 403. If you do not include it you might get a 401 and URL's username/password will be used. I suspect we should let Fetch handle 401's automatically in that case and otherwise defer to UI (though more testing is needed for <script>/<img>/...).
(At some point we might want to offer a way in which 401 gets back to XMLHttpRequest without the UA trying do do smart things. Same with redirects.)
Created attachment 1353 [details]
Flow diagram, sort of
Created attachment 1354 [details]
Flow diagram, somewhat simplified
Created attachment 1355 [details]
XHR auth flow diagram
Created attachment 1358 [details]
XHR auth flow diagram
Created attachment 1359 [details]
XHR auth flow diagram
I've added some tests:
- asserts that browsers should *not* send Authorization until they've seen a 401 challenge, should use user name and password from open() if they have received 401, should not pass on 401 response details to JS
- asserts that if setRequestHeader() is used to add an Authorization header, it should be sent immediately whether or not there has been a 401 challenge.
- asserts that user name and password from open() should not be used in CORS requests (even if the JS sets withCredentials and the remote server explicitly allows both credentials and Authorization header). This is obviously a limitation that's based on these arguments as far as I can tell:
* because it might add some complexity to implementations
* because we feel like limiting it :-p
- asserts that when using setRequestHeader() to add Authorization: header, the header should be sent if the remote server allows Authorization headers. This would allow a (less convenient) way to use HTTP Auth in cross-origin requests even if we disallow open() user/pass. Fails in all current browsers.
So, largely, implementations don't let you do cross-origin XHR auth at all..
I basically want cross-origin HTTP auth to work like this:
When send() is called for a CORS XHR with user/pass supplied:
CORS Options request..
-> Response that indicates Authorization header is OK
Request without auth..
-> 401 response
Request with auth
-> Real response
It's a little bit slow with two preliminary requests, but I don't think it's that complex actually.
Comment 17 describes a new feature and that should be out of scope of this bug.
(In reply to Anne from comment #18)
> Comment 17 describes a new feature and that should be out of scope of this
But wouldn't it be better if code that manages credentials doesn't have to worry about whether it's a same-origin or a CORS request?
Maybe. We already discussed this. It's not clear how often HTTP authentication is used, much less cross-origin. This can be worked around in script. Let's for now focus on defining the existing feature set as it's already far from fully interoperable and understood.
(In reply to Anne from comment #20)
> This can be worked around in script.
CORS authentication? What workarounds do you have in mind?
The only workarounds I can think of is adding the credentials data to the posted body instead of using HTTP auth, or having authenticated sessions running and use withCredentials.
> Let's for now focus on defining the existing feature set as it's
> already far from fully interoperable and understood.
Well, on the HTTP level auth is not a new feature at all ;-). So IMHO comment #17 is part of trying to "define the existing feature set".
You can do the whole dance yourself with a custom-generated Authorization header. cross-origin HTTP authentication is not part of the existing feature set and was explicitly excluded when we designed CORS. Let's not play games and definitely not in this bug which is about figuring out what is actually implemented :/
Per my last statements in comment #16 no browser supports that at this point..
I'm just nagging because I write tests for this stuff and I want to be fairly sure that they are asserting the right things. Writing tests seems like a natural exercise in thinking like an author, and I think I'm just raising opinions that sort of follow from trying to think like an author. I don't think I was involved when "we" excluded HTTP auth from CORS. Sorry if you think I'm "playing games".
I think this is fixed now, basically because Fetch defines the right thing. Might need to make it a little bit clearer how authentication entries are scoped.
I'm considering this fixed. Follow ups are bug 23704, bug 23706, and bug 21013 it seems.