I prefer simplicity and using the first example but I’d be happy to hear other options. Here’s a few examples:
HTTP/1.1 403 POST /endpoint
{ "message": "Unauthorized access" }
HTTP/1.1 403 POST /endpoint
Unauthorized access (no json)
HTTP/1.1 403 POST /endpoint
{ "error": "Unauthorized access" }
HTTP/1.1 403 POST /endpoint
{
"code": "UNAUTHORIZED",
"message": "Unauthorized access",
}
HTTP/1.1 200 (🤡) POST /endpoint
{
"error": true,
"message": "Unauthorized access",
}
HTTP/1.1 403 POST /endpoint
{
"status": 403,
"code": "UNAUTHORIZED",
"message": "Unauthorized access",
}
Or your own example.
That clown emoji would make me think the company has alt-right sympathies
A simple error code is sufficient in all of these cases. The error provided gives no additional information. There is no need for a body for these responses.
There may be a need for additional information, there just isn’t any in these responses. Using a basic JSON schema like the Problem Details RFC provides a standard way to add that information if necessary. Error codes are also often too general to have an application specific meaning. For example, is a “400 bad request” response caused by a malformed payload, a syntactically valid but semantically invalid payload, or what? Hence you put some data in the response body.
A plain 400 without explanation is definitely not great UX. But for something like 403, not specifying the error may be intentional for security reasons.
I know of some people that never use 403, but instead opt for 404 for security reasons. 403 implies that there is something they could have access to, but don’t.
I think in some situations that this can be valid, but it shouldn’t be a crux.
I like the last one, I think having the status code in the body could help clarify where the error is coming from when traversing a reverse proxy.
There are competing interests here: normal consumers and script kiddies. If I build an API that follows good design, RFCs, pretty specs, all of that, my normal users have a very good time. Since script kiddies brute force off examples from those areas, so do they. If I return 200s for everything without a response body unless authenticated and doing something legit, I can defeat a huge majority of script kiddies (really leaving denial of service). When I worked in video games and healthcare, this was a very good idea to do because an educated API consumer and a sufficiently advanced attacker both have no trouble while the very small amount of gate keeping locks out a ton of annoying traffic. Outside of these high traffic domains, normal design is usually fine unless you catch someone’s attention.
That’s true! It also seems like you might not have experience dealing with attacks at scale? Defense in depth involves using everything. If I can reduce incoming junk traffic by 80% by masking returns, I have achieved quite a lot for very little. Don’t forget the A in the CIA triad.
A mix between the last one and the previous. The key error
should be set in order to indicate the presence of an error, while status
and code
represents, respectively, the error numerical code and the error type, while the error message
is supposed to be concatenated to an human-friendly error text.