Should my API method return a 401 or a 204? - api-design

I have a client coded in JavaScript. This client calls an API method on my server which is coded in PHP. This API method is "GET /user" and could return a 200 status or a 204 status.
The 200 status is intended for authenticated users.
The 204 status is intended for guest users and can in the future become a 200 with information about these guest users (like frontend autorizations).
I had a confrontation with developers who think my API should return a 401 status in case the user is a guest. But I said there was no client error since the client just ask for the authentication state + some information about the non guest user.
So my question is pretty simple: is there a missconception in my API ?

I would suggest neither.
Take a look at section 10 of the HTTP/1.1 RFC and see if you can match your response code with what is being returned.
If you are returning data from GET /users (regardless of authenticated client or guest) then 204 No Content is not appropriate:
The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields.
A 401 Unauthorized is only appropriate if the method being called is only accessible to authenticated clients. You are essentially telling the client that they have done something they are not allowed to.
If both an authenticated user and a guest user are expected clients of your API I would suggest returning a 200 OK to both clients and have any differentiation made clear in the returned body itself.

I totally agree with the answer of #Johnson. Do not change the meaning of the status codes. If it is a valid request, you should respond with 200.
The problem here is that the endpoint you define is not a resource-oriented endpoint (problem = REST API design guidelines are not really applicable as they are mostly focused on resource oriented APIs). /user in your case seems to return a state rather than a resource. 401 is good for telling the client that it is not authenticated and thus cannot fetch the requested resource.
In your case it is just an informative endpoint for the client to check its login state and both states (logged in and not logged in) are successful answers from a technical point of view and thus require a 200 response. To tell the client the login state you should use the response body, not the status code.

Related

Forbidden (HTTP 403) when scraping CSV data in R [duplicate]

For a web page that exists, but for which a user does not have sufficient privileges (they are not logged in or do not belong to the proper user group), what is the proper HTTP response to serve?
401 Unauthorized?
403 Forbidden?
Something else?
What I've read on each so far isn't very clear on the difference between the two. What use cases are appropriate for each response?
A clear explanation from Daniel Irvine [original link]:
There's a problem with 401 Unauthorized, the HTTP status code for authentication errors. And that’s just it: it’s for authentication, not authorization.
Receiving a 401 response is the server telling you, “you aren’t
authenticated–either not authenticated at all or authenticated
incorrectly–but please reauthenticate and try again.” To help you out,
it will always include a WWW-Authenticate header that describes how
to authenticate.
This is a response generally returned by your web server, not your web
application.
It’s also something very temporary; the server is asking you to try
again.
So, for authorization I use the 403 Forbidden response. It’s
permanent, it’s tied to my application logic, and it’s a more concrete
response than a 401.
Receiving a 403 response is the server telling you, “I’m sorry. I know
who you are–I believe who you say you are–but you just don’t have
permission to access this resource. Maybe if you ask the system
administrator nicely, you’ll get permission. But please don’t bother
me again until your predicament changes.”
In summary, a 401 Unauthorized response should be used for missing
or bad authentication, and a 403 Forbidden response should be used
afterwards, when the user is authenticated but isn’t authorized to
perform the requested operation on the given resource.
Another nice pictorial format of how http status codes should be used.
Edit: RFC2616 is obsolete, see RFC9110.
401 Unauthorized:
If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials.
403 Forbidden:
The server understood the request, but is refusing to fulfill it.
From your use case, it appears that the user is not authenticated. I would return 401.
Something the other answers are missing is that it must be understood that Authentication and Authorization in the context of RFC 2616 refers ONLY to the HTTP Authentication protocol of RFC 2617. Authentication by schemes outside of RFC2617 is not supported in HTTP status codes and are not considered when deciding whether to use 401 or 403.
Brief and Terse
Unauthorized indicates that the client is not RFC2617 authenticated and the server is initiating the authentication process. Forbidden indicates either that the client is RFC2617 authenticated and does not have authorization or that the server does not support RFC2617 for the requested resource.
Meaning if you have your own roll-your-own login process and never use HTTP Authentication, 403 is always the proper response and 401 should never be used.
Detailed and In-Depth
From RFC2616
10.4.2 401 Unauthorized
The request requires user authentication. The response MUST include a WWW-Authenticate header field (section 14.47) containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field (section 14.8).
and
10.4.4 403 Forbidden
The server understood the request but is refusing to fulfil it. Authorization will not help and the request SHOULD NOT be repeated.
The first thing to keep in mind is that "Authentication" and "Authorization" in the context of this document refer specifically to the HTTP Authentication protocols from RFC 2617. They do not refer to any roll-your-own authentication protocols you may have created using login pages, etc. I will use "login" to refer to authentication and authorization by methods other than RFC2617
So the real difference is not what the problem is or even if there is a solution. The difference is what the server expects the client to do next.
401 indicates that the resource can not be provided, but the server is REQUESTING that the client log in through HTTP Authentication and has sent reply headers to initiate the process. Possibly there are authorizations that will permit access to the resource, possibly there are not, but let's give it a try and see what happens.
403 indicates that the resource can not be provided and there is, for the current user, no way to solve this through RFC2617 and no point in trying. This may be because it is known that no level of authentication is sufficient (for instance because of an IP blacklist), but it may be because the user is already authenticated and does not have authority. The RFC2617 model is one-user, one-credentials so the case where the user may have a second set of credentials that could be authorized may be ignored. It neither suggests nor implies that some sort of login page or other non-RFC2617 authentication protocol may or may not help - that is outside the RFC2616 standards and definition.
Edit: RFC2616 is obsolete, see RFC7231 and RFC7235.
+-----------------------
| RESOURCE EXISTS ? (if private it is often checked AFTER auth check)
+-----------------------
| |
NO | v YES
v +-----------------------
404 | IS LOGGED-IN ? (authenticated, aka user session)
or +-----------------------
401 | |
403 NO | | YES
3xx v v
401 +-----------------------
(404 no reveal) | CAN ACCESS RESOURCE ? (permission, authorized, ...)
or +-----------------------
redirect | |
to login NO | | YES
| |
v v
403 OK 200, redirect, ...
(or 404: no reveal)
(or 404: resource does not exist if private)
(or 3xx: redirection)
Checks are usually done in this order:
404 if resource is public and does not exist or 3xx redirection
OTHERWISE:
401 if not logged-in or session expired
403 if user does not have permission to access resource (file, json, ...)
404 if resource does not exist or not willing to reveal anything, or 3xx redirection
UNAUTHORIZED: Status code (401) indicating that the request requires authentication, usually this means user needs to be logged-in (session). User/agent unknown by the server. Can repeat with other credentials. NOTE: This is confusing as this should have been named 'unauthenticated' instead of 'unauthorized'. This can also happen after login if session expired.
Special case: Can be used instead of 404 to avoid revealing presence or non-presence of resource (credits #gingerCodeNinja)
FORBIDDEN: Status code (403) indicating the server understood the request but refused to fulfill it. User/agent known by the server but has insufficient credentials. Repeating request will not work, unless credentials changed, which is very unlikely in a short time span.
Special case: Can be used instead of 404 to avoid revealing presence or non-presence of resource (credits #gingerCodeNinja) in the case that revealing the presence of the resource exposes sensitive data or gives an attacker useful information.
NOT FOUND: Status code (404) indicating that the requested resource is not available. User/agent known but server will not reveal anything about the resource, does as if it does not exist. Repeating will not work. This is a special use of 404 (github does it for example).
As mentioned by #ChrisH there are a few options for redirection 3xx (301, 302, 303, 307 or not redirecting at all and using a 401):
Difference between HTTP redirect codes
How long do browsers cache HTTP 301s?
What is correct HTTP status code when redirecting to a login page?
What's the difference between a 302 and a 307 redirect?
According to RFC 2616 (HTTP/1.1) 403 is sent when:
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
In other words, if the client CAN get access to the resource by authenticating, 401 should be sent.
Assuming HTTP authentication (WWW-Authenticate and Authorization headers) is in use, if authenticating as another user would grant access to the requested resource, then 401 Unauthorized should be returned.
403 Forbidden is used when access to the resource is forbidden to everyone or restricted to a given network or allowed only over SSL, whatever as long as it is no related to HTTP authentication.
If HTTP authentication is not in use and the service has a cookie-based authentication scheme as is the norm nowadays, then a 403 or a 404 should be returned.
Regarding 401, this is from RFC 7235 (Hypertext Transfer Protocol (HTTP/1.1): Authentication):
3.1. 401 Unauthorized
The 401 (Unauthorized) status code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource. The origin server MUST send a WWW-Authenticate header field (Section 4.4) containing at least one challenge applicable to the target resource. If the request included authentication credentials, then the 401 response indicates that authorization has been refused for those credentials. The client MAY repeat the request with a new or replaced Authorization header field (Section 4.1). If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user agent SHOULD present the enclosed representation to the user, since it usually contains relevant diagnostic information.
The semantics of 403 (and 404) have changed over time. This is from 1999 (RFC 2616):
10.4.4 403 Forbidden
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.
In 2014 RFC 7231 (Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content) changed the meaning of 403:
6.5.3. 403 Forbidden
The 403 (Forbidden) status code indicates that the server understood the request but refuses to authorize it. A server that wishes to make public why the request has been forbidden can describe that reason in the response payload (if any).
If authentication credentials were provided in the request, the server considers them insufficient to grant access. The client SHOULD NOT automatically repeat the request with the same credentials. The client MAY repeat the request with new or different credentials. However, a request might be forbidden for reasons unrelated to the credentials.
An origin server that wishes to "hide" the current existence of a forbidden target resource MAY instead respond with a status code of 404 (Not Found).
Thus, a 403 (or a 404) might now mean about anything. Providing new credentials might help... or it might not.
I believe the reason why this has changed is RFC 2616 assumed HTTP authentication would be used when in practice today's Web apps build custom authentication schemes using for example forms and cookies.
401 Unauthorized: I don't know who you are. This an authentication error.
403 Forbidden: I know who you are, but you don't have permission to access this resource. This is an authorization error.
This is an older question, but one option that was never really brought up was to return a 404. From a security perspective, the highest voted answer suffers from a potential information leakage vulnerability. Say, for instance, that the secure web page in question is a system admin page, or perhaps more commonly, is a record in a system that the user doesn't have access to. Ideally you wouldn't want a malicious user to even know that there's a page / record there, let alone that they don't have access. When I'm building something like this, I'll try to record unauthenticate / unauthorized requests in an internal log, but return a 404.
OWASP has some more information about how an attacker could use this type of information as part of an attack.
This question was asked some time ago, but people's thinking moves on.
Section 6.5.3 in this draft (authored by Fielding and Reschke) gives status code 403 a slightly different meaning to the one documented in RFC 2616.
It reflects what happens in authentication & authorization schemes employed by a number of popular web-servers and frameworks.
I've emphasized the bit I think is most salient.
6.5.3. 403 Forbidden
The 403 (Forbidden) status code indicates that the server understood the request but refuses to authorize it. A server that wishes to make public why the request has been forbidden can describe that reason in the response payload (if any).
If authentication credentials were provided in the request, the server considers them insufficient to grant access. The client SHOULD NOT repeat the request with the same credentials. The client MAY repeat the request with new or different credentials. However, a request might be forbidden for reasons unrelated to the credentials.
An origin server that wishes to "hide" the current existence of a forbidden target resource MAY instead respond with a status code of 404 (Not Found).
Whatever convention you use, the important thing is to provide uniformity across your site / API.
These are the meanings:
401: User not (correctly) authenticated, the resource/page require authentication
403: User's role or permissions does not allow to access requested resource, for instance user is not an administrator and requested page is for administrators.
Note: Technically, 403 is a superset of 401, since is legal to give 403 for unauthenticated user too. Anyway is more meaningful to differentiate.
!!! DEPR: The answer reflects what used to be common practice, up until 2014 !!!
TL;DR
401: A refusal that has to do with authentication
403: A refusal that has NOTHING to do with authentication
Practical Examples
If apache requires authentication (via .htaccess), and you hit Cancel, it will respond with a 401 Authorization Required
If nginx finds a file, but has no access rights (user/group) to read/access it, it will respond with 403 Forbidden
RFC (2616 Section 10)
401 Unauthorized (10.4.2)
Meaning 1: Need to authenticate
The request requires user authentication. ...
Meaning 2: Authentication insufficient
... If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. ...
403 Forbidden (10.4.4)
Meaning: Unrelated to authentication
... Authorization will not help ...
More details:
The server understood the request, but is refusing to fulfill it.
It SHOULD describe the reason for the refusal in the entity
The status code 404 (Not Found) can be used instead
(If the server wants to keep this information from client)
they are not logged in or do not belong to the proper user group
You have stated two different cases; each case should have a different response:
If they are not logged in at all you should return 401 Unauthorized
If they are logged in but don't belong to the proper user group, you should return 403 Forbidden
Note on the RFC based on comments received to this answer:
If the user is not logged in they are un-authenticated, the HTTP equivalent of which is 401 and is misleadingly called Unauthorized in the RFC. As section 10.4.2 states for 401 Unauthorized:
"The request requires user authentication."
If you're unauthenticated, 401 is the correct response. However if you're unauthorized, in the semantically correct sense, 403 is the correct response.
I have created a simple note for you which will make it clear.
In English:
401
You are potentially allowed access but for some reason on this request you were
denied. Such as a bad password? Try again, with the correct request
you will get a success response instead.
403
You are not, ever, allowed. Your name is not on the list, you won't
ever get in, go away, don't send a re-try request, it will be refused,
always. Go away.
401: You need HTTP basic auth to see this.
If the user just needs to log in using you site's standard HTML login form, 401 would not be appropriate because it is specific to HTTP basic auth.
403: This resource exists but you are not authorized to see it, and HTTP basic auth won't help.
I don't recommend using 403 to deny access to things like /includes, because as far as the web is concerned, those resources don't exist at all and should therefore 404.
In other words, 403 means "this resource requires some form of auth other than HTTP basic auth (such as using the web site's standard HTML login form)".
https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2
401: Who are you again?? (programmer walks into a bar with no ID or invalid ID)
403: Oh great, you again. I've got my eye on you. Go on, get outta here. (programmer walks into a bar they are 86'd from)
I think it is important to consider that, to a browser, 401 initiates an authentication dialog for the user to enter new credentials, while 403 does not. Browsers think that, if a 401 is returned, then the user should re-authenticate. So 401 stands for invalid authentication while 403 stands for a lack of permission.
Here are some cases under that logic where an error would be returned from authentication or authorization, with important phrases bolded.
A resource requires authentication but no credentials were specified.
401: The client should specify credentials.
The specified credentials are in an invalid format.
400: That's neither 401 nor 403, as syntax errors should always return 400.
The specified credentials reference a user which does not exist.
401: The client should specify valid credentials.
The specified credentials are invalid but specify a valid user (or don't specify a user if a specified user is not required).
401: Again, the client should specify valid credentials.
The specified credentials have expired.
401: This is practically the same as having invalid credentials in general, so the client should specify valid credentials.
The specified credentials are completely valid but do not suffice the particular resource, though it is possible that credentials with more permission could.
403: Specifying valid credentials would not grant access to the resource, as the current credentials are already valid but only do not have permission.
The particular resource is inaccessible regardless of credentials.
403: This is regardless of credentials, so specifying valid credentials cannot help.
The specified credentials are completely valid but the particular client is blocked from using them.
403: If the client is blocked, specifying new credentials will not do anything.
401 response means one of the following:
An access token is missing.
An access token is either expired, revoked, malformed, or invalid.
403 response on the other hand means that the access token is indeed valid, but that the user does not have appropriate privileges to perform the requested action.
Given the latest RFC's on the matter (7231 and 7235) the use-case seems quite clear (italics added):
401 is for unauthenticated ("lacks valid authentication"); i.e. 'I don't know who you are, or I don't trust you are who you say you are.'
401 Unauthorized
The 401 (Unauthorized) status code indicates that the request has not
been applied because it lacks valid authentication credentials for
the target resource. The server generating a 401 response MUST send
a WWW-Authenticate header field (Section 4.1) containing at least one
challenge applicable to the target resource.
If the request included authentication credentials, then the 401
response indicates that authorization has been refused for those
credentials. The user agent MAY repeat the request with a new or
replaced Authorization header field (Section 4.2). If the 401
response contains the same challenge as the prior response, and the
user agent has already attempted authentication at least once, then
the user agent SHOULD present the enclosed representation to the
user, since it usually contains relevant diagnostic information.
403 is for unauthorized ("refuses to authorize"); i.e. 'I know who you are, but you don't have permission to access this resource.'
403 Forbidden
The 403 (Forbidden) status code indicates that the server understood
the request but refuses to authorize it. A server that wishes to
make public why the request has been forbidden can describe that
reason in the response payload (if any).
If authentication credentials were provided in the request, the
server considers them insufficient to grant access. The client
SHOULD NOT automatically repeat the request with the same
credentials. The client MAY repeat the request with new or different
credentials. However, a request might be forbidden for reasons
unrelated to the credentials.
An origin server that wishes to "hide" the current existence of a
forbidden target resource MAY instead respond with a status code of
404 (Not Found).
I have a slightly different take on it from the accepted answer.
It seems more semantic and logical to return a 403 when authentication fails and a 401 when authorisation fails.
Here is my reasoning for this:
When you are requesting to be authenticated, You are authorised to make that request. You need to otherwise no one would even be able to be authenticated in the first place.
If your authentication fails you are forbidden, that makes semantic sense.
On the other hand the forbidden can also apply for Authorisation, but
Say you are authenticated and you are not authorised to access a particular endpoint. It seems more semantic to return a 401 Unauthorised.
Spring Boot's security returns 403 for a failed authentication attempt
In the case of 401 vs 403, this has been answered many times. This is essentially a 'HTTP request environment' debate, not an 'application' debate.
There seems to be a question on the roll-your-own-login issue (application).
In this case, simply not being logged in is not sufficient to send a 401 or a 403, unless you use HTTP Auth vs a login page (not tied to setting HTTP Auth). It sounds like you may be looking for a "201 Created", with a roll-your-own-login screen present (instead of the requested resource) for the application-level access to a file. This says:
"I heard you, it's here, but try this instead (you are not allowed to see it)"

HTTP status for "email not verified"

I have seen the list of all HTTP status codes.
However to me it looks like there is no code for "email not verified" (used for authentication/authorization).
Did you ever had the same "problem"? What HTTP status code did you use?
I guess it should be a code starting with a 4 as it's a "client error".
The 4xx class of status code is intended for situations in which the client seems to have erred:
6.5. Client Error 4xx
The 4xx (Client Error) class of status code indicates that the client
seems to have erred. Except when responding to a HEAD request, the
server SHOULD send a representation containing an explanation of the
error situation, and whether it is a temporary or permanent
condition. These status codes are applicable to any request method.
User agents SHOULD display any included representation to the user.
For authentication and authorization, 401 and 403 are the proper status codes to be used, respectively. Regardless of the status code, you should always describe that reason of the error in the response payload.
401 Unauthorized
Use this status code for problems with HTTP authentication, that is, invalid credentials.
3.1. 401 Unauthorized
The 401 (Unauthorized) status code indicates that the request has not
been applied because it lacks valid authentication credentials for
the target resource. The server generating a 401 response MUST send
a WWW-Authenticate header field containing at least one
challenge applicable to the target resource.
If the request included authentication credentials, then the 401
response indicates that authorization has been refused for those
credentials. The user agent MAY repeat the request with a new or
replaced Authorization header field. If the 401
response contains the same challenge as the prior response, and the
user agent has already attempted authentication at least once, then
the user agent SHOULD present the enclosed representation to the
user, since it usually contains relevant diagnostic information.
403 Forbidden
Use this status code for problems with authorization, that is, the credentials are valid but they are insufficient to grant access.
6.5.3. 403 Forbidden
The 403 (Forbidden) status code indicates that the server understood
the request but refuses to authorize it. A server that wishes to
make public why the request has been forbidden can describe that
reason in the response payload (if any).
If authentication credentials were provided in the request, the
server considers them insufficient to grant access. The client
SHOULD NOT automatically repeat the request with the same
credentials. The client MAY repeat the request with new or different
credentials. However, a request might be forbidden for reasons
unrelated to the credentials. [...]
While CodeCaster has provided a very definitive answer as a comment, that which is correct is sometimes not appropriate.
Firstly, you'll see there is no mention of email addresses in the specs. Similarly there is no mention of shoe sizes, model railway gauges, breeds of dogs nor many other things. It is not relevant to HTTP. This is just a data item.
You seem to have some state associated with this data item which you use for authentication purposes - but don't provide any explanation of that state nor how it is applied. I assume that you mean that the "not verified" state means that the only association between the data item and the user interacting with your site is an assertion of the user. And further that you do not allow the user to authenticate with this as a token.
It may seem I'm being pedantic here - but there are other, valid interpretations of "email not verified". You should have provided more information in your question.
There's another gap in your story: which request are we taking about here? Again, I'll take the liberty of assuming that the request is an attempt to authenticate.
In this case, there is nothing intrinsically wrong with the request. There is nothing intrinsically wrong with the client. There is nothing intrinsically wrong at the server. Not permitting the user to authenticate is a policy decision based on the data.
Another critical bit of information missing from your question is what is actually making the request. If its a form sent by a browser, then returning anything other than a 200 OK (or 204, or a redirect to a 200) to MSIE will, by default, cause the browser to display an internal message and not the content you send.
OTOH if the client is an application running on the users device, or an Ajax request, then you control the API and can define your own semantics. If you want to return a 692 status code to represent this condition, then you can return a 692 error code. You can even inject your own headers in the response (by convention these should begin with 'X-').
In the defined state the authentication fails. But returning a 401 response will prompt a browser to attempt HTTP authentication - which doesn't address the issue.
IMHO, the nearest existing code is 403 or 422. But based on the information you've supplied I can't say if thats what you should be using.

HTTP status 401, but expected data in HTTP response

I'm receiving a 401 status code on a response from a webserver. However, if I trap the 401 exception and process the data, the response actually contains the same information as an authorized request - I have a different client that received status 200 with data to confirm this. Fiddler also confirms the data.
I believe this is incorrect behavior by the server; however, I cannot find the spec to confirm as such. I've read through RFC 2616 and 2617, and the most relevant snippet seems to be:
If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user SHOULD be presented the entity that was given in the response, since that entity might include relevant diagnostic information.
However, by the generic definition of entity, the entity could theoretically be the data that would be sent to an authorized request.
I would be more comfortable with a 401 and no data/auth-denial data, or a 200 with authorized data. But I'm afraid this may be a defect and cause me issues down the road. I can't tell if I'm being properly 401'ed or not.
Is there any spec that states that authorized data should not be returned to a client request which generates a 401 response? Or is this scenario valid?
I don't think
the entity could theoretically be the data that would be sent to an authorized request.
From RFC 2616:
If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user SHOULD be presented the entity that was given in the response, since that entity might include relevant diagnostic information.
So it's legal to present entity to a unauthenticated user.
As you said, authorized data should not be returned to a client... but in your case, the entities are just both the same for authenticated and unauthenticated users.
On the other hand, Google's login page uses 200 as the response code for failing to authenticate.
Note that:
the response MUST include a WWW-Authenticate header field(section 14.47) containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field (section 14.8).
The reason may be that most of the browsers will open a popup window for uses to fill in username and password when they receive a WWW-Authenticate header field. But that's based on plain text which may cause a security problem.

RESTful Login Failure: Return 401 or Custom Response

This is a conceptual question.
I have a client (mobile) application which needs to support a login action against a RESTful web service. Because the web service is RESTful, this amounts to the client accepting a username/password from the user, verifying that username/password with the service, and then just remembering to send that username/password with all subsequent requests.
All other responses in this web service are provided in a JSON format.
The question is, when I query the web service simply to find out whether a given username/password are valid, should the web service always respond with JSON data telling me its successful or unsuccessful, or should it return HTTP 200 on good credentials and HTTP 401 on bad credentials.
The reason I ask is that some other RESTful services use 401 for bad credentials even when you're just asking if the credentials are valid. However, my understanding of 401 responses are that they represent a resource that you are not supposed to have access to without valid credentials. But the login resource SHOULD be accessible to anyone because the entire purpose of the login resource is to tell you if your credentials are valid.
Put another way, it seems to me that a request like:
myservice.com/this/is/a/user/action
should return 401 if bad credentials are provided. But a request like:
myservice.com/are/these/credentials/valid
should never return 401 because that particular URL (request) is authorized with or without valid credentials.
I'd like to hear some justified opinions one way or the other on this. What is the standard way of handling this, and is the standard way of handling this logically appropriate?
First off. 401 is the proper response code to send when a failed login has happened.
401 Unauthorized
Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. The response must include a WWW-Authenticate header field containing a challenge applicable to the requested resource.
Your confusion about, myservice.com/are/these/credentials/valid sending back 401 when you just do a check, I think is based on the fact that doing boolean requests in REST often is wrong by the RESTful constraints. Every request should return a resource. Doing boolean questions in a RESTful service is a slippery sloop down to RPC.
Now I don't know how the services that you looked on are behaving. But a good way of solving this is to have something like an Account object, that you try to GET. If your credentials are correct, you will get the Account object, if you don't want to waste bandwidth just to do a "check" you can do a HEAD on the same resource.
An Account Object is also a nice place to store all those pesky boolean values that otherwise would be tricky to create individual resources for.
401 should be sent only when the request needs authorization header field and authorization fails. Since the Login API doesn't require authorization, hence 401 is the wrong error code in my opinion
As per the standard here https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
*10.4.2 401 Unauthorized
The request requires user authentication. The response MUST include a WWW-Authenticate header field (section 14.47) containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field (section 14.8). If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user SHOULD be presented the entity that was given in the response, since that entity might include relevant diagnostic information. HTTP access authentication is explained in "HTTP Authentication: Basic and Digest Access Authentication" [43].*
If the 401 response code is misleading for user authentication, the API can send HTTP status code 200 OK for both successful and failed authentication, but set a custom header on the authentication successful response and omit that header on failed logins.
The client can check if the header exists or not and decide the action.
Example: SpringBoot API Response
The call to OK when login is successful sets the header "gotyouin" with a value (anything). Call to failed does not add the header and client can treat this as a failed login attempt.
public class LoginResponseEntityHelper {
public static ResponseEntity<?> ok(String token) {
return ResponseEntity.status(HttpStatus.OK).header("gotyouin", token).body(null);
}
public static ResponseEntity<?> failed() {
return ResponseEntity.status(HttpStatus.OK).body(null);
}}
It is logical to use 401 http status code when access to a resource is denied because the request lacks or has incorrect credentials. And when correct credentials are provided, the request should complete successfully granting access to the protected resource.
Applicable for your case: myservice.com/this/is/a/user/action.
Maybe we should be clear about this credentials
In most secure applications, credentials are needed to access protected resource(s). These credentials can be sent along every request via HTTP header. Specifically the Authorization Header.
Therefore the Authorization header contains credentials to allow a user to access protected resource(s).
Remember that a user who was verified successfully as an authorized user(successful login), is the one given this priviledged credentials to allow for access on protected resources on the server.
From the server point of view, a HTTP request targeting a protected resource yet it is lacking credentials or containing invalid credentials may cause to validly send back 401 response code.
Therefore the Login API should not send a 401 response code because of a failed login attempt. It is misleading. Reasoning out that you are requesting to the server application via the Login API to grant you with credentials required to access resources that are protected. By itself, the Login API is not a protected resource.
So what is the correct response status?
Well I will opine towards sending back a 400 response code because a failed login attempt is a client error who is at fault for not providing the correct username or password.
According to RFC standards about 400 code:
The 400 (Bad Request) status code indicates that the server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing)
Again I will reiterate that failed login is a client error due to providing details that are incorrect. Sending back a 400 doesn't have to mean that the request syntax is malformed. But a malformed syntax is one of the reasons.
Sending a 401 would really be misleading as the user doesn't need to provide authentication information to access the login API.

HTTP 401 Unauthorized or 403 Forbidden for a "disabled" user?

An authentication service allows user accounts be disabled (a sort of soft-delete).
If the server then receives an authentication request for a disabled user that would otherwise be valid, should the server return 401 or 403? With either status code, I would return a message indicating that the account had been disabled.
For quick reference, relevant quotes from HTTP/1.1 spec (emphasis mine):
401 Unauthorized
The request requires user authentication. The response MUST include a
WWW-Authenticate header field (section 14.47) containing a challenge
applicable to the requested resource. The client MAY repeat the
request with a suitable Authorization header field (section 14.8). If
the request already included Authorization credentials, then the 401
response indicates that authorization has been refused for those
credentials. If the 401 response contains the same challenge as the
prior response, and the user agent has already attempted
authentication at least once, then the user SHOULD be presented the
entity that was given in the response, since that entity might
include relevant diagnostic information. HTTP access authentication
is explained in "HTTP Authentication: Basic and Digest Access
Authentication" [43].
403 Forbidden
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.
Based on an email written by Roy T. Fielding, there's apparently a bug in the current HTTP spec.
The way the spec is intended to be read is as follows (using quotes from above email):
401 "Unauthenticated":
you can't do this because you haven't authenticated
403 "Unauthorized":
user agent sent valid credentials but doesn't have access
So, in the case of a disabled user, 403 is the correct response (and 404 is also an option).
I've got two different answers for what to return in this case.
Semantic choice - 401 Unauthorized. In this case, your client has provided credentials, and the request has been refused based on the specific credentials. If the client were to try again with a different set of credentials, or if the account were to be re-enabled in the future, the same request might succeed.
Security choice - 404 Not Found. Many services will simply return a 404 for any failure, in order to avoid information leakage. Github comes to my mind immediately.
From General API Information, in github's developer docs:
Unauthenticated requests will return 404 to prevent any sort of
private information leakage.
For something I was deploying as a public service, I'd probably go with using 404 to avoid giving an attacker clues about their credential attempts. If it was for internal-only consumption, or in testing, I'd probably return 401.
technically both are correct, it really comes down to how much you want to reveal.
returning a 401 says to the caller that the account isn't valid, which is correct, but if your api is then going to be called again to register a user with the same credentials that call would also fail. which might not be much use to the caller.
so, it really depends on how your api will be used and who/what the target audience is.

Resources