Asp.Net Web Api Custom Authorize Attribute with Custom Database Table

Securing your application on the web is essential part and common. And especially when you plane to reach to every customer then you have to think of security as a first step. Well luckily, securing Asp.Net applications aren’t that much difficult as the .Net framework provide out of the box many essential security features to make our jobs (as a developer) easy. 

Basically there are two common ways to secure your application i.e Authentication and Authorization. Now these two topics cannot be explained in short article like this. So if you are interested in reading more about it then I suggest you should definitely check for MVC and for Web Api

Hush! I don’t like a lot of theories so let’s see how we can secure our application by using Custom Authorize Attribute in Web Api as implementing authorization (not authenticating). 

Requirements Scope

Of course I have already created a Web Api project in visual studio and I am using Web Api 2.2. So I will create a class called ApiAuthorizeAttribute (you guessed it, it is an attribute). As this is an attribute and my requirement is that we can apply it as a class level and method level and we can also skip (bypass / ignore) some methods which we want to publically exposed it (if we want so). 

Here is the attribute looks like;

    // you can use [ApiAuthorize] only you don't want to skip the authorization on a class or a method
    [ApiAuthorize(ByPassAuthorization = true)]
    public class BasePublicApiController : BaseApiController
    {

    }

Explanation

So, of course this class I will inherit from ‘System.Web.Http.AuthorizeAttribute’. Notice that I am using System.Web.Http namespace as this is web api namespace and not mvc. 

    
    public class ApiAuthorizeAttribute : System.Web.Http.AuthorizeAttribute
    {
    }

Now i will have 1 private variable for response reason and a property to bypass this authorization check. 

        private string _responseReason = "";
        public bool ByPassAuthorization { get; set; }

Then i will create a method which will simply pass the unauthorized status code and message to the response like so;

        protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden);
            if (!string.IsNullOrEmpty(_responseReason))
                actionContext.Response.ReasonPhrase = _responseReason;
        }

Next, we need a couple of methods to check if a class or a method has our custom attribute becuase as we can apply our authorize attribute metheod level, so we need to check (we will do check later) if we have such attribute applied on a method then we want to skip authorization if that method has set the ByPassAuthorization property set to true.

        
private IEnumerable<apiauthorizeattribute>GetApiAuthorizeAttributes(HttpActionDescriptor descriptor)
        {
            return descriptor.GetCustomAttributes<apiauthorizeattribute>(true)
                .Concat(descriptor.ControllerDescriptor.GetCustomAttributes<apiauthorizeattribute>(true));
        }

private bool IsApiPageRequested(HttpActionContext actionContext)
        {
            var apiAttributes = GetApiAuthorizeAttributes(actionContext.ActionDescriptor);
            if (apiAttributes != null && apiAttributes.Any())
                return true;
            return false;
        }

Now, i will override the base AuthorizeAttribute class method to use the above methods. This method will check (our custom requirments) first that whether our custom plugin (current web api plugin) has permission for the current store. if so then go to the custom Database and Table otherwise don't even bother about looking / check for permission for current user. Becuase we need this to make sure that some logic runs before the customer is authenticated. So if you have such requirements the you can write below logic inside Authroizae method otherwise you can skip the logic and directly call  base.OnAuthorization(actionContext).

  
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            //logic that will run before even authorizing the customer / user. if this logic fails
            // then the user checking against our custom database table will not processed. 
           // you can skip this if you don't have such requirements and directly call 
            if (IsApiPageRequested(actionContext))
            {
                if (!this.IsPluginAvailableForCurrentStore())
                {
                    this.HandleUnauthorizedRequest(actionContext);
                    _responseReason = "Web services plugin is not available in this store";
                }
                else
                {
                    base.OnAuthorization(actionContext);
                }
            }

        }     

When we call the base.OnAuthorization(actionContext), we are now calling another method called IsAuthorized() which basically calls after OnAuthorization. This is the method which will check the user against our custom database table.  

        
        private bool HasWebServiceAccess()
        {
            //_permissionService is our custom service that calls Authorize() method which goes to the custom table in database and check if current user has some permission the reply back with true or false
            bool result = _permissionService.Authorize(HttpContext.Current.User);
            return result;
        } 
        protected override bool IsAuthorized(HttpActionContext actionContext)
        { 
            //logic for check whether we have an attribute with ByPassAuthorization = true e.g [ByPassAuthorization(true)], if so then just return true  
            if (ByPassAuthorization 
                || GetApiAuthorizeAttributes(actionContext.ActionDescriptor).Any(x=> x.ByPassAuthorization))
                return true;
            //checking against our custom table goes here 
            if (!this.HasWebServiceAccess())
            {
                this.HandleUnauthorizedRequest(actionContext);
                _responseReason = "Access Denied";
                return false;
            }

            return base.IsAuthorized(actionContext);
        }

So, as you can see at last we reached to the point where we are going to our custom database table that returns only true or false and then are passing (if false) the unathorize response to the client / browser. 

I hope i explained it easy for you and everything made sense. 




Write your own review
Bad Excellent