I don't have a tremendous amount of experience building RESTful APIs; so, it's not always clear which HTTP status code in the 4xx block I should use when refusing to fulfill an incoming resource request. One tricky scenario that I've had to code against recently is the request for a properly formed, valid resource of which the authenticating user doesn't have permissions to view.
Image that we have two users in our system: Sarah, with ID 4, and Tricia, with ID 37. Now, imagine that Sarah makes an authenticated request to view Tricia's profile resource:
GET /users/37/profile HTTP/1.1
Authorization: Basic YmVuK2F206dGVzdA==
Here, Sarah is using Basic Authorization to identify herself as Sarah; however, she's making a request to another user's profile (Tricia's). For sake of argument, let's say that in this API, a user can only view his or her own profile. What HTTP status code should I return?
The three status codes that felt the most appropriate are:
- 401 - Unauthorized
- 403 - Forbidden
- 404 - Not Found
In my mind, the use of each of these three HTTP status codes could be justified. Sarah is not authorized to view Tricia's profile (401); Sarah is forbidden from viewing someone else's profile (403); and, Sarah simply cannot see resources that she's not allowed to view (404).
The initial problem that I had with using either of the HTTP status codes, 401 or 403, was that I felt like it was exposing secure information. Both of those responses sort of say, "Yeah, that resource exists, but you can't see it." My problem with this is that it confirms that those resources exist.
When you ask a Doctor if he treats a particular patient (at least in Law & Order - wicked awesome show!), he will often say something to the effect of, "Officer, you know I can neither confirm nor deny having a patient as it would be a breach of doctor-patient confidentiality." This is how I feel about 401 and 403 in this particular type of resource request - I don't want to confirm or deny its existence.
Then, one day, when I was reading over the description of the 403 Forbidden HTTP status code, something clicked. At the end of the description, it states:
The server understood the request, but is refusing to fulfill it. Authorization will not help and the request SHOULD NOT be repeated. If the request method was not HEAD and the server wishes to make public why the request has not been fulfilled, it SHOULD describe the reason for the refusal in the entity. If the server does not wish to make this information available to the client, the status code 404 (Not Found) can be used instead.
That last line seemed to solve my problem - if I don't want to expose the information, I should return a 404 Not Found instead. This definitely makes me feel the most comfortable with the response.
Now, clearly, I am not advocating the use of 404 Not Found instead of 401 Unauthorized or 403 Forbidden in all cases; these other HTTP status codes make much more sense in other contexts. I am simply saying that in the specific use case in which an authorized user is making a request for a valid resource that they don't have permissions to view, 404 Not Found feels like most secure solution.