Authorization Filters In ASP.NET Web API 2 And AngularJS

Introduction

It is important to restrict unauthorized access to particular operations/actions of an application. I did an experiment while I was working on a project that needed to restrict an unauthorized person from performing Crud operations. The authorization is based on the user role.

OK, Let's get started. Here are the steps; I hope you will enjoy it.

Contents

  • SQL Database

    • Create a new Database
    • Run the DB-script

  • ASP.NET MVC Application(Web Api)

    • MVC, WebAPI Project
    • Install AngularJS
    • Authentication &
    • Authorization

Create New Database

  1. /****** Object: Database [ApiSecurity] Script Date: 7/3/2016 11:50:11 AM ******/  
  2. CREATE DATABASE [ApiSecurity] ON PRIMARY   
  3. NAME = N'ApiSecurity', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\ApiSecurity.mdf' , SIZE = 3072KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )  
  4. LOG ON   
  5. 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%)  
  6. GO  
After creating the database, let's download and run the script. Let's create a new MVC Application

MVC Application

Install AngularJS for client side scripting from NuGet package installer.

First, we need to login for the authentication check

Authentication & Authorization 
  • Authentication : Identity of the user.
  • Authorization : Allowed to perform an action.
    login

After successful login(Authentication), we can access the get customer link to show all the customers, only if we have the read permission in the database, given below:
database.
In our database table, we have to restrict the access (CanRead to "False") of Administrator to view the customer list.

table

The result will show a 401 response message while fetching the data from the database, where the logged in user role is an administrator.

administrator

Using the code

Here's our API Controller, that is restricted by using [BasicAuthorization] attribute at the top of the Crud methods.

  1. [RoutePrefix("api/Customer")]  
  2. public class CustomerController : ApiController  
  3. {  
  4.     private CustomersMgt objCust = null;  
  5.   
  6.     //Get  
  7.     [BasicAuthorization, HttpGet, Route("GetCustomers/{pageNumber:int}/{pageSize:int}")]  
  8.     public IHttpActionResult GetCustomers(int pageNumber, int pageSize)  
  9.     {  
  10.         objCust = new CustomersMgt();  
  11.         return Json(objCust.GetCustomer(pageNumber, pageSize));  
  12.     }  
  13.   
  14.     //Post  
  15.     [BasicAuthorization, HttpPost, Route("SaveCustomer")]  
  16.     public IHttpActionResult SaveCustomer(Customer model)  
  17.     {  
  18.         objCust = new CustomersMgt();  
  19.         return Json(objCust.SaveCustomer(model));  
  20.     }  
  21.   
  22.     //Put  
  23.     [BasicAuthorization, HttpPut, Route("UpdateCustomer")]  
  24.     public IHttpActionResult UpdateCustomer(Customer model)  
  25.     {  
  26.         objCust = new CustomersMgt();  
  27.         return Json(objCust.UpdateCustomer(model));  
  28.     }  
  29.   
  30.     //Delete  
  31.     [BasicAuthorization, HttpDelete, Route("DeleteCustomer/{CustomerID:int}")]  
  32.     public IHttpActionResult DeleteCustomer(int CustomerID)  
  33.     {  
  34.         objCust = new CustomersMgt();  
  35.         return Json(objCust.DeleteCustomer(CustomerID));  
  36.     }  
  37. }  
The code snippet, given below, of our Customer attribute, which is inherited from AuthorizationFilterAttribute, using System.Web.Http.Filters is targeted to both the  class and method. If you want to target only the method, remove the AttributeTargets class-targeted attributes with the or operator.
  1. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]  
  2. public class BasicAuthorization : AuthorizationFilterAttribute  
  3. {  
  4.     private const string _authorizedToken = "AuthorizedToken";  
  5.     private const string _userAgent = "User-Agent";  
  6.   
  7.     private UserAuthorizations objAuth = null;  
  8.   
  9.     public override void OnAuthorization(HttpActionContext filterContext)  
  10.     {  
  11.         string authorizedToken = string.Empty;  
  12.         string userAgent = string.Empty;  
  13.   
  14.         try  
  15.         {  
  16.             var headerToken = filterContext.Request.Headers.SingleOrDefault(x => x.Key == _authorizedToken);  
  17.             if (headerToken.Key != null)  
  18.             {  
  19.                 authorizedToken = Convert.ToString(headerToken.Value.SingleOrDefault());  
  20.                 userAgent = Convert.ToString(filterContext.Request.Headers.UserAgent);  
  21.                 if (!IsAuthorize(authorizedToken, userAgent))  
  22.                 {  
  23.                     filterContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);  
  24.                     return;  
  25.                 }  
  26.             }  
  27.             else  
  28.             {  
  29.                 filterContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);  
  30.                 return;  
  31.             }  
  32.         }  
  33.         catch (Exception)  
  34.         {  
  35.             filterContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);  
  36.             return;  
  37.         }  
  38.   
  39.         base.OnAuthorization(filterContext);  
  40.     }  
  41.   
  42.     private bool IsAuthorize(string authorizedToken, string userAgent)  
  43.     {  
  44.         objAuth = new UserAuthorizations();  
  45.         bool result = false;  
  46.         try  
  47.         {  
  48.             result = objAuth.ValidateToken(authorizedToken, userAgent);  
  49.         }  
  50.         catch (Exception)  
  51.         {  
  52.             result = false;  
  53.         }  
  54.         return result;  
  55.     }  
Here, OnAuthorization is a method, that overrides from the inherited class, and calls when a process requests for the authorization and filterContext is a parameter which encapsulates the information for using System.Web.Http.Filters.AuthorizationFilterAttribute.

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

Script for Token Generation

Below is the AngularJS script for the token generation at the client end, while each request is a process which generates first and sends it along with the request header to validate.
  1. AppSecurity.controller('tokenCtrl', ['$scope''$http''crudService''$sessionStorage',  
  2. function ($scope, $http, crudService, $sessionStorage) {  
  3.   
  4.     //Token Generate ClientEnd  
  5.     $scope.tokenManager = {  
  6.         generateSecurityToken: function (methodtype) {  
  7.             var model = {  
  8.                 username: $sessionStorage.loggeduser,  
  9.                 key: methodtype,  
  10.                 ip: $sessionStorage.loggedip,  
  11.                 userAgent: navigator.userAgent.replace(/ \.NET.+;/, '')  
  12.             };  
  13.   
  14.             var message = [model.username, model.ip, model.userAgent].join(':');  
  15.             var hash = CryptoJS.HmacSHA256(message, model.key);  
  16.   
  17.             var token = CryptoJS.enc.Base64.stringify(hash);  
  18.             var tokenId = [model.username, model.key].join(':');  
  19.             var tokenGenerated = CryptoJS.enc.Utf8.parse([token, tokenId].join(':'));  
  20.   
  21.             return CryptoJS.enc.Base64.stringify(tokenGenerated);  
  22.         },  
  23.     };  
  24. }]);  
Token is generated by Base64-encode, where the hash has the message body and the encryption key. In this app, we have used Crud type as key.

Server Token Generation

Because of the way in which the client token was generated, we need to re-generate the token. In the same way, we will compare and validate whether the request is fake or not.
  1. public string generateToken(string userid, string methodtype, string ip, string userAgent)  
  2. {  
  3.     string message = string.Join(":"new string[] { userid, ip, userAgent });  
  4.     string key = methodtype ?? "";  
  5.   
  6.     var encoding = new System.Text.ASCIIEncoding();  
  7.   
  8.     byte[] keyByte = encoding.GetBytes(key);  
  9.     byte[] messageBytes = encoding.GetBytes(message);  
  10.   
  11.     using (var hmacsha256 = new HMACSHA256(keyByte))  
  12.     {  
  13.         byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);  
  14.         return Convert.ToBase64String(hashmessage);  
  15.     }  
  16. }  
Validate Token

This part of the code will compare and validate the request in two steps -- first token is compared and it will validate the authorization from the database to access the  Crud operations.
  1. public bool ValidateToken(string authorizedToken, string userAgent)  
  2. {  
  3.     bool result = false;  
  4.     try  
  5.     {  
  6.         string key = Encoding.UTF8.GetString(Convert.FromBase64String(authorizedToken));  
  7.         string[] parts = key.Split(new char[] { ':' });  
  8.         if (parts.Length == 3)  
  9.         {  
  10.             objModel = new tokenModel()  
  11.             {  
  12.                 clientToken = parts[0],  
  13.                 userid = parts[1],  
  14.                 methodtype = parts[2],  
  15.                 ip = HostService.GetIP()  
  16.             };  
  17.   
  18.             //compare token  
  19.             string serverToken = generateToken(objModel.userid, objModel.methodtype, objModel.ip, userAgent);  
  20.             if (objModel.clientToken == serverToken)  
  21.             {  
  22.                 result = ValidateAuthorization(objModel.userid, objModel.methodtype);  
  23.             }  
  24.         }  
  25.     }  
  26.     catch (Exception e)  
  27.     {  
  28.         e.ToString();  
  29.     }  
  30.     return result;  
  31. }  
Authorization

This sample of the code will validate the access permission from the database on each action.
  1. public bool ValidateAuthorization(string userid, string methodtype)  
  2. {  
  3.     bool IsValid = false;  
  4.     if (userid != null)  
  5.     {  
  6.         using (_ctx = new ApiSecurityEntities())  
  7.         {  
  8.             if (_ctx.UserAuthentications.Any(u => u.LoginID == userid && u.StatusID == 1))  
  9.             {  
  10.                 switch (methodtype)  
  11.                 {  
  12.                     case "get":  
  13.                         IsValid = (from u in _ctx.UserAuthentications  
  14.                                     join r in _ctx.UserRoles on u.RoleID equals r.RoleID  
  15.                                     where u.LoginID == userid && u.StatusID == 1 && r.CanRead == true  
  16.                                     select u).Any();  
  17.                         break;  
  18.                     case "post":  
  19.                         IsValid = (from u in _ctx.UserAuthentications  
  20.                                     join r in _ctx.UserRoles on u.RoleID equals r.RoleID  
  21.                                     where u.LoginID == userid && u.StatusID == 1 && r.CanCreate == true  
  22.                                     select u).Any();  
  23.                         break;  
  24.                     case "put":  
  25.                         IsValid = (from u in _ctx.UserAuthentications  
  26.                                     join r in _ctx.UserRoles on u.RoleID equals r.RoleID  
  27.                                     where u.LoginID == userid && u.StatusID == 1 && r.CanUpdate == true  
  28.                                     select u).Any();  
  29.                         break;  
  30.                     case "delete":  
  31.                         IsValid = (from u in _ctx.UserAuthentications  
  32.                                     join r in _ctx.UserRoles on u.RoleID equals r.RoleID  
  33.                                     where u.LoginID == userid && u.StatusID == 1 && r.CanDelete == true  
  34.                                     select u).Any();  
  35.                         break;  
  36.                     default:  
  37.                         IsValid = false;  
  38.                         break;  
  39.                 }  
  40.             }  
  41.         }  
  42.     }  
  43.     return IsValid;  
  44. }  
I hope this helped.