38

I am implementing the authentication for an app, and I am using a pluggable system with "authentication methods". This allows me to implement both HTTP Basic as well as HTML-based authentication.

With HTTP Basic/Digest auth the server sends a 401 Unauthorized response header. However, according to the HTTP/1.1 RFC:

The response MUST include a WWW-Authenticate header field (section 14.47) containing a challenge applicable to the requested resource.

Since I do not know of any "html" WWW-Authenticate header, sending a 401 with an HTML login form seems inappropriate. Is there any alternative to this? I want to design my app in a RESTful way.

What is the correct HTTP Status code (and headers) for an HTML-based login form? And what is the correct code when the login fails?

Note: I am not interested in Digest Authentication.

igorw
  • 27,759
  • 5
  • 78
  • 90

4 Answers4

19

What about this one ?

When requesting the login form which is a public page, you get what you want, so it's a 200 status code :

GET /login -> 200

When requesting for a page that needs a http level authentication that you didn't initiated (basic http, ssl certificate, etc.), the application must tell the browser itself that it needs to initiate this authentication for you :

GET /secured -> 401 with WWW-Authenticate header

When the authentication is a cookie-based session, you already have a cookie (if it's not the case, you will get one with the set-cookie header when requesting the page) but this cookie doesn't tell that you are allowed to access the /secured uri. So if you try to access this uri, you should get a "403 forbidden" status. Then the "log in" action is no more than just changing the state of the application with a POST request to make the application grant access for this cookie, so...

Log in with bad credentials:

GET /secured -> 403 with HTML login form (with action="/login")
POST /login -> 403 with HTML login form, displaying errors

Log in with good credentials but not enough permissions :

GET /secured -> 403 with HTML login form (with action="/login")
POST /login -> 403 with HTML page saying "I know you are John, but you can't get this page"

Log in with good credentials and enough permissions :

GET /secured -> 403 with HTML login form (with action="/login")
POST /login -> 302 (temporary redirection to /secured)
GET /secured -> 200
17

For HTML I think you should respond with a 400.

This may be true for non-HTML requests as well, since 401 is as far as I understand it more designed to respond to a request to content that requires authentication, not to respond to an authentication request.

HTML does not always allow for pure use of RESTful APIs, so it's ok to cut corners here and there imo, but maybe there is a better way I'm not seeing in this particular case.

Seldaek
  • 40,986
  • 9
  • 97
  • 77
  • 1
    Responding with the login form directly (as opposed to redirecting) is certainly an option, and may actually make more sense. – igorw May 24 '11 at 13:14
  • That may be the most practical solution as HTTP tends to mix authentication and authorisation. – Hunter Morris May 24 '11 at 15:31
  • 1
    401 is issued for failed authentication AND authorization attempt. It can as well be used for failed login. 403 on other hand is used for complete restriction of data - no authentication/authorization is necessary and performed. – Gelmir Mar 26 '13 at 07:44
  • 1
    I don't agree with 400 because that's for a "bad request" and clearly means malformed request. I think the key question is what the browser is supposed to do. Especially with regards to how the login form is being delivered, as redirect or as injected. Redirect is what I do with jetty now, redirect to the /login form and then the the login POST goes to /j_security_check, from there it will re-direct back to the originally rejected page (albeit without whatever the request parameters originally were.) – Gunther Schadow May 18 '20 at 19:29
8

This is a tricky question, largely because the most well-established HTTP clients used by people are browsers. According to the RFC, the WWW-Authenticate header can contain anything. Basic and digest authentication are just two examples of further standardised challenge/response mechanisms. You can simply specify a challenge like html-form id=foo and return the 401 along with an HTML form. Also, recall from the spec that multiple challenges can be specified within the same WWW-Authenticate header, but I don't have any experience testing browsers extensively with different schemes.

Hunter Morris
  • 417
  • 3
  • 8
-5

@2016-02-17 Updated

The login form http status should be 200 OK.

The error http status better use 401 Unauthorized. (The name may be confused, 401 is about authentication. RFC7235

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

If you want to handle if no permission right, you may need 403 Forbidden [RFC7231]

HTTP 422 is used for WebDAV, but the meaning might fit the needs. (Not suggested for most cases)

For more information, please see the comment of Cássio Mazzochi Molin below.


@2016-02-12 **Updated** *(This is the reference to the accepted answer.)*

The login form http status should be 200.

The error http status better use 400.

HTTP 422 is used for WebDAV, but the meaning might fit the needs. HTTP 401 is for authorization. And is not suitable for authentication.


@2016-02-12 Original

HTTP 422 is now better choice other than 400 / 401. HTTP 422 is an alternative choice.

Because it means the server understand the data but is not correct for part of the data. i.e. It can show client that username / password incorrect.

11.2. 422 Unprocessable Entity

The 422 (Unprocessable Entity) status code means the server understands the content type of the request entity (hence a 415(Unsupported Media Type) status code is inappropriate), and the syntax of the request entity is correct (thus a 400 (Bad Request) status code is inappropriate) but was unable to process the contained instructions. For example, this error condition may occur if an XML request body contains well-formed (i.e., syntactically correct), but semantically erroneous, XML instructions.

RFC4918

Community
  • 1
  • 1
goodseller
  • 19
  • 4
  • 1
    Please see these 2 links before downvoting and will possible change you mind: http://stackoverflow.com/questions/16133923/400-vs-422-response-to-post-of-data http://www.bennadel.com/blog/2434-http-status-codes-for-invalid-data-400-vs-422.htm – goodseller Feb 12 '16 at 08:48
  • Please see the [RFC 7231](https://tools.ietf.org/html/rfc7231), the [RFC 7232](https://tools.ietf.org/html/rfc7232), the [RFC 7233](https://tools.ietf.org/html/rfc7233), the [RFC 7234](https://tools.ietf.org/html/rfc7234) and the [RFC 7235](https://tools.ietf.org/html/rfc7235). They are the references for HTTP 1.1. The `422` status code is not even mentioned there. – cassiomolin Feb 12 '16 at 08:59
  • IC your point, you are considering it syntactically. However, it is really having better meaning for 422 as an authentication validation purposes. Doesn't it? – goodseller Feb 12 '16 at 11:07
  • HTTP authentication is presumed to be **stateless**: all of the information necessary to authenticate a request must be provided in the request, rather than be dependent on the server remembering prior requests. Have a look at what the [RFC 7235](https://tools.ietf.org/html/rfc7235#section-3.1) says about the `401` (Unauthorized): _[...] If the request included authentication credentials, then **the 401 response indicates that authorization has been refused for those credentials**. [...]_ So, `401` seems to be the correct choice. – cassiomolin Feb 12 '16 at 11:49
  • 401 is about *authorization*, not *authentication*. I am implementing an authentication API and found different approach (include this): 400 vs 401 vs 422. You really make a point that seeing why Seldaek say 400. – goodseller Feb 12 '16 at 15:17
  • `401` is about *authentication* and `403` is about *authorization*. For more details, check what the [RFC 7231](https://tools.ietf.org/html/rfc7231#section-6.5.3) says about the `403` (Forbidden): _[...] indicates that the server understood the request but refuses to authorize it. [...] **If authentication credentials were provided in the request, the server considers them insufficient to grant access** [...]_ – cassiomolin Feb 12 '16 at 16:02
  • The standard HTTP `Authorization` header has a bad name and usually causes a lot of confusion. It's used to carry *authetication* instead of *authorization* data. Have a look at what the [RFC 7235](https://tools.ietf.org/html/rfc7235#section-4.2) says about it: _The `Authorization` header field allows a user agent to **authenticate** itself with an origin server [...] Its value **consists of credentials containing the authentication information** of the user agent [...] **Authorization = credentials** [...]_ – cassiomolin Feb 12 '16 at 16:14
  • 1
    Oh. That drive me crazy! The name is so confused. Really appreciate how deep you are understanding about it and kindly sharing it with clear references. ;) – goodseller Feb 17 '16 at 05:38