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

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

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. //Get
  6. [BasicAuthorization, HttpGet, Route("GetCustomers/{pageNumber:int}/{pageSize:int}")]
  7. public IHttpActionResult GetCustomers(int pageNumber, int pageSize)
  8. {
  9. objCust = new CustomersMgt();
  10. return Json(objCust.GetCustomer(pageNumber, pageSize));
  11. }
  12. //Post
  13. [BasicAuthorization, HttpPost, Route("SaveCustomer")]
  14. public IHttpActionResult SaveCustomer(Customer model)
  15. {
  16. objCust = new CustomersMgt();
  17. return Json(objCust.SaveCustomer(model));
  18. }
  19. //Put
  20. [BasicAuthorization, HttpPut, Route("UpdateCustomer")]
  21. public IHttpActionResult UpdateCustomer(Customer model)
  22. {
  23. objCust = new CustomersMgt();
  24. return Json(objCust.UpdateCustomer(model));
  25. }
  26. //Delete
  27. [BasicAuthorization, HttpDelete, Route("DeleteCustomer/{CustomerID:int}")]
  28. public IHttpActionResult DeleteCustomer(int CustomerID)
  29. {
  30. objCust = new CustomersMgt();
  31. return Json(objCust.DeleteCustomer(CustomerID));
  32. }
  33. }
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. private UserAuthorizations objAuth = null;
  7. public override void OnAuthorization(HttpActionContext filterContext)
  8. {
  9. string authorizedToken = string.Empty;
  10. string userAgent = string.Empty;
  11. try
  12. {
  13. var headerToken = filterContext.Request.Headers.SingleOrDefault(x => x.Key == _authorizedToken);
  14. if (headerToken.Key != null)
  15. {
  16. authorizedToken = Convert.ToString(headerToken.Value.SingleOrDefault());
  17. userAgent = Convert.ToString(filterContext.Request.Headers.UserAgent);
  18. if (!IsAuthorize(authorizedToken, userAgent))
  19. {
  20. filterContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
  21. return;
  22. }
  23. }
  24. else
  25. {
  26. filterContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
  27. return;
  28. }
  29. }
  30. catch (Exception)
  31. {
  32. filterContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
  33. return;
  34. }
  35. base.OnAuthorization(filterContext);
  36. }
  37. private bool IsAuthorize(string authorizedToken, string userAgent)
  38. {
  39. objAuth = new UserAuthorizations();
  40. bool result = false;
  41. try
  42. {
  43. result = objAuth.ValidateToken(authorizedToken, userAgent);
  44. }
  45. catch (Exception)
  46. {
  47. result = false;
  48. }
  49. return result;
  50. }
  51. }
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. //Token Generate ClientEnd
  4. $scope.tokenManager = {
  5. generateSecurityToken: function (methodtype) {
  6. var model = {
  7. username: $sessionStorage.loggeduser,
  8. key: methodtype,
  9. ip: $sessionStorage.loggedip,
  10. userAgent: navigator.userAgent.replace(/ \.NET.+;/, '')
  11. };
  12. var message = [model.username, model.ip, model.userAgent].join(':');
  13. var hash = CryptoJS.HmacSHA256(message, model.key);
  14. var token = CryptoJS.enc.Base64.stringify(hash);
  15. var tokenId = [model.username, model.key].join(':');
  16. var tokenGenerated = CryptoJS.enc.Utf8.parse([token, tokenId].join(':'));
  17. return CryptoJS.enc.Base64.stringify(tokenGenerated);
  18. },
  19. };
  20. }]);
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. var encoding = new System.Text.ASCIIEncoding();
  6. byte[] keyByte = encoding.GetBytes(key);
  7. byte[] messageBytes = encoding.GetBytes(message);
  8. using (var hmacsha256 = new HMACSHA256(keyByte))
  9. {
  10. byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
  11. return Convert.ToBase64String(hashmessage);
  12. }
  13. }
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. //compare token
  18. string serverToken = generateToken(objModel.userid, objModel.methodtype, objModel.ip, userAgent);
  19. if (objModel.clientToken == serverToken)
  20. {
  21. result = ValidateAuthorization(objModel.userid, objModel.methodtype);
  22. }
  23. }
  24. }
  25. catch (Exception e)
  26. {
  27. e.ToString();
  28. }
  29. return result;
  30. }
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.