When a client requests a resource from an HTTP server and it’s not allowed to access that resource, the client needs to know enough about why in order to present the right message or options to the user. Basically, we need to know whether the user can do something about it or not.
HTTP status codes help us differentiate these scenarios and when the reason has to with authentication (verifying who the client is) or authorization (what that client is allowed to access), the server should use the 401 and 403, respectively.
There are a couple things that complicate the use of 401 and 403:
- The terminology used around the 401 status code in the HTTP spec (RFC 2616), namely “unauthorized” is often misused in place of “unauthenticated,” and
- HTTP doesn’t provide a status code for authenticated users who aren’t allowed to use a resource, so we use 403.
Let’s start by understanding the scenarios that we need to be able to differentiate. There are six outcomes of a request when viewed from an authentication or authorization perspective:
|#||Authentication||Authorized||Resource delivered||HTTP Status Code||Resolution|
|4||yes||no||n/a||no||401||Provide Valid Authentication|
- The unauthenticated client is authorized to access the resource (HTTP 200-class).
- The unauthenticated client is perhaps authorized to access the resource if authenticated (HTTP 401).
- The unauthenticated client is not allowed access the resource; authentication will not help (HTTP 403).
- The client’s authentication credentials are incorrect, invalid, expired, or revoked (HTTP 401).
- The client is authenticated but cannot access the resource (use HTTP 403 Forbidden).
- The client is authenticated and may access the resource (HTTP 200-class).
Just the 4xx scenarios and how to handle them
|Authentication credentials provided|
|401||2) Access restricted to authenticated clients.
User Experience: User should log in order to access this resource.
|4) Authentication bad/failed.
User experience: If”logged in”: User should be “logged out”. If trying to “log in”: credentials are not valid
|403||3) Client not allowed.
User Experience: Tell user that this information not available.
|5) Authentication good, but access not allowed.
User experience: Tell user that this information is private. There is essentially no recourse (other than requesting access somehow).
But isn’t “401 Unauthorized” about authorization, not authentication?
Back when the HTTP spec (RFC 2616) was written, the two words may not have been as widely understood to be distinct. It’s clear from the description and other supporting texts that 401 is about authentication.
RFC 2616 Hypertext Transfer Protocol: 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.
HTTP access authentication is explained in “HTTP Authentication: Basic and Digest Access Authentication” [RFC 2617].”
Notice the use of “authentication” and “authorization” interchangeably, but that the way for the client to overcome a 401 is to provide credentials, which is authentication. All three references are about authentication:
- Section 14.47 WWW-Authenticate
The WWW-Authenticate response-header field MUST be included in 401 (Unauthorized) response messages.
This is so that the client know what authentication methods it may use if it wishes to try again with authentication
- Section 14.8 Authorization:
“A user agent that wishes to authenticate itself with a server—usually, but not necessarily, after receiving a 401 response—does so by including an Authorization request-header field with the request. The Authorization field value consists of credentials containing the authentication information of the user agent for the realm of the resource being requested.”
- RFC 2617 “HTTP Authentication”, the specification for a Basic Access Authentication scheme.
RFC 5849 The OAuth 1.0 Protocol: 3.2 Verifying Requests
The OAuth spec is much more clear:
“The server SHOULD return a 401 (Unauthorized) status code when receiving a request with invalid client credentials, an invalid or expired token, an invalid signature, or an invalid or used nonce.”
401 for Unauthorized, also?
The spec for 401 doesn’t explicitly properly define 401 to also possibly mean that a resource is not allowed to be accessed by an authenticated user. However, if we were to allow 401 to mean either — and OAuth doesn’t allow for this — then the client can’t know how to resolve the solution; should it ask the user to authenticate again or should it tell the user that they’re not allowed to access the resource? We need another status for code Unauthorized.
“403 Forbidden” for Unauthorized
In section 10.4.4 403 Forbidden of RFC 2616, it says:
The server understood the request, but is refusing to fulfill it. Authorization will not help and the request SHOULD NOT be repeated.
So, 403 is for “the unauthenticated client is not authorized to use the resource.” There’s no status code defined for “the client is authenticated but is not authorized to use the resource.” One could define their own, but since 403 is so close in mearning and there’s no loss of versatility or amibugity introduced by also using it for the authenticated case, it’s the best option.