Search code examples
c#asp.net-mvccustom-attributesactionfilterattribute

When apply an attribute in class and method together, How to force use method attribute?


I have an attribute to check authentication in controller actions. My attribute like this :

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class AuthenticationRequiredAttribute : ActionFilterAttribute, IAuthenticationFilter
{
    private readonly bool checkAuthentication;
    public AuthenticationRequiredAttribute(bool checkAuthentication)
    {
        this.checkAuthentication = checkAuthentication;
    }

    public void OnAuthentication(AuthenticationContext filterContext)
    {
        if (checkAuthentication && !UserIdentity.IsAuthenticated)
            filterContext.Result = new HttpUnauthorizedResult(); 
    }

    public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
    {
        if (filterContext.Result == null || filterContext.Result is HttpUnauthorizedResult)
        {
            filterContext.Result = new RedirectToRouteResult(
                        new System.Web.Routing.RouteValueDictionary{
                                        {"controller", "Account"},
                                        {"action", "Login"}
                                    });
        }
    }
}

If checkAuthentication = false no check authentication. All actions in a controller should be check authentication except one action. I apply [AuthenticationRequired(true)] on controller and [AuthenticationRequired(false)] on specific action. but it not work and always check authentication. When apply [AuthenticationRequired(true)] on other actions and remove it from controller it work fine.

How I can force use method attribute in this case?


Solution

  • Modify your OnAuthentication and add validation of AllowAnonymous attribute.

    bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)
                                     || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true);
    
            if (skipAuthorization)
            {
                return;
            }
    

    After that just add AllowAnonymous attribute to methods that should skip authentication\authorization.