Search code examples
asp.net-mvcasp.net-membership

Extend AuthorizeAttribute to detect logged in non-user (How to handle user authorization)


Environment: ASP.NET MVC 4, Visual Studio 2012

The [Authorize] attribute verifies that the user has a valid login cookie, but it does NOT verify that the user actually exists. This would happen if a user is deleted while that user's computer still holds the persisted credentials cookie. In this scenario, a logged-in non-user is allowed to run a controller action marked with the [Authorize] attribute.

The solution would seem to be pretty simple: Extend AuthorizeAttribute and, in the AuthorizeCore routine, verify that the user exists.

Before I write this code for my own use, I'd like to know if someone knows of a ready-to-go solution to this gaping hole in the [Authorize] attribute.


Solution

  • You need a special authentication global action filter.

    Solution to your problem is the following. You have to introduce the global action filter that will be executed before controller action is invoked. This event is named OnActionExecuting. And within this global action filter you can also handle the scenario that user have a valid auth cookie, but does not exists in persistence (DB) anymore (and you have to remove its cookie).

    Here is the code example to get an idea:

        public class LoadCustomPrincipalAttribute : ActionFilterAttribute 
        {
            public override void OnActionExecuting(ActionExecutingContext filterContext)
            {
                    CustomIdentity customIdentity;
    
                    if (HttpContext.Current.User.Identity.IsAuthenticated)
                    {        
                        UserData userData = UserRepository.GetUserByName(HttpContext.Current.User.Identity.Name);
    
                        if (userData == null)
                        {
                          //TODO: Add here user missing logic, 
                          //throw an exception, override with the custom identity with "false" - 
                          //this boolean means that it have IsAuthenticated on false, but you
                          //have to override this in CustomIdentity!
                          //Of course - at this point you also remove the user cookie from response!
                        }
    
                        customIdentity = new CustomIdentity(userData, true);
                    }
                    else
                    {
                        customIdentity = new CustomIdentity(new UserData {Username = "Anonymous"}, false);
                    }
    
                    HttpContext.Current.User = new CustomPrincipal(customIdentity);
    
                base.OnActionExecuting(filterContext);
            }
        }
    

    Hope it helps to you!

    Do not forget to register this action filter as a global one. You can do this like:

        private static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new LoadCustomPrincipalAttribute());
        }
    

    Just to add this. Leave alone AuthorizeAttribute. It should work as it was meant. It simply check the HttpContext.Current.User.Identity.IsAuthenticated == true condition. There are situations that you would need to overide it, but this is not the one. You really need a proper user/auth handling before even AuthorizeAttribute kicks in.