Bug 15418 - sort out HTTP auth
Summary: sort out HTTP auth
Alias: None
Product: WebAppsWG
Classification: Unclassified
Component: XHR (show other bugs)
Version: unspecified
Hardware: PC Windows 3.1
: P2 normal
Target Milestone: ---
Assignee: Anne
QA Contact: public-webapps-bugzilla
: 19969 (view as bug list)
Depends on:
Reported: 2012-01-04 14:39 UTC by Anne
Modified: 2013-11-01 17:17 UTC (History)
6 users (show)

See Also:

Flow diagram, sort of (45.62 KB, image/png)
2013-05-02 13:15 UTC, Hallvord R. M. Steen
Flow diagram, somewhat simplified (43.63 KB, image/png)
2013-05-02 13:30 UTC, Hallvord R. M. Steen
XHR auth flow diagram (43.93 KB, image/png)
2013-05-03 10:09 UTC, Hallvord R. M. Steen
XHR auth flow diagram (50.44 KB, image/png)
2013-05-06 16:09 UTC, Hallvord R. M. Steen
XHR auth flow diagram (56.17 KB, image/png)
2013-05-06 17:01 UTC, Hallvord R. M. Steen

Note You need to log in before you can comment on or make changes to this bug.
Description Anne 2012-01-04 14:39:20 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.
Comment 1 Julian Reschke 2012-01-04 20:56:59 UTC
I have looked at the spec and haven't seen anything that is obviously wrong.

Do you have something specific in mind?
Comment 2 Anne 2012-01-04 21:14:02 UTC
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?
Comment 3 Anne 2012-11-15 13:50:03 UTC
*** Bug 19969 has been marked as a duplicate of this bug. ***
Comment 4 Hallvord R. M. Steen 2012-11-15 13:57:16 UTC
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.
Comment 5 Anne 2012-11-22 14:59:46 UTC
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).
Comment 6 Hallvord R. M. Steen 2012-11-23 13:43:53 UTC
And which wins here?

x.open('GET', 'http://foo1:bar1@example.com/', 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.
Comment 7 Anne 2012-11-23 13:47:41 UTC
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.
Comment 8 Anne 2012-11-23 13:49:37 UTC
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.
Comment 9 Hallvord R. M. Steen 2012-11-23 18:25:04 UTC
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.
Comment 10 Anne 2012-11-24 08:55:13 UTC
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.)
Comment 11 Hallvord R. M. Steen 2013-05-02 13:15:21 UTC
Created attachment 1353 [details]
Flow diagram, sort of
Comment 12 Hallvord R. M. Steen 2013-05-02 13:30:39 UTC
Created attachment 1354 [details]
Flow diagram, somewhat simplified
Comment 13 Hallvord R. M. Steen 2013-05-03 10:09:51 UTC
Created attachment 1355 [details]
XHR auth flow diagram
Comment 14 Hallvord R. M. Steen 2013-05-06 16:09:12 UTC
Created attachment 1358 [details]
XHR auth flow diagram
Comment 15 Hallvord R. M. Steen 2013-05-06 17:01:46 UTC
Created attachment 1359 [details]
XHR auth flow diagram
Comment 16 Hallvord R. M. Steen 2013-05-16 14:18:37 UTC
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..
Comment 17 Hallvord R. M. Steen 2013-10-08 15:10:40 UTC
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 18 Anne 2013-10-15 12:29:40 UTC
Comment 17 describes a new feature and that should be out of scope of this bug.
Comment 19 Hallvord R. M. Steen 2013-10-16 07:16:24 UTC
(In reply to Anne from comment #18)
> Comment 17 describes a new feature and that should be out of scope of this
> bug.

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?
Comment 20 Anne 2013-10-16 11:05:52 UTC
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.
Comment 21 Hallvord R. M. Steen 2013-10-16 11:18:08 UTC
(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".
Comment 22 Anne 2013-10-16 11:27:48 UTC
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 :/
Comment 23 Hallvord R. M. Steen 2013-10-16 12:56:05 UTC
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".
Comment 24 Anne 2013-10-23 17:21:11 UTC
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.
Comment 25 Anne 2013-11-01 17:17:28 UTC
I'm considering this fixed. Follow ups are bug 23704, bug 23706, and bug 21013 it seems.