REST-API, proper HTTP status code for invalid DELETE
Asked Answered
K

3

22

I'm designing a RESTful API that is using the HTTP status codes and verbs as key components in communicating.

On the religious level it's on the zealot side of RESTafarian.

Rule of thumb for deciding HTTP status codes has been this graph, or similar resources.

  • GET /api/documents/1 - 401 User has not logged in
  • GET /api/documents/1 - 200 User has permission
  • GET /api/documents/1 - 403 User does not have permission
  • DELETE /api/documents/1 - 204 Users has permission
  • DELETE /api/documents/1 - 403 User does not have permission
  • GET /api/documents/2 - 404 Users permission irrelevant, resource does not exist
  • DELETE /api/documents/2 - 404 Users permission irrelevant, resource does not exist
  • DELETE /api/documents/1 - 404 Users has permission, resource already deleted
  • DELETE /api/documents/1 - 404 Users does not have permission, resource already deleted

Goals:

  • Consistency in usage
  • Not to expose private information through errors
  • Proper use of status codes for client or middle layer caches
  • Fail early, keep lookups to a minimum

In this situation there is a lot of different status code to chose from ( 404, 403, 410, 405) and in my case I went with 403 on a existing resource if its not yours to not clear the cache, and 404 on all non existing resources so to tell the clients to wipe that data.

But I do not like the switch from 403 to 404 on resources that are not yours.

I'm interested to hear from others how you solved this use-case, or in general what status codes you feel appropriate to send in all invalid DELETE calls, since I deem that as one of the hardest to be concise with.

(A lot of REST discussions and answers on the internet in it whole are just "Throw a 400 bad request, no one cares anyway", I do not have a problem that needs a quick fix or a pragmatic hack. Thanks)

Kiowa answered 6/5, 2014 at 6:0 Comment(0)
H
19

General pointer: In case a resource exists but a user is not authorized to perform operations on it, you should return 401 over 403:

401 Unauthorized

Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided.

and

403 Forbidden

The request was a valid request, but the server is refusing to respond to it. Unlike a 401 Unauthorized response, authenticating will make no difference.

See also Correct HTTP status code when resource is available but not accessible because of permissions


I went with 403 on a existing resource if its not yours to not clear the cache, and 404 on all non existing resources so to tell the clients to wipe that data.

As pointed out earlier, 401 should be used instead of 403. 404 is ok to return if you just want to say "sorry, resource not found". If you however want to say "resource was here but it's not anymore and never again will be" (this appears to be the case in your situation) you can return 410:

410 Gone

Indicates that the resource requested is no longer available and will not be available again. This should be used when a resource has been intentionally removed and the resource should be purged. Upon receiving a 410 status code, the client should not request the resource again in the future. Clients such as search engines should remove the resource from their indices


To summarize, this is how I would implement it in your case. The changes I made are in bold.

  • GET /api/documents/1 - 401 User has not logged in
  • GET /api/documents/1 - 200 User has permission
  • GET /api/documents/1 - 401 User does not have permission
  • DELETE /api/documents/1 - 204 User has permission
  • DELETE /api/documents/1 - 403 User does not have permission
  • GET /api/documents/2 - 404 Users permission irrelevant, resource does not exist
  • DELETE /api/documents/2 - 404 Users permission irrelevant, resource does not exist
  • DELETE /api/documents/1 - 410 User has permission, resource already deleted
  • DELETE /api/documents/1 - 401 User does not have permission, resource already deleted

For the last one, you can return 401 if you do not want the unauthorized user to know that there was a resource that has already been deleted. If you don't care you can return 410. That is for you to decide.

I do not like the switch from 403 to 404 on resources that are not yours.

It's perfectly fine to return different status codes depending on what the situation is.

I hope this helps you out a bit.

Hilversum answered 6/5, 2014 at 7:4 Comment(10)
A very good point, but I must disagree, dealing with 401 and 403. As the answer you linked to pointed out, in the wild, 401 usually prompts for username/password and 403 means permission denied. I'm are using Oauth2 tokens for access and a 401 would tell the client to kill the token and log out (e.g in case it expired or password change). So I interpreted that in the same way, therefore I would have to disagree to your proposed implementation.Kiowa
Fair enough, however it also says 403 is for a page where authenticating won't change anything. in your case authenticating would make a difference so I don't think you should use 403Hilversum
That specific sentence has kept me awake at night, also involved in a lot of discussions at the office. But that would mean that even if you log in as a super user, nothing would change. So I have come to interpet it as 403 - is for page where the same authentication won't change anything, in my case, successfully log in and obtaining a new token but using the same client_id/user/scopeKiowa
Maybe you misread that sentence? It does not say ..where the same authentication won't change anything, but it says 403 means any authentication whatsoever won't change anythingHilversum
Indeed it does not say so. As I pointed out, I interpreted it so. As did the answer you linked to, and also as he pointed out "in the wild", which I think means de facto standard, which is a common thing dealing with HTTP, and I do not mean to fight a lot of common implementation. As I've come to know it, 401 usually tells e.g a App on a phone that the user should be redirected to the login page. If a resource changes owner while the app is idle and the user later tries to delete it, it would result in the user being logged out even though his/hers token is valid for normal useKiowa
Ok in that case it was I who misread ;-) Anyway I hope this cleared some things up so that you can work from hereHilversum
@Kiowa if your issues are resolved, do you mind accepting either answer?Hilversum
in the end we chose to implement 410 as the status for deprecating the whole version of the api, since it speaks true in that the resource will never ever be available again. 404 was pragmatically kept as the code for already deleted resource.Kiowa
if the resource exists and the user is not authorized to delete that resource, the http spec says its also allowed to return a 404 :-) No must for a 401/403. quote:"...This status code is commonly used when the server does not wish to reveal exactly why the request has been refused..."Recognition
401 should only used when authentication is failed during request. So if you are authenticated but requesting someone else's resource a 401 will be misleading. For example someone can implement a client so that a 401 redirects to login page. However 403 (or hiding behind 404) implies you can continue with other requests without re-authorization.Purify
J
5

The response code for an invalid delete call depends on what the failure is. In your cases, I would go with:

  • DELETE /api/documents/1 - Users has permission
    • 204 No Content
  • DELETE /api/documents/2 - Users permission irrelevant, resource does not exist
    • 404 Not Found
  • DELETE /api/documents/1 - Users has permission, resource already deleted
    • 410 Gone
  • DELETE /api/documents/1 - Users does not have permission, resource already deleted
    • 403 Forbidden

The last call is the only one worth really talking about. I believe (and your graph agrees) that the user's lack of permission takes precedence over the resource already being deleted. If the user were to get a 410, then you'd be leaking information (resource already deleted).

As far as 401/403, 401 is "you haven't logged in yet". 403 is "you have logged in, and you don't have permission to do what you want". I don't see anything unusual in your usage of those codes.

Having said all this, I feel like I'm somehow misinterpreting the question.

Jeroldjeroma answered 6/5, 2014 at 15:9 Comment(0)
I
1

I don't like the idea of a 404 as representing a failed delete where the resource cannot be found (or for a put or patch for that matter). It is fairly common to have DNS issues and for people to have parameter based routing issues that would both yield a 404 if the actual site could not be found. Introducing this type of ambiguity can make diagnosing simple problems really and unnecessarily difficult. I think 410 Gone is a better choice for representing a resource not found when it comes to APIs.

Indihar answered 12/5, 2017 at 0:54 Comment(1)
how is "Gone" ('the target resource is no longer available at the origin server') better than "Not Found" ('The server has not found anything matching the Request-URI') when the resource was not found and not known to exist at all? Also, 'The 410 (Gone) status code SHOULD be used if the server knows (...) that an old resource is permanently unavailable.' Both RFC 7231 and common sense tells that Gone indicates that we are sure that something was present in the past. What you propose is not only invalid in light of RFC 7231, but also, IMVHO, simply absurd.Picot

© 2022 - 2024 — McMap. All rights reserved.