Authorization Filters in ASP.NET Web API 2

In this post we will focus on securing the ASP.Net Web Api2 using Authorization Filter, before step into that let’s shortly discus about Web API.

ASP.NET Web API:

Application Programming Interface (API) is a platform that help makes to build HTTP service accessed by different application interfaces, also known as Representational State Transfer (REST) Service for networked application.

HTTP Methods in Web API: Http is all about to create a request & getting server response that correspond four HTTP methods.

  • C – Create – POST
  • R – Read – GET
  • U – Update – PUT
  • D – Delete – DELETE

Example request:

GET http://localhost/api/products HTTP/1.1
User-Agent: Fiddler
Host: localhost:24127
Accept: application/json

Example response:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
Date: Mon, 27 Jan 2016 08:53:35 GMT
Content-Length: 56

[{"Id":1,"Name":"Yo-yo","Category":"Toys","Price":6.95}]

Types of HTTP Status Codes:

  • 1XX Codes: Informational codes. Rarely used in modern web apps.
  • 2XX Codes: Success codes. Tells the client that the request succeeded.
  • 3XX Codes: Redirect codes. Tells the client that they may need to redirect to another location.
  • 4XX Codes: Client Error codes. Tells the client that something was wrong with what it sent to the server.
  • 5XX Codes: Server Error codes. Tells the client that something went wrong on the server’s side, so that the client may attempt the request again, possibly at a later time.

HTTP Status Codes:

  • 200 – OK
  • 204 – No Content
  • 304 – Not Modified
  • 400 – Bad Request
  • 401 – Unauthorized
  • 404 – Not Found
  • 500 – Internal Server Error
  • 501 – Not Implemented

Now let’s get back to security issue while using ASP.Net Web Api2. It is important to restrict un-authorized access of particular operation/action of application. This has experimented while i was working on a project that needed to restrict un-authorized person to perform crud operations. The authorization is based on user role.

OK, Let’s get started, here’s the steps hope you will enjoy it.

Contents

  • SQL Database
    • Create new Database
    • Run the db-script
  • ASP.Net Mvc Application(Web Api)
    • MVC, WebApi Project
    • Install AngularJS
    • Authentication &
    • Authorization

Create New Database

/****** Object:  Database [ApiSecurity]    Script Date: 7/3/2016 11:50:11 AM ******/
CREATE DATABASE [ApiSecurity] ON  PRIMARY 
( NAME = N'ApiSecurity', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\ApiSecurity.mdf' , SIZE = 3072KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
 LOG ON 
( NAME = N'ApiSecurity_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\ApiSecurity_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
GO

After creating the database let’s download & run the script. Let’s create a new MVC application

MVC Application

api_7

Install the AngularJS for clientside scripting from nuget package installer.

api_6

First we need to login for authentication check

api_1

Authentication & Authorization: 

  • Authentication : identity of the user.
  • Authorization : allowed to perform an action.

After successfully login(Authentication) we can access the get customer link to show all the customer, only if we have the read permission in database.

api_3

In our database table we have restrict the access (CanRead to “False”) of Administrator to view customer list.

api_4

The result will show 401 response message while fetching data from database where logged user role is administrator.

api_5

Using the code

Here’s our api Controller  that is restrict by using [BasicAuthorization] attribute at the top of the CRUD methods.

[RoutePrefix("api/Customer")]
public class CustomerController : ApiController
{
    private CustomersMgt objCust = null;

    //Get
    [BasicAuthorization, HttpGet, Route("GetCustomers/{pageNumber:int}/{pageSize:int}")]
    public IHttpActionResult GetCustomers(int pageNumber, int pageSize)
    {
        objCust = new CustomersMgt();
        return Json(objCust.GetCustomer(pageNumber, pageSize));
    }

    //Post
    [BasicAuthorization, HttpPost, Route("SaveCustomer")]
    public IHttpActionResult SaveCustomer(Customer model)
    {
        objCust = new CustomersMgt();
        return Json(objCust.SaveCustomer(model));
    }

    //Put
    [BasicAuthorization, HttpPut, Route("UpdateCustomer")]
    public IHttpActionResult UpdateCustomer(Customer model)
    {
        objCust = new CustomersMgt();
        return Json(objCust.UpdateCustomer(model));
    }

    //Delete
    [BasicAuthorization, HttpDelete, Route("DeleteCustomer/{CustomerID:int}")]
    public IHttpActionResult DeleteCustomer(int CustomerID)
    {
        objCust = new CustomersMgt();
        return Json(objCust.DeleteCustomer(CustomerID));
    }
}

Below code snippet of our Custome Attribute which  is inherited from AuthorizationFilterAttributeusing System.Web.Http.Filters. Targeted to both class and method, if you want target only  method then remove the AttributeTargets Class tagated attributes with the or oerator.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class BasicAuthorization : AuthorizationFilterAttribute
{
    private const string _authorizedToken = "AuthorizedToken";
    private const string _userAgent = "User-Agent";

    private UserAuthorizations objAuth = null;

    public override void OnAuthorization(HttpActionContext filterContext)
    {
        string authorizedToken = string.Empty;
        string userAgent = string.Empty;

        try
        {
            var headerToken = filterContext.Request.Headers.SingleOrDefault(x => x.Key == _authorizedToken);
            if (headerToken.Key != null)
            {
                authorizedToken = Convert.ToString(headerToken.Value.SingleOrDefault());
                userAgent = Convert.ToString(filterContext.Request.Headers.UserAgent);
                if (!IsAuthorize(authorizedToken, userAgent))
                {
                    filterContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
                    return;
                }
            }
            else
            {
                filterContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
                return;
            }
        }
        catch (Exception)
        {
            filterContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            return;
        }

        base.OnAuthorization(filterContext);
    }

    private bool IsAuthorize(string authorizedToken, string userAgent)
    {
        objAuth = new UserAuthorizations();
        bool result = false;
        try
        {
            result = objAuth.ValidateToken(authorizedToken, userAgent);
        }
        catch (Exception)
        {
            result = false;
        }
        return result;
    }
}

Here OnAuthorization is a method that override from inherited class, calls when a process requests for authorization & filterContext is parameter which encapsulates information for using System.Web.Http.Filters.AuthorizationFilterAttribute

In this section exception is handled by sending response of Forbidden (403) & Unauthorized (401) of response code.

Script for Token Generation

Below is angularJS script for token generation at client end, while each request is process, this is generated first and send it along with request header to validate.

AppSecurity.controller('tokenCtrl', ['$scope', '$http', 'crudService', '$sessionStorage',
function ($scope, $http, crudService, $sessionStorage) {

    //Token Generate ClientEnd
    $scope.tokenManager = {
        generateSecurityToken: function (methodtype) {
            var model = {
                username: $sessionStorage.loggeduser,
                key: methodtype,
                ip: $sessionStorage.loggedip,
                userAgent: navigator.userAgent.replace(/ \.NET.+;/, '')
            };

            var message = [model.username, model.ip, model.userAgent].join(':');
            var hash = CryptoJS.HmacSHA256(message, model.key);

            var token = CryptoJS.enc.Base64.stringify(hash);
            var tokenId = [model.username, model.key].join(':');
            var tokenGenerated = CryptoJS.enc.Utf8.parse([token, tokenId].join(':'));

            return CryptoJS.enc.Base64.stringify(tokenGenerated);
        },
    };
}]);

Token is generated by Base64-encode, where the hash has the message body & the encryption key, in this app we have use crud type as key.

Server Token Generation

The way which the client token was generated we need to re-generate the token according to the same way, which will compare & validate whether the request is fake?

public string generateToken(string userid, string methodtype, string ip, string userAgent)
{
    string message = string.Join(":", new string[] { userid, ip, userAgent });
    string key = methodtype ?? "";

    var encoding = new System.Text.ASCIIEncoding();

    byte[] keyByte = encoding.GetBytes(key);
    byte[] messageBytes = encoding.GetBytes(message);

    using (var hmacsha256 = new HMACSHA256(keyByte))
    {
        byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
        return Convert.ToBase64String(hashmessage);
    }
}

Validate Token

This part of code will compare & validate the request in two step, first token is compared and it will validate the authorization from database of access the crud operations.

public bool ValidateToken(string authorizedToken, string userAgent)
{
    bool result = false;
    try
    {
        string key = Encoding.UTF8.GetString(Convert.FromBase64String(authorizedToken));
        string[] parts = key.Split(new char[] { ':' });
        if (parts.Length == 3)
        {
            objModel = new tokenModel()
            {
                clientToken = parts[0],
                userid = parts[1],
                methodtype = parts[2],
                ip = HostService.GetIP()
            };

            //compare token
            string serverToken = generateToken(objModel.userid, objModel.methodtype, objModel.ip, userAgent);
            if (objModel.clientToken == serverToken)
            {
                result = ValidateAuthorization(objModel.userid, objModel.methodtype);
            }
        }
    }
    catch (Exception e)
    {
        e.ToString();
    }
    return result;
}

Authorization

This sample of code will validate the access permission from database on each action.

public bool ValidateAuthorization(string userid, string methodtype)
{
    bool IsValid = false;
    if (userid != null)
    {
        using (_ctx = new ApiSecurityEntities())
        {
            if (_ctx.UserAuthentications.Any(u => u.LoginID == userid && u.StatusID == 1))
            {
                switch (methodtype)
                {
                    case "get":
                        IsValid = (from u in _ctx.UserAuthentications
                                    join r in _ctx.UserRoles on u.RoleID equals r.RoleID
                                    where u.LoginID == userid && u.StatusID == 1 && r.CanRead == true
                                    select u).Any();
                        break;
                    case "post":
                        IsValid = (from u in _ctx.UserAuthentications
                                    join r in _ctx.UserRoles on u.RoleID equals r.RoleID
                                    where u.LoginID == userid && u.StatusID == 1 && r.CanCreate == true
                                    select u).Any();
                        break;
                    case "put":
                        IsValid = (from u in _ctx.UserAuthentications
                                    join r in _ctx.UserRoles on u.RoleID equals r.RoleID
                                    where u.LoginID == userid && u.StatusID == 1 && r.CanUpdate == true
                                    select u).Any();
                        break;
                    case "delete":
                        IsValid = (from u in _ctx.UserAuthentications
                                    join r in _ctx.UserRoles on u.RoleID equals r.RoleID
                                    where u.LoginID == userid && u.StatusID == 1 && r.CanDelete == true
                                    select u).Any();
                        break;
                    default:
                        IsValid = false;
                        break;
                }
            }
        }
    }
    return IsValid;
}

Source Code: I’ve uploaded the full source code to download/clone , Hope this will help 🙂

Author:

Since March 2011, have 8+ years of professional experience on software development, currently working as Senior Software Engineer at s3 Innovate Pte Ltd.

3 thoughts on “Authorization Filters in ASP.NET Web API 2”

Leave a Reply