MVC 4 AuthorizeAttribute.HandleUnauthorizedRequest ViewResult - infinite loop

Sonic Soul picture Sonic Soul · Nov 16, 2013 · Viewed 23k times · Source

i've stepped through my code a million times and can't find a problem with my implementation..

in custom AuthorizeAttribute i overwrote 2 methods

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (!httpContext.Request.IsAuthenticated)
            return false;
        var routeData = httpContext.Request.RequestContext.RouteData;
        var ctrl = routeData.Values["controller"].ToString();
        var action = routeData.Values["action"].ToString();
        var user = httpContext.User.Identity.Name;
        _logger.Info("[logging all the details]");
        return ctrl == "SomeController";
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext ctx)
    {
        ctx.Result = new ViewResult  { ViewName = "Unauthorized" };
        // base.HandleUnauthorizedRequest(ctx);
     }

the authorization logic is mocked to return false only on specific controller, and i've stepped through this to verify it's working correctly.

above code will cause infinite loop. in my log i can see that line hit 666 times (coincidence?) ..

if i do call base.HandleUnauthorizedRequest(ctx), all i get is a blank page. so i reflected what the base does, and it's this

filterContext.Result = new HttpUnauthorizedResult();

so this explains why it renders a blank page instead of redirecting to Unauthorized.cshtml. what i'm not sure about, is why does it go into an infinite loop if i don't call the base.

p.s.

i've verified that if i put the wrong Unauthorized view it will error out (but still hangs indefinitely)

 System.InvalidOperationException: The view 'Unauthorized11' or its master was not found or no view engine supports the searched locations

Answer

Sonic Soul picture Sonic Soul · Jun 10, 2014

Here is the implementation that i ended up going with and it's working very well.

    public override void OnAuthorization(AuthorizationContext filterContext)
    {

        base.OnAuthorization(filterContext);

        // this is overriden for kendo menus to hide 
        var ctrl = filterContext.RequestContext.RouteData.GetRequiredString("controller");
        var action = filterContext.ActionDescriptor.ActionName;

        [custom authorization logic on action/ctrl]

        // useful to determine if it's authorizing current controller path or menu links
        var path = filterContext.HttpContext.Request.PhysicalPath;
        _authorizingCurrentPath = path.Contains(ctrl) || path.EndsWith("WebUI") ;


        if (userAuth < requiredAuth)
            HandleUnauthorizedRequest(filterContext);
    }


    protected override void HandleUnauthorizedRequest(AuthorizationContext ctx)
    {
        if (!ctx.HttpContext.User.Identity.IsAuthenticated)
            base.HandleUnauthorizedRequest(ctx);
        else {
            if (_authorizingCurrentPath) {
                // handle controller access
                ctx.Result = new ViewResult { ViewName = "Unauthorized" };
                ctx.HttpContext.Response.StatusCode = 403;
            }
            else {
                // handle menu links
                ctx.Result = new HttpUnauthorizedResult();
                ctx.HttpContext.Response.StatusCode = 403;
            }
        }
    }