views:

291

answers:

2

I am using a custom authorization filter on my ASP.NET MVC controllers that redirects the user to a url other than the login screen if they fail authorisation on a particular action.

This is ok for actions that return views, but many of my actions return other result types such as PartialResult or JsonResult.

My current filter looks like this:

<AuthorizeWithRedirect(Roles:="ServerAccess", Controller:="Home", Action:="Unauthorised")>

This indicates that if the user is not in the ServerAccess role then they should be redirected to /Home/Unauthorised/

I am curious how other people are handling this? This seems particularly problematic when you consider the number of actions that are intended to only be called by client-side script AJAX calls. How can the /Home/Unauthorised/ action know whether the caller was intended to receive a view, partialview, json, content, etc?

+1  A: 

I think you'll need to pass in that information with the redirection.

A couple ways you could handle this:

  • Consider making separate action methods for each type of response you need - UnauthorizedJson, UnauthorizedHtml, UnauthorizedEtc... that corresponded to the original action response type

  • Pass in the format information with the redirection by adding another parameter to the Unauthorized method and appending it to the URL in your filter

womp
The first option is the route I have currently taken, with a controller action for each type of ActionResult ie UnauthorisedPartial, UnauthorisedJSON. It allows me to write one standard page and return it to any caller.
Nick
+2  A: 

Use Request.IsAjaxRequest(), e.g.:

public sealed class AjaxAuthorizeAttribute : AuthorizeAttribute
{
    public AjaxAuthorizeAttribute() : base()
    {
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        // Extends the original Web.MVC.AuthorizeAttribute for Ajax calls.
        // Basically if the request is not authorized and the request is an AJAX Request.
        // then we simply set the stats Code to 403 and set an empty Result, in order to 
        // determine in Javascript if the AJAX call came back completed and valid.
        base.OnAuthorization(filterContext);
        if (filterContext.Result == null)
        {
            return;
        }
        else if (filterContext.Result.GetType() == typeof(HttpUnauthorizedResult) 
                 && filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = new ContentResult();
            filterContext.HttpContext.Response.StatusCode = 403;
        }
    }
}

Note 403, not 401, since ASP.NET intercepts 401s and turns them into HTML error pages. It doesn't matter what the AJAX call expected to receive; it can still see the status code.

Craig Stuntz
Returning a 403 is an interesting approach that I hadn't considered. I guess this puts the onus on the client script to generate an appropriate message for the user.
Nick
That onus is always present with AJAX, no matter how you do it.
Craig Stuntz