HttpForbidden for authenticated, unauthorized requests.

Topics: ASP.NET MVC, ASP.NET Web API
May 23, 2012 at 4:44 PM

In both Web Api and MVC, the AuthorizeAttribute filter returns HttpStatusCode.Unauthorized when the request's current principal is unauthenticated (anonymous) and when the request is authenticated but unauthorized.

On the web, this has the undesired effect of redirecting authenticated users to an authentication page. There's little the user can do at that point - re-authenticating and attempting to access the resource again will simply redirect the user back to the authentication page.

Consider how most operating systems work. If I try to access a network file resource anonymously, I'm presented with a dialog to enter my user credentials. If I try to access a network file resource I do not have permission to access while authenticated (ie/ I'm unauthorized) then I'm presented with an "access denied" message - not another login screen.

I believe the HTTP 1.1 specification confuses "authentication" with "authorization". Under 10.4.2 401 Unauthorized it states "the request requires user authentication".

The statement is nonsense - Unauthorized does *not* mean the request requires authentication - authorization and authentication are two completely separate things. If we go by the description of 401, it's clearly talking about unauthenticated users. It also describes specific behavior - that any 401 response should result in the user being prompted for credentials.

I argue that 403 Forbidden should be returned from AuthorizeAttribute when the current thread pricipal is *authenticated* but *unauthorized* to access the resource. The experience is what people intuitively would expect - "if we know who you are and you're not allowed to access the resource, you are (403) Forbidden from accessing it."

What do you think? Should the behavior of AuthorizeAttribute be changed?

See http://stackoverflow.com/questions/238437/why-does-authorizeattribute-redirect-to-the-login-page-for-authentication-and-au/5844884#5844884 for some additional discussion on the issue.

May 24, 2012 at 9:12 AM

I do agree. I had to write my own attribute to return Forbidden.

Jun 4, 2012 at 9:43 PM

I'm wondering what the ASP.NET Web Stack team's thoughts are regarding 401 Unauthorized and 403 Forbidden.

Or should I go ahead and post it as an issue?

Coordinator
Jun 4, 2012 at 11:48 PM

This is the right place for discussions. Issue Tracker is for bugs that need code changes.

Here is what the HTTP spec says:

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

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.

Daniel Roth

Jun 8, 2012 at 3:06 PM

I came across this recently as well working in the WebAPI. Since I was writing a custom authentication token to validate an API key anyway, I just took the easy way out and returned 403. I've read other comments about this and the other point of view is that the 401 should be handled differently by forms authentication. Give us an option to specift the behavior when a 401 is returned. Something like, redirect to login page, or redirect to a custom page (where we can display a custom error message and an option to go to the login page), or do nothing.

Jul 13, 2012 at 8:14 PM
Edited Jul 13, 2012 at 8:29 PM

I agree with ShadowChaser's proposal in that the AuthorizeAttribute should use the HTTP return codes as described: 401 should be returned for an Authentication Failure and 403 should be returned for an Authorization Failure.

I too have had to write my own code to make this distinction, so isn't it time that the framework did it properly.

Jul 16, 2012 at 6:32 AM

Well - for me the distinction is not crystal clear. I agree that it would be useful sometimes to have separate error codes..but i am not sure that this was the original intention of the spec.

Jul 16, 2012 at 9:55 PM

For someone who is trying to authenticate and authentication is unsuccessful or not authenticated, 401 is correct. User needs to be authenticated. But with Authorization, user already authenticated. There is absolutely nothing he can do to change the situation hence 403-forbidden makes sense.

The picture also gets complicated because of ASP.NET's forms authentication - which some might still use in an ASP.NET Web API. Forms Authentications' dodgy-hacky-bad-bad code that subscribes to RequestEnd in ASP.NET and redirects to login url in case of 401 makes you having to return other than 401.