Multiple Authentication Schemes and WWW-Authenticate Challenges - rest

I am developing a REST API which supports multiple authentication schemes (OAuth, Bearer, and Basic). When the Authorization header is absent or contains an unsupported scheme, the service responds with multiple WWW-Authenticate headers:
WWW-Authenticate: OAuth realm="myRealm"
WWW-Authenticate: Bearer realm="myRealm"
WWW-Authenticate: Basic realm="myRealm"
When a request contains an Authorization header with one of the supported schemes but invalid credentials, should my service respond with all supported WWW-Authenticate schemes, or just the scheme provided in the request?
For example, if a client provides:
Authorization: Bearer invalid
Should my service respond with just the Bearer challenge?
WWW-Authenticate: Bearer realm="myRealm", error="invalid_token", error_description="token is malformed or represents invalid credentials"
Or should it respond with all WWW-Authenticate challenges?
WWW-Authenticate: Bearer realm="myRealm", error="invalid_token", error_description="token is malformed or represents invalid credentials"
WWW-Authenticate: OAuth realm="myRealm"
WWW-Authenticate: Basic realm="myRealm"
EDIT: RFC 7235 seems to provide a suggestion, although its not concrete. I've added an answer accordingly.

Although it's not strictly required, RFC 7235 seems to suggest that all supported authentication schemes should be returned. This will provide the most information to callers, provided they are able to parse these headers properly.
4.1. WWW-Authenticate
The "WWW-Authenticate" header field indicates the authentication
scheme(s) and parameters applicable to the target resource.
WWW-Authenticate = 1#challenge
A server generating a 401 (Unauthorized) response MUST send a
WWW-Authenticate header field containing at least one challenge. A
server MAY generate a WWW-Authenticate header field in other response
messages to indicate that supplying credentials (or different
credentials) might affect the response.

Related

Oauth2 Parameter Matchup

I'm new to rest and oauth. I was granted developer access to my law firm's billing software. I received a "developer account" with a username and password, and I registered my "app", at which point the sum total of all information I have is the following:
API Key
Secret Key
Username used to register the app (an email address)
Password used to register the app
The app name, which I came up with
The redirect url, which I provided
The "documentation" says that I need to pass the following URL-encoded line in the body of my request, with all parameters being required:
client_id={client_id}&client_secret={client_secret}&grant_type={grant_type}&code={code}&redirect_uri={redirect_uri}
I'm having a little trouble figuring out what's what. I believe client_id and client_secret are the api key and secret key provided when I registered the app. Does that sound correct?
I have no idea which grant type to use.
I have no idea what the "code" is or where to get it.
It almost seems like the developers didn't give me all the info I needed, but I could be wrong.
No it doesn't seem like enough info! Yes the API key and secret sound like the client_id and client_secret.
Generally the grant type will be dependent on what type of application you are developing. The most commonly used ones are:
Web App with user = Authorisation Code Grant
Javascript Browser App = Implicit Grant
Native / Mobile App = Authorisation Code Grant with PKCE.
Web App / API with no user context required = Client Credentials
Grant.
With the information you provide I'm not sure of the best fit for you but here's an example of the usual protocol to get a token using a couple of grant types:
The Authorisation Code Grant:
So you would make 2 calls to the law firm's Authorisation server:
1) GET /authorize?response_type=code&client_id=yourclientid
&redirect_uri=https%3A%2F%2Fyourappserver%2Ecom%2Fcallback HTTP/1.1
You would get a code sent back to your redirect endpoint which you'd then send on to the /token endpoint of the auth server.
2) POST /token HTTP/1.1
Host: server.yourlawfirm.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fyourappserver%2Ecom%2Fcallback
Note in this example the basic authentication code contains the client id and secret but often they are sent in the request body e.g.
2) POST /token HTTP/1.1
Host: server.yourlawfirm.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fyourappserver%2Ecom%2Fcallback
&client_id=yourclientid&client_secret=yoursecret
The authorisation server would then return you an access token from step 2)
If your application is going to talk to the law firm's billing software without the need for / context of a user then you would use the client credentials grant.
The Client Credentials Grant.
An example of this protocol is one call to the law firm's Auth server:
POST /token HTTP/1.1
Host: server.yourlawfirm.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
And this would return an access token.
Again the client id and secret are wrapped up in the basic auth header but it may be that you can supply them in the request body as I showed before.
e.g.
POST /token HTTP/1.1
Host: server.yourlawfirm.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=yourclientid&client_secret=yoursecret
This information is based on the OAuth2.0 specification.

What WWW-Authenticate header should a http server return in a 401 response when using form-based authentication?

I have a web application with a Javascript part running on the browser. That frontend uses several HTTP endpoints (more or less REST). The frontend must be able to distinguish between 401 and 403 responses and must not receive the 3xx redirects usually used for human users.
Authorization is done with a plain form login (no Javascript involved there), then a session cookie is used (for both "REST" and normal requests).
What would be a correct value for the WWW-Authenticate header value?
From RFC 7235: "A server generating a 401 (Unauthorized) response MUST send a WWW-Authenticate header field containing at least one challenge."
The Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry does not list any scheme for form-based authentication.
See also:
HTTP 401 Unauthorized when not using HTTP basic auth?
Authorization in RESTful HTTP API, 401 WWW-Authenticate
What would be a correct value for the WWW-Authenticate header value?
There's no point in returning 401 and WWW-Authenticate when you are not using HTTP Authentication as described in the RFC 7235. In HTTP Authentication, the client is expected to send the credentials in the Authorization header of the request with an authentication scheme token.
If you want to send the credentials in the request payload using POST, the 403 status code you be more suitable to indicate that the server has refused the request.
Since there is no standard challenge for this type of authentication, you are (as you predicted yourself) inventing your own.
I don't think there is a standard mechanism for specifying vendor tokens here, so I think the best thing you can do is use a token that's unlikely to clash with anything else.
Amazon has done this with AWS, and there's many others. My recommendation would be to use something like productname-schemename, for example acme-webauth.
In theory you should respond
HTTP/1.x 401 Unauthorized
WWW-Authenticate: cookie; cookie-name=my-session-cookie-name
However, real world internet browsers (user agents) do not really support this and in my experience the least bad option is to use HTTP status 403 for all of "Access Denied", "Unauthorized" or "Not allowed". If you have a REST service that's accessed with non-browser user-agents only, you could just follow the spec and return the above headers.
Also note that as described in RFC 7231 section 6.5.4 (https://www.rfc-editor.org/rfc/rfc7231#section-6.5.4) the server can respond with 404 for both "Access Denied" and "Not Found" so following should be okay for all user agents (browsers and standalone clients):
403 Unauthorized or Not allowed
404 Access Denied or Not Found
Or, you can just use 400 for any case because that should cause browser do fallback to generic error handling case. The HTTP status code 401 is especially problematic because it historically meant (at least in practice) that Basic Realm authentication is in use and the submitted HTTP header Authorization was not accepted. Because you cannot submit the session cookie via that header with commonly available browsers, those browsers have trouble handling 401 correctly when you use cookies for authentication. (According to the spec, HTTP 401 MUST include header WWW-Authenticate which should be used to figure out correct handling by the user agent but this doesn't seem to happen in reality with browsers.)
There is no HTTP authentication scheme for form based authentication.
Browsers implement basic, digest, etc. by showing a pop up where you can enter credentials.

HTTP 401 Unauthorized when not using HTTP basic auth?

When building a REST API that doesn't use HTTP basic authentication (but something else like an api-key) and the client provides invalid credentials, what HTTP Status Code are you supposed to return? 401 Unauthorized or 403 Forbidden?
The IANA HTTP Status Code Registry lists RFC7235, Section 3.1 as responsible for "401 Unauthorized", where it states:
The server generating a 401 response MUST send a WWW-Authenticate header field
Does that mean that a REST API should only ever return a 401 when using HTTP basic authentication but not when for example using authentication via an api-key?
Django seems to agree:
HTTP 401 responses must always include a WWW-Authenticate header, that instructs the client how to authenticate. HTTP 403 responses do not include the WWW-Authenticate header.
The kind of response that will be used depends on the authentication scheme.
While Richardson seems to disagree:
401 (“Unauthorized”)
Importance: High.
The client tried to operate on a protected resource without providing the proper authentication credentials. It may have provided the wrong credentials, or none at all.
The credentials may be a username and password, an API key, or an authentication
token—whatever the service in question is expecting. It’s common for a client to make
a request for a URI and accept a 401 just so it knows what kind of credentials to send
and in what format. [...]
You are assuming that the www-authenticate value needs to be basic. You can return a different value like "API-key" as the type of auth that needs to happen. So feel free to return 401 and www-authenticate header with some other value. You can even return multiple headers with different values indicating the different types of authentication that your app supports.

Is there a standard for using SAML tokens with RESTful services?

I'm using SAML tokens to authenticate against a set of REST-ful services, by putting the SAML token in the Authorization header.
I can't find anything out there that would suggest that there's a standard way to do this. For example, do I use:
Authorization: Bearer <EncryptedAssertion ...
or:
Authorization: Bearer PEVuY3J5cHRlZEFzc2VydGlvbiAuLi4=
or:
Authorization: SAML PEVuY3J5cHRlZEFzc2VydGlvbiAuLi4=
or something else?
Note that the first one doesn't work if the certificate has multiple name components (because the comma messes up the header parsing).
The fact that I'm using 'Bearer' doesn't say anything about the format of the token.
Apache CXF appears to use the third variant.
Which one is standard? Is there a standard? If not, is there a de-facto standard?
The standard for custom auth schemes in HTTP is defined in the RFCs 2617 and 7235.
Authorization: scheme key="value", ...
I doubt there is a standard for your specific case, but I'd say this is acceptable:
Authorization: SAML bearer="PEVuY3J5cHRlZEFzc2VydGlvbiAuLi4="
After doing quite a lot of research on this topic, I could not find any standard defining how to use a SAML Token in the Authorization header.
However CXF which is a quite famous Web-Serviec stack supports SAML Token in the following manner:
Authorization: SAML eJydV1m....9fYTCPr=
OAuth2 also defines how to authenticate with SAML Token to receive a OAuth2 Access Token which can then be used to invoke another REST Service (https://www.rfc-editor.org/rfc/rfc7522)
POST /token.oauth2 HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Asaml2-bearer&
assertion=PHNhbWxwOl...[omitted for brevity]...ZT4

determining web http authentication methods

How do you determine if a REST webservice is using Basic, Kerberos, NTLM, or one of the many other authentication methods?
When you send an unauthenticated request the service has to respond with a "HTTP/1.1 401 Unauthorized" and the response contains a WWW-Authenticate header that specifies what authentication scheme is expected (Basic, Digest), the security realm and any other specific value (like Digets's nonce). So if the server responds with:
HTTP/1.0 401 Unauthorized
WWW-Authenticate: Digest realm="example.com",
qop="auth,auth-int",
nonce="...",
opaque="..."
it wants a Digest authentication. If the response looks like:
HTTP/1.0 401 Unauthorized
WWW-Authenticate: Basic realm="example.com"
then it wants a Basic authentication. Some (poorly) implemented servers/sites don't handle the Basic correctly and respond directly with 403 Forbidden instead of challenging first.
NTLM is similar in as the server reponds with a 401 and a WWW-Authenticate header with the value NTLM, but there is no official public spec for it, since is Microsoft proprietary. There are various reverse engineered descriptions.
Unfortunately REST does not come with a WSDL style description of service to discover the authentication scheme used a priori.
You send it a request, presumably get an HTTP 401 code, and look at the WWW-Authenticate header that (per RFC 2616) the response MUST include. If instead you get a 403 or some other weird status, or a missing WWW-Authenticate header, you curse at website authors that don't follow the core HTTP RFC, and start sniffing the traffic to try to reverse engineer what nonstandard mess they've done this time;-).
If it's a black box scenario, I usually connect with Fiddler, and inspect the actual traffic.