Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct http status code for resource which requires authorization

There seems to be a lot of confusion about the correct http status code to return if the user tries to access a page which requires the user to login.

So basically what status code will be send when I show the login page?

I'm pretty sure we need to use a status code in the 4xx range.

I'm not talking about HTTP authentication here, so that's at least 1 status code we aren't going to use (401 Unauthorized).

Now what should we use? The answers (also here on SO) seem to vary:

According to the answer here we should use 403 Forbidden.

But in the description of the status code is:

Authorization will not help and the request SHOULD NOT be repeated.

Well that doesn't look like the right one. Since authorization WOULD help.

So let´s check out some other answer. The answer here even doesn't use the 4xx range at all but rather uses 302 Found

The description of the 302 Found status code:

The requested resource resides temporarily under a different URI. Since the redirection might be altered on occasion, the client SHOULD continue to use the Request-URI for future requests. This response is only cacheable if indicated by a Cache-Control or Expires header field.

I think that also isn't what I want. Since it is not the requested resource which resides under a different URI. But rather a completely different resource (login page vs authenticated content page).

So I moved along and picked another answer surprisingly with yet another solution.

This answer suggest we choose 400 Bad Request.

The description of this status code is:

The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.

I think the server understood the request just fine, but just refuses to give access before the user is authenticated.

Another answer also says a 403 response is correct, however it ends with:

If this is a public facing website where you are trying to deny access based on a session cookie [that's what I do], 200 with an appropriate body to indicate that log in is needed or a 302 temporary redirect to a log in page is often best.

So 403 is correct, but 200 or 302 is THE BEST.

Hey! That's what I am looking for: THE BEST solution. But shouldn't the best be the same as the correct one? And why would it be the best?

Thanks to all who have made it this far into this question :)

I know I shouldn't worry too much about it. And I think this question is more hypothetical (not really, but used it because of lack of a better word).

But this question is haunting me for some time now.

And if I would have been a manager (who just picked up some cool sounding words as they always do) I would have said: but, but, but, but restfulness is important. :-)

So: what is the right way™ of using a status code in the above situation (if any)?

tl;dr

What is the correct http status code response when a user tries to access a page which requires login?

like image 791
PeeHaa Avatar asked Dec 05 '11 17:12

PeeHaa


People also ask

What is meaning of HTTP status code 401 & 403?

401 Unauthorized is the status code to return when the client provides no credentials or invalid credentials. 403 Forbidden is the status code to return when a client has valid credentials but not enough privileges to perform an action on a resource.

What is HTTP status code1?

We tend to get -1 status codes when there are network issues or connection problems, so we display the user a network problems page in those cases.

What is the HTTP code related to resource?

302: “The requested resource has moved, but was found.” This code is used to indicate that the requested resource was found, just not at the location where it was expected. It is used for temporary URL redirection.

What is the difference between 200 and 201 status code?

The 200 status code is by far the most common returned. It means, simply, that the request was received and understood and is being processed. A 201 status code indicates that a request was successful and as a result, a resource has been created (for example a new page).


2 Answers

If the user has not provided any credentials and your API requires them, return a 401 - Unauthorized. That will challenge the client to do so. There's usually little debate about this particular scenario.

If the user has provided valid credentials but they are insufficient to access the requested resource (perhaps the credentials were for a freemium account but the requested resource is only for your paid users), you have a couple of options given the looseness of some of the HTTP code definitions:

  1. Return 403 - Forbidden. This is more descriptive and is typically understood as, "the supplied credentials were valid but still were not enough to grant access"
  2. Return 401 - Unauthorized. If you're paranoid about security, you might not want to give the extra information back to the client as was returned in (1) above
  3. Return either 401 or 403 but with helpful information in the response body describing the reasons why access is being denied. Again, that information might be more than you would want to provide in case it helps attackers somewhat.

Personally, I've always used #1 for the scenario where valid credentials have been passed but the account they're associated with doesn't have access to the requested resource.

like image 171
Brian Kelly Avatar answered Oct 06 '22 20:10

Brian Kelly


You ask for "the best", "the right way", and "the correct", in turn, which makes answering this question difficult because those criteria are not necessarily interchangeable and may, in fact, conflict -- especially where RESTfulness is concerned.

The "best" answer depends on your application. Are you building a Plain Old Browser-Based (POBB) web-application? Are you building a native client (ex. iOS or Android) and hitting a service over the Web? Are you making heavy use of AJAX to drive web-page updates? Is curl the intended client?

Let's assume you are building a traditional web application. Let's look at how Google does it (output chopped for brevity):

$ curl -v http://gmail.com/ < HTTP/1.1 301 Moved Permanently < Location: http://mail.google.com/mail/ < Content-Type: text/html; charset=UTF-8 < Content-Length: 225 < ... 

Google first redirects us to the "true" URL for GMail (using a 302 redirect).

$ curl -v http://mail.google.com/mail/ < HTTP/1.1 302 Moved Temporarily < Location: https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false&continue=http://mail.google.com/mail/&scc=1&ltmpl=default&ltmplcache=2 < Content-Type: text/html; charset=UTF-8 < Content-Length: 352 < ... 

And then it redirects us to the login page (using a 302 redirect).

$ curl -v 'https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false&continue=http://mail.google.com/mail/&scc=1&ltmpl=default&ltmplcache=2' < HTTP/1.1 200 OK < Content-Type: text/html; charset=UTF-8 < Transfer-Encoding: chunked < ... 

The login page itself is delivered with the 200 status code!

Why this way?

From a user-experience perspective, if a user goes to a page they can't view because they are not authenticated, you want to take the user to a page that allows them to correct this (via logging in). In this example, the login page stands alone and is just another page (which is why 200 is appropriate).

You could throw up a 4XX page with an explanation and a link to the login page. That might, in fact, seem more RESTful. But it's a worse user experience.

Ok, but is there a case where something like 403 makes sense? Absolutely.

First, though, note that 403 isn't well-defined in the specification. In order to understand how it should be used, you need to look at how it's implemented in the field.

403 is commonly used by web servers like Apache and IIS as the status code for pages returned when the browser requests a directory listing (a URI ending in "/") but the server has directory listings disabled. In this case, 403 is really a specialized 404, and there isn't much you can do for the user except let him/her know what went wrong.

However, here's an example of a site that uses the 403 to both signal to the user that he/she doesn't have sufficient privilege and what action to take to correct the situation (check out the full response for details):

curl -v http://www.w3.org/Protocols/rfc2616/ < HTTP/1.1 403 Forbidden < Content-Type: text/html; charset=iso-8859-1 < Content-Length: 1564 < ... 

(As an aside, 403 is also seen in web-based APIs, like Twitter's API; here, 403 means "The request is understood, but it has been refused. An accompanying error message will explain why. This code is used when requests are being denied due to update limits.")

As an improvement, let's assume, however, that you don't want to redirect the user to a login page, or force the user to follow a link to the login page. Instead, you want to display the login form on the page that the user is prevented from seeing. If they successfully authenticate, they see the content when the page reloads; if they fail, they get the login form again. They never navigate to another URL.

In this case, a status code of 403 makes a lot of sense, and is homologous to the 401 case, with the caveat that the browser won't pop up a dialog asking the user to authenticate -- the form is in the page itself.

This approach to authentication is not common, but it could make sense, and is IMHO preferable to the pop-up-a-javascript-modal-to-log-in solutions that developers try to implement.

It comes down to the question, do you want to redirect or not?

Additional: thoughts about the 401 status code...

The 401 status code -- and associated basic/digest authentication -- has many things going for it. It's embraced by the HTTP specification, it's supported by every major browser, it's not inherently un-RESTful... The problem is, from a user experience perspective, it's very very unattractive. There's the un-stylable, cryptic pop-up dialog, lack of an elegant solution for logging out, etc. If you (or your stakeholders/clients) can live with those issues (a big if) then it might qualify as the "correct" solution.

like image 21
toddsundsted Avatar answered Oct 06 '22 18:10

toddsundsted