Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC 4 AuthorizeAttribute.HandleUnauthorizedRequest ViewResult - infinite loop

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
like image 462
Sonic Soul Avatar asked Nov 16 '13 00:11

Sonic Soul


2 Answers

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;
            }
        }
    }
like image 151
Sonic Soul Avatar answered Nov 08 '22 15:11

Sonic Soul


The default implementation of AuthorizeAttribute sets the response on the action context, by not calling into the base the response is never set which causes the filter to repeat the authorization process until a response is set (hence the infinite loop).

You can see this logic in the AuthorizationFilterAttribute class which AuthorizeAttribute derives from.

like image 6
James Avatar answered Nov 08 '22 13:11

James