Subject: [art] HTTP 3xx XOR 401

From: Nico Williams <[email-masked]>
Date: Fri, 22 Mar 2019 12:00:17 -0500

The rise of 3xx, redirect-based HTTP authentication/authorization
schemes has caused a problem: 3xx results are mutually-exclusive with
401-based authentication schemes.

This means: the server gets to choose {SAML, OAuth, ...} xor {API keys,
Negotiate (Kerberos, basically), Basic, SCRAM, ...}, but cannot "offer"
both and let the user-agent pick one.

The user-agent can "pre-flight" some HTTP authentication methods (API
keys, Negotiate/Kerberos) but not others (e.g., SCRAM), and other than
that.  Other than that, the only other way the user-agent can influence
the server's choice is via the User-Agent: header...

If you have a user-agent that cannot do OAuth (e.g., a non-browser
applications), then you have a problem: you have to know a priori to
pre-flight the API keys / Kerberos / whatever, or the server has to
decide which of 3xx or 401 to respond with based on... the User-Agent:
header value.

Oof.  Just sayin'.

Fixing this may require a time machine -- one that can go back in time,
and I don't have access to one.  Here's how I would fix this if I had
one:

 - Blend 3xx with 401.  E.g., 3xx responses could carry
   WWW-Authenticate: headers to indicate "chase the redirect OR
   authenticate".  Or 401s could carry an optional redirect URI, perhaps
   as an authentication method, perhaps with a header other than
   WWW-Authenticate:.

   This way user-agents can choose based on _their_ capabilities.

 - Let 3xx redirects from authorization headers carry Authorization:
   headers that user-agents can/must then add to the next request when
   chasing the referral.

   This way a very dumb user-agent that can use HTTP authentication or
   TLS user certificates to authenticate to an authorization server
   could just chase referrals to get authorized at the relying party.

There are user-agents and servers that support the second fix, actually!
The Windows PowerShell Invoke-WebRequest command-let has a
-PreserveAuthorizationOnRedirect option described as:

|   Indicates the cmdlet should preserve the Authorization header, when
|   present, across redirections.
|
|   By default, the cmdlet strips the Authorization header before redirecting.
|   Specifying this parameter disables this logic for cases where the header
|   needs to be sent to the redirection location.

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-webrequest?view=powershell-6

I suppose that implies that there are servers that do include an
Authorization header in 3xx replies, though I don't know which those
are.

Without a time machine... we can still do the above, but perhaps also:

 - Add a request header by which user-agents can idicate their
   authentication capabilities, which servers could use to choose
   between 3xx and 401.

   Since borwsers could quickly start supporting this, servers could
   default to 401 in the absence of this header sometime soon and end up
   doing the right thing 95% of the time.

Nico
--