5

I am working in an MVC project where user roles, and permissions for each of the action methods are stored in the database. What I want to achieve is to use the authorize filter feature in mvc by overriding AuthorizeAttribute. This way I can register my custom authorize filter globally.

Here is what I am trying to do, following this method. I am following this article to handle ajax requests.

protected override bool AuthorizeCore(HttpContextBase httpContext)
{
     //Custom permission checking logic 
     return false;//no permission for all methods-Just for testing
}

public override void OnAuthorization(AuthorizationContext filterContext)
{
    base.OnAuthorization(filterContext);
    CheckIfUserIsAuthenticated(filterContext);}
}

private void CheckIfUserIsAuthenticated(AuthorizationContext filterContext)
{
    // If Result is null, we’re OK: the user is authenticated and authorized. 
    if (filterContext.Result == null)
        return;

    var httpContext = filterContext.HttpContext;
    var request = httpContext.Request;
    var response = httpContext.Response;

    var user = httpContext.User;
    // If here, you’re getting an HTTP 401 status code. In particular,
    // filterContext.Result is of HttpUnauthorizedResult type. Check Ajax here. 

    if (request.IsAjaxRequest())
    {
        if (user.Identity.IsAuthenticated == false)
            response.StatusCode = (int)HttpStatusCode.Unauthorized;//401
        else
            response.StatusCode = (int)HttpStatusCode.Forbidden;//403

        response.SuppressFormsAuthenticationRedirect = true;

        response.End();// This line is causing the problems

        return;
    }

    if (user.Identity.IsAuthenticated)
    {
        //Redirect to customerror page
    }
}

The issue is when I am calling a method using ajax post, my custom exception filter is catching an error saying "Server cannot set status after HTTP headers have been sent " If I remove response.End(), I am getting a 401 response in the ajax error handler method instead of 403. I tried

 HttpContext.Current.ApplicationInstance.CompleteRequest()

instead of response.end, but then also I am getting 401 error

Is there anyway to avoid this exception?

Update I am using this general error handler method in javascript to catch all ajax request errors ( I need correct status code for response):

 function ajaxFailed(xhr, textstatus, errorthrown) {
    debugger;
    if (textstatus == "error") {
        if (xhr.status == 401) {
            window.location = "/Account/Login";
        }
        if (xhr.status == 403) {
            $("#divoutput").html("you don't have permission.");
        }
    }
    if (xhr.status == 500) {
        $("#divoutput").append("<br/> internal servererror." + xhr.responseText);
    }
}

Update 2: My Current Solution: Check the Exception.Message property of the raised exception in my custom exception handler filter and ignore it if the message is :"Server cannot set status after HTTP headers have been sent ".

I know this is an inefficient method, but I have no other solution yet. Waiting for a good solution :)

Community
  • 1
  • 1
Sharun
  • 3,022
  • 6
  • 30
  • 59

1 Answers1

0

Try to change the order of the handling the request:

public override void OnAuthorization(AuthorizationContext filterContext)
{
    CheckIfUserIsAuthenticated(filterContext);}
    base.OnAuthorization(filterContext);
}

Update:

The article you've linked says that you have to handle other event handler method HandleUnauthorizedRequest:

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)

Not the OnAuthorization as you did.

Update #2:
Try to use the buffering output with

Response.BufferOutput = true;

so your server is prevented to send the headers back to the client.

VMAtm
  • 27,943
  • 17
  • 79
  • 125
  • if (filterContext.Result == null) is always true if I do so. – Sharun Apr 08 '15 at 13:45
  • THat's because no validation were performed - but the headers were not sent. – VMAtm Apr 08 '15 at 13:54
  • I don't get you. I need to execute base.OnAuthorization(filterContext) first so that user.Identity.IsAuthenticated property will be set correctly. Then in my custom method, I am checking this value to find, whether it is an authentication issue or authorization issue. – Sharun Apr 08 '15 at 13:58
  • This method is sending the `headers` back to the client. After that you can't change status of the response. – VMAtm Apr 08 '15 at 14:00
  • Try to use `Response.BufferOutput = true;` here, preventing the server send the headers to the client, more info here: http://stackoverflow.com/a/2892633/213550 – VMAtm Apr 08 '15 at 14:20