190

A similar question is posted here: What's an appropriate HTTP status code to return by a REST API service for a validation failure?

The answer in the thread above states that "For instance if the URI is supposed to have an ISO-8601 date and you find that it's in the wrong format or refers to February 31st, then you would return an HTTP 400. Ditto if you expect well-formed XML in an entity body and it fails to parse."

However, what happens if the user submitted correctly formatted data? By this I mean, the user submitted a plain alphabetical string / text for the username and password (which is perfectly valid for my application). The only issue is that the password did not match with the username. In this case, 400 will be incorrect because it is perfectly valid syntax and well-formed.

A 401 would be incorrect (as suggested here: Which HTTP status code to say username or password were incorrect?) because the user is not trying to access any page, he is simply trying to login and entered data which does not match.

If you look back at the first post I linked to, the second answer states that 422 is the correct response (and it looks correct to me), however, I am using Django Rest Framework and 422 is not part of the status codes (a list of the status codes which are part of DRF can be found here: http://www.django-rest-framework.org/api-guide/status-codes/#client-error-4xx)

404 also doesn't look right because the data is successfully accepted and not refused.

With that said, what is the real correct response which should be used?

Community
  • 1
  • 1
SilentDev
  • 20,997
  • 28
  • 111
  • 214
  • [Wikipedia](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) says: " **401 Unauthorized**: Similar to 403 Forbidden, but specifically for use when authentication is required and ***has failed*** or has not yet been provided." Seems logical, though, it is marked as *citation needed*. – Movahhedi Feb 13 '23 at 07:37

5 Answers5

219

If you are strictly using the HTTP authentication framework provided by RFC 7235 for your REST API, the correct HTTP code would actually be 401. From the RFC:

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).

Your REST API should employ an authentication scheme of some sort in order to return a valid 401 response to your client.

Another pertinent section from RFC 7235, page 4:

Upon receipt of a request for a protected resource that omits
credentials, contains invalid credentials (e.g., a bad password) or
partial credentials (e.g., when the authentication scheme requires
more than one round trip), an origin server SHOULD send a 401
(Unauthorized) response that contains a WWW-Authenticate header field with at least one (possibly new) challenge applicable to the
requested resource.

A higher-level response, such as a rendered login page for a visual user (redirected from a protected resource via 302), would be better served with the 200 status code (per @KernelDeimos' answer, for example). Since login pages are typically their own resource (e.g. /login?redirect=original-resource), the unauthenticated user is still authorized to see this page, even if they provide an incorrect username/password. Then, you redirect the authenticated user back to the resource, at which point would show 200 if allowed, or 403 if the user is forbidden to view the resource.

The area where 401 could come into play with a visual login page is a front-end library that leverages the REST API using XHR requests, then relay the 401 response from the REST API back into a meaningful format on the login page.

Community
  • 1
  • 1
sjagr
  • 15,983
  • 5
  • 40
  • 67
  • 2
    oh you're right, 401 is what I should be using. Wow, can't believe I missed the second part of the explanation. Thanks. I'll mark this question as correct in 8 minutes when it allows me to. – SilentDev Sep 24 '15 at 03:03
  • 1
    From the very part you are quoting, a 401 response indicates that the request has **not been applied**. Therefore, no login **attempt** was even made, because authorization is needed at a lower level first. This can usually happen when several layers of security are nested, and is a valid response to indicate the requirement to auth with an outer layer before attempting to auth with an inner layer. – spectras May 11 '17 at 08:24
  • @spectras "the request has not been applied because it **lacks valid authentication credentials for the target resource**" - however I'm pointing to the second part where the request did include authentication credentials and were refused, a separate case and _section_ altogether. If you want to split hairs over this though, perhaps suggest a better status code instead. – sjagr May 11 '17 at 15:16
  • 4
    @sjagr> actually, I commented because I was linked your answer on my take on the same question, here: http://stackoverflow.com/a/32897804/3212865 . The main point being the question mixes up different layers of communication and the most orthogonal response would actually be 200, meaning _“the request was processed successfully and a login attempt was made accordingly, find the result in the response's content”_. Attempting to express an application-level error in a transport-level status code is a design mistake. – spectras May 11 '17 at 19:21
  • 21
    I think this answer is wrong. The requirement for an accompanying `WWW-Authenticate` implies 401 is for prompting for authentication via HTTP Basic or similar. For failed logins (in a typical cookie-based login scenario), you should use 403, as explained by this answer: https://webmasters.stackexchange.com/questions/24443/should-i-return-a-http-401-status-code-on-an-html-based-login-form – callum Sep 27 '18 at 14:24
  • 2
    This seems to be subject to some interpretation depending on how literally some statements are taken. My understanding is that the request lacks *valid* authentication credentials, not that it _lacks credentials at all_. Since the second portion still mentions that the request could have included credentials in the first place, I'm of the opinion that the 401 status is correct. – sjagr Aug 26 '19 at 13:48
  • 2
    @sjagr I think it lacks credentials at all, as credentials wrt a request exist in the headers. Sending 401 implies you don't have permission to attempt login as @spectras suggested. A separation of concerns exists between data transfer and application logic, so sending anything other than a `2xx` status for a successfully produced failed login response is unnecessary, overcomplicated, and inconsistent with other things your web server might do. – KernelDeimos May 02 '21 at 18:17
  • 1
    @KernelDeimos thanks for your comments. Since this answer seems to be causing some confusion, I double-checked RFC 7235 to see if there were any new versions that updates it, and instead found another section that further clarifies the usage of 401. Hope this helps. – sjagr Jul 21 '21 at 18:44
  • @sjagr this seems to still be in the context of the HTTP `Authentication` header rather than a post request with login credentials. Is there anything in the RFC that seems to dispute this? (you highlighted "contains invalid credentials (e.g., a bad password)", but I suspect the context is at the HTTP protocol rather than application logic) – KernelDeimos Jul 23 '21 at 21:29
  • @KernelDeimos Sure: "HTTP does not restrict applications to this simple challenge-response framework for access authentication. Additional mechanisms can be used, such as authentication at the transport level or via message encapsulation, and with additional header fields specifying authentication information. However, such additional mechanisms are not defined by this specification." – sjagr Jul 24 '21 at 04:20
  • 1
    401 can only be used when the failure is related to HTTP authentication, as defined in RFC 7235. "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." – Julian Reschke Jul 24 '21 at 11:13
  • @sjagr I believe the paragraph you quoted describes how the HTTP access authentication can be extended; notice that it says "additional header fields", implying that additional mechanisms still need to be compatible with this RFC (and thereby implying that the `Authentication` header is present in the request). – KernelDeimos Jul 24 '21 at 20:23
  • Thanks @JulianReschke - I agree that the server must include the `WWW-Authenticate` field, however the Authentication header is not required. This is slightly alluded here in page 10 of the RFC: "Therefore, new authentication schemes that choose not to carry credentials in the Authorization header field (e.g., using a newly defined header field) will need to explicitly disallow caching" – sjagr Jul 25 '21 at 16:21
  • Regarding the spirit of this question and answer, the context it was answered in was in the OP's usage of a REST API, which very well could contain the user's credentials wrapped by whatever front-end mechanism and [authentication scheme](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes) the designer wishes to choose, or even create. I'll clarify the scope of the answer to note this. – sjagr Jul 25 '21 at 16:29
  • @sjagr The paragraph you quoted from describes the importance of ensuring credentials don't get cached if they're sent with a different HTTP header. I don't think this means sending credentials in a POST body is a valid HTTP authentication scheme as described by this RFC. The first token in `WWW-Authenticate` identifies an authentication scheme from this list: https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml - so it's difficult to imagine what kind of `WWW-Authenticate` header you'd send back for login credentials sent in the body of a POST. – KernelDeimos Jul 25 '21 at 18:40
  • @KernelDeimos The authentication scheme does not need to be on that list - the RFC allows the designer to create and register their own. Many often don't register (see AWS, etc.) Regarding `Authorization` header from the client, it falls under the "SHOULD" category whereas `WWW-Authenticate` from the server falls under the "MUST". My quote was to further emphasize the optionality of the `Authorization` header. The RFC also does not specify any request methods, and the OP did not mention the use of POST. – sjagr Jul 26 '21 at 04:02
  • I think this is answer is wrong. It should be 200. Checkout https://stackoverflow.com/a/74077300/2311074 – Adam Feb 17 '23 at 08:55
15

If login is handled at a higher-level (ex: sending a POST to the server with a username and password), use the appropriate status code in 2xx for a successfully handled login request with the wrong password. If using the HTTP authentication framework provided by RFC 7235, send 401 (see answer by @sjagr for further detail).

Below the line is the rest of my original answer, which explains my train of thought. Also note the thread on sjagr's answer which includes a debate which improved both our answers and a comment from Julian Reschke (one of the RFC's authors).


Before asking "what is the correct HTTP status code", it's important to consider this question: "Should success or failure of login be reflected in the HTTP status code of the response?"

In @sjagr's answer the first part of this section is highlighted. I'm going to highlight the second part and explain why:

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).

This refers to an Authorization header, rather than a request body containing login credentials. The phrasing of the first part, unfortunately, could be misinterpreted to refer to a request body containing login information. This ambiguity can be resolved by considering separation of concerns; (https://en.wikipedia.org/wiki/Separation_of_concerns) the server's response header should not depend on the differences of two valid request bodies, with the exception of when it causes an internal server error, otherwise the concerns of data transfer and appliction login begin to creep into each other.

I would use HTTP response 2xx for a valid login request, where the client has permission to attempt a login, that is handled successfully with a response indicating success or failure.

I also like the way @spectras expressed this in the comments:

Attempting to express an application-level error in a transport-level status code is a design mistake.

KernelDeimos
  • 503
  • 4
  • 12
12

401 - Unauthorized

403 - Forbidden

http://www.buggybread.com/2012/11/http-error-codes-401-access-denied-403.html

Kelderic
  • 6,502
  • 8
  • 46
  • 85
Tony Arnold
  • 649
  • 2
  • 7
  • 15
9

The correct HTTP response status code is 200.

I think what causes all the confusion is that there are two entities that need to be authenticated. Firstly, the client (front-end application) must verify its authorization to initiate a login request on behalf of the user. Secondly, the user must authenticate themselves by providing their username and password.

Regarding HTTP response status codes, it's important to note that they relate only to the client making the request and not the user. The appropriate response code for a successful request is 200, regardless of the authentication status of the user's credentials. The response body should contain information on whether the password matches or not, but this has no impact on the status code since the request was authorized and parsed successfully.

A response code of 401 would indicate that the front-end token is invalid, not the user's password. Similarly, a response code of 403 would indicate that the token is valid, but it doesn't have the necessary access rights to request authentication information for the user's credentials.

See also https://developer.mozilla.org/en-US/docs/Web/HTTP/Status

Adam
  • 25,960
  • 22
  • 158
  • 247
4

If you try to log into a Google account with the wrong password, it will return a 200 response containing data that indicates the password was incorrect. For that reason, I just use a 200.

At the end of the day, which status code you use is purely a semantic issue and isn't going to change the functionality of your application. What really matters is that your application displays the correct information to the user.

ICW
  • 4,875
  • 5
  • 27
  • 33
  • 6
    Technically the correct answer, but just "Google does it that way" isn't really a good reason to do something – Jake Feb 02 '22 at 18:02
  • 1
    @Jake It's a good enough reason considering there's no advantage to doing it one way or another. This is one of those things where you just need to make sure you understand how the server is going to respond and react accordingly. Whether or not you do that by checking the status code has makes absolutely no difference to how the application functions nor does it make the application more/less maintainable. So, really, just do what you want with this one. The fact that Google does it this way just gives reassurance that it's a viable solution. Sometimes it's okay to say "it doesn't matter" – ICW Feb 07 '22 at 16:09