views:

158

answers:

3

I would like to generate a 401 page if the user does not have the right permission.

The user requests a url and is redirected to the login page (I have deny all anonymous in web.config). The user logs in successfully and is redirected to the original url. However, upon permission check, it is determined that the user does not have the required permission, so I would like to generate a 401. But Forms Authentication always handles 401 and redirects the user to the login page.

To me, this isn't correct. The user has already authenticated, the user just does not have the proper authorization.

In other scenarios, such as in ajax or REST service scenario, I definitely do not want the login page - I need the proper 401 page.

So far, I've tried custom Authorize filter to return ViewResult with 401 but didn't work. I then tried a normal Action Filter, overriding OnActionExecuting, which did not work either.

What I was able to do is handle an event in global.asax, PostRequestHandlerExecute, and check for the permission then write out directly to response:

if (permissionDenied)
{
    Context.Response.StatusCode = 401;
    Context.Response.Clear();
    Context.Response.Write("Permission Denied");
    Context.Response.Flush();
    Context.Response.Close();
    return;
}

That works but it's not really what I want. First of all, I'm not even sure if that is the right event or the place in the pipeline to do that. Second, I want the 401 page to have a little more content. Preferably, it should be an aspx page with possibly the same master page as the rest of the site. That way, anyone browsing the site can see that the permission is denied but with the same look and feel, etc. but the ajax or service user will get the proper status code to act on.

Any idea how this can be achieved? I've seen other posts with similar requests but didn't see a solution that I can use.

And no, I do not want a 403.

A: 

You might want to look here: http://stackoverflow.com/questions/1171035/asp-net-mvc-custom-error-handling-application-error-global-asax

You should be able to catch the 401 and route to a custom action at that point. We're doing this for 404 and other exceptions in our code, and it is working quite nicely.

Robaticus
I'm not sure which of the answers you are directing me to.In any case, I do not want to re-route or redirect because then my response status won't be 401 - it will be something like 302 followed by 200, which is not what I'd expect if I was accessing the url from an ajax client.
Jiho Han
Jiho - I apologize, I didn't read the question quite closely enough. I thought you were just trying to do a custom 401 redirect.
Robaticus
A: 

I agree the default behavior is wrong, especially considering Ajax requests. I hope to see some sort of solution in MVCv3 (fingers crossed).

The only way I know to accomplish this is to remove the Authentication section of the web.config, this is what ASP.NET looks for to redirect unauthorized requests. You can not disable this "feature" to my knowledge. If you have an authentication section you WILL be redirected to the login url if ASP.NET ever encounters a 401 status code.

But by removing this you have some other problems. If you ever want the user to be redirected to the login page for non-ajax requests, you will need to implement your own AuthorizeAttribute and use custom settings to determine where to redirect to. Also, anything else in the Authentication section would probably have to be re-implemented by you as well. Not a very practical solution in complicated sites.

I haven't done this myself, I settled for returning a 403 instead. It's annoying not having the proper HttpCode returned, but it's better than any other solution I've found so far.

Jab
you're right - I just took Reflector into FormsAuthenticationModule and in EndRequest handling it checks for 401 and redirects. It doesn't look like there is a way to override this behavior either.
Jiho Han
Yeah, 403s don't sound so bad any more :)
Jab
+1  A: 

I found a workable solution.

401 redirect by FormsAuthenticationModule occurs during EndRequest. Since during event processing Modules are invoked before global.asax, we can override the status code after FormsAuthenticationModule has had its grubby hands on the request.

In my custom AuthorizationFilter, I set HttpContext.Items["PermissionDenied"] to true and then in my global.asax EndRequest, I flip the status code from 200 to 401. Then I Server.Transfer to my custom PermissionDenied view.

I would still prefer FormsAuthenticationModule itself was updated to handle this scenario but I think this is not too hackish that I think I can live with it.

Obviously, you can change how you signal that global.asax should flip the status code. I just tried setting the status code to something like 511 and used that as the condition rather than HttpContext.Items and that worked as well. I guess as long the proper status code goes out the door, the ASP.NET engine doesn't care.

Jiho Han