Why do we send 302 on signup and login? - redirect

Typically, it seems that most web applications will respond with a 302 if a new user signs up or an old user signs in.
i.e.
client ---signup req-----------------------> server
server ---302 Temporarily Moved or Found---> client
Doesn't it make more sense to redirect with 200 or just say Found and separately redirect the client? There is no resource that has temporarily moved in this case, rather they've been successfully authenticated. I'm confused since 302 Temporarily Moved and Found seem to contradict each other based on what their names indicate indicate.
RFC 1945 doesn't indicate anything about the Found status code but only the Temporarily Moved.

I figured out the answer. Perhaps this will help someone else.
There are different ways of doing authorization. Some are standardized, others are not. Examples are Digest Auth (in HTTP Spec), Basic Auth (HTTP Spec) and Forms Based Auth.
There are pros and cons to each method. But, Basic and Digest Auth both give 401 Unauthorized Codes. Forms-Based Authorization however prefers using the 302 Temporary Redirect to a login page. This is so that it gives the control of application login experience to the developers themselves.
Forms-based auth is the most popular even though it is not standardized. Typically the client sends a request, server returns 302, client sends another request at new login page, server sends back with set-cookie headers.

Related

Why do browsers automatically redirect to urls in location?

When a response to a certain request is redirection, with status code 3XX and location header, it seems that the browser automatically redirects to the other page (by URLs included in location header).
But when I send the same request via cURL or postman, the response comes with the 3XX but no redirect occurs.
So I concluded that the redirection from the first case is not a basic response but an additional function that browsers.
If so where I can find the settings about this? If not, what is the main cause of this redirection?
The browser is trying to give the user a good experience. If the web site returns a redirect, then the best user experience is for it to go ahead and do the redirect. cURL and wget will also follow the redirects if you enable the option, but since those tools are mostly for diagnostics, the default is to have one request, one response. It's up to the consumer to decide what to do with that response.
I'm not sure what you mean by "basic response". The RFC does not require a redirect. The request receives a response, and it's up to the recipient to decide what to do next.

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.

Optional SPNEGO Kerberos authentication

Is it possible to do optional kerberos authentication?
What I want is: if the client (browser) is not on the domain it is redirected to a username/password web login. Otherwise it will do SPNEGO do Kerberos authentication.
If I just send the WWW-Authenticate: Negotiate header to a non domain browser it just does nothing further.
Is there some option to tell the browser to try something different if it doesn't know how to authenticate? Or do I have to determine if user is part of the domain before sending the "WWW-Authenticate" header?
I haven't found anynone who has solved this publicly and in a standard way. Yes, as mentioned, one could fall back to Basic but that doesn't work for authentication schemes which involve requesting a username and password from a CGI form where, as far as the browser sees things, you're falling back to no authentication if Negotiate fails. Maybe that is suggestive that the authentication scheme is broken? I don't know.
I'll tell you what I know, first. Our site is, effectively, Cosign-protected, so we have a similar problem to you: only specially-configured machines respond to the WWW-Authenticate header, so by default we must send all users to our Cosign login page. The trick is that the Cosign server also allows authenticated GSSAPI/Kerberos hosts to complete the authentication process without entering login details, but only on certain browsers, by means of a workaround.
This workaround consists simply of a block of JavaScript within the login page which attempts a HEAD of an SPNEGO-protected resource; if successful, the script redirects the browser to an SPNEGO-protected version of the same page, which grants appropriate Cosign cookies and completes the process without password entry. If the browser lacks any one of JavaScript, Kerberos support or adequate credentials, then the user will see the cosign login page as usual.
So, the above alone might count as an answer to your question; personally though I don't think this goes far enough and what follows is more of a discussion...
The above seems unsatisfactory as it insists that any connecting user agent supports either JavaScript (unlikely to be the case for text-based browsers and HTTP client libraries) or knowledge of the arbitrary path to which we redirect Kerberos-capable users (useless to anything which has not been hard-coded for our site). I've come to the conclusion that there might be a better workaround, or if not, a gap where a standard should be. The best practical suggestion I have is this:
A normal part of the SPNEGO process is that the client attempt to retrieve a page whose initial response is an HTTP 401 but with the header WWW-Authenticate: Negotiate. This is the cue for a GSSAPI/Kerberised client to respond appropriately; a "regular" client will simply display the error page. Perhaps the solution is simply to modify the Cosign server to deliver the human-friendly login page as part of this error response?
It might be technically difficult with off-the-shelf Apache and modules, and might go against various standards (or at least principles). I'm no expert on the systems involved, so can only speculate unless (or until) I get a chance to try it out...
Send additionally WWW-Authenticate: Basic for username/password challenge.
It depends on the application server, for example in JBoss you can set <auth-method>SPNEGO,FORM</auth-method> in web.xml and it should fall back to a login form "in cases where Kerberos/SPNEGO tokens are not present":
See https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.2/html/how_to_set_up_sso_with_kerberos/additional_features

When does HTTP Authentication occur? Is one of them by using http://peter:mypassword#www.somesite.com?

There seems to be 2 HTTP Authentication: Basic access authentication and Digest access authentication
So I think in general, a user tries to access a URL, and the web server returns 401 Unauthorized, and then the browser
pops up an app window, asking for username and password, and then set the credentials in the HTTP headers then sends the HTTP request again.
What about http://peter:mypassword#www.somesite.com ? Is that supposed to not wait for the 401 to come back but provide the username and password in advance?
Some how, I tried http://peter:mypassword#www.google.com or yahoo but inside of Fiddler (to monitor net traffic), I don't see any credential info in the HTTP request?
You still need to send the 401 on the server side. The user:pass#host is just a convenience to avoid displaying the login dialog.
If you think about it, this makes sense, because if you don't send the 401 with the header explaining which method to use, the client doesn't know how to format the credentials.
(There are actually any number of schemes, not just Basic and Digest.)
For some understanding of it, there is a Railscast that talks about HTTP Basic Authentication and adding it to a Rails project and how it looks like on a browser: http://railscasts.com/episodes/82-http-basic-authentication

Using HTTP status codes in web applications

I'm currently building a web application (utilizing Zend Framework) and have gotten some of the basic HTTP status codes down, such as:
404 on missing controller/action
500 on server exception
3xx returned by Apache
At the moment I have just implemented some basic ACL checking that the user is authorized to access a certain resource. It's implemented as a controller plugin, doing its thing on the routeShutdown event. It works like follows:
Get the role of the user. If the user is not logged in, assign the role of 'guest'
Check that the user's rule has access to the resource
2.1. If the does not have access to the resource and is a guest, save the resource he was trying to access, and forward him to a login prompt. Once he has supplied his credentials, he is redirected back to the original resource (via a HTTP redirect status code)
2.2. If the user is authenticated and the ACL denies him access to the resource, he is forwarded to the error controller, to an action I've called noPrivilegies.
If the user does have access, let the request continue as usual.
Now, my questions:
Can/should I use HTTP 401 for the 2.1 scenario? It's not like I want a WWW-Authenticate header field sent by the client. I just need him to login through my login-form. But I still think that the request wasn't a 200 OK, since he didn't get access to the requested resource.
Can/should I use HTTP 401 for the 2.2 scenario? Same reason here, WWW-Authenticate won't help. Perhaps it's better to use HTTP 403 Forbidden?
Do you recommend any other status codes for these two scenarios?
What other status codes do you usually return from your application, and when are they applicable?
Provided that the guest role may be allowed to perform some actions, then no 4xx response or other form of error is warranted at stage 2.1 of your process because not being logged in is not an error.
Neither 401 nor 403 is really ideal for an application-level "permission denied", but weighing 401's "The response MUST include a WWW-Authenticate header field" against 403's "Authorization will not help", I would say (and most cases I've encountered to date seem to agree) that 401 is the appropriate response if you are requesting HTTP level authentication before proceeding and 403 is appropriate in all other cases. So probably 403 in your case.
I think that 403 is the correct response. Per the HTTP spec, 401 is for cases where HTTP-level authentication is possible and required, 403 is for cases where the user does not have permission to access the resource. In the case of your app, the user has logged in, and it's unreasonable to expect that s/he will be able to use a different account.
Normally I don't ever use the status codes to report 'application' level authentication failures. That's generally reported via the app.
And it makes sense to do that; because they can access the 'page' from a 'http' sense; they just can't access your applications processing of that page, if that makes sense.
What I'm saying is generally you don't worry about the codes, or set them yourself at all, when returning data to the client. Leave it to the webserver and provide an 'abstracted' method of responding; i.e. the typical 'invalid username or password' page, etc.
Just my view, though.
i've recently implemented something that works in a similar way, we return a 401 response when a user has to login and a 403 if after login they've tried accessing something that they don't have permission to access.
you can also set the reason phrase in the http response to something to denote the reason for the failure.
Even not specifically stated in HTTP spec, the common practice is
401 - For authentication error
403 - For authorization error
Try not to use 401 if you don't want authenticate user. I recently encountered a problem with a HTTP client (not one of the popular browsers) that it logs an error when it sees 401 but not WWW-Authenticate header. Granted this is a bug on the client side but it shows how people perceives 401.