n
nnn
nnn
nIn this article I am going to walk you through how to implement a custom authentication and a custom resource based authentication.
n
n
n
nIntroduction
nResource-based authorization allows you to attach resources such as view, button etc to a role or group, and in turn users that has the particular role or group will automatically have access to the attached resource(s) .
n
n
nThe Concept
nIn this tutorial, I will be taking ASP.Net MVC ActionResult as resources. Since I will be building a custom authentication and authorization, I will make use of membership provider class which has methods that check the user credentials (username & password) and role provider class that is has methods that get returns logged-in user roles.
n
nResources – (views) will be attached to roles and then roles in turn will be assigned to users. Users will only have access to those views that was attached to those roles that user belong to. Unauthorized users will be redirected to Unauthorized page even if they navigated via Url in the browser .
nnn
nnn
n
nCreating the Project
nLet’s fire open our visual studio and create a new ASP.Net MVC web application project named AuthApp and change the Authentication to No Authentication like so:
nnn
nnn
n
n
n
nNext, right click on the model’s folder and create a class name CustomMembership, let it inherit from MembershipProvider class like so:
n
using System;nusing System.Web.Security;nnnamespace AuthApp.Modelsn{n public class CustomMembership: MembershipProvidern {n public override string ApplicationNamen {n getn {n throw new NotImplementedException();n }n setn {n throw new NotImplementedException();n }n }nn public override bool ChangePassword(string username, string oldPassword, string newPassword)n {n throw new NotImplementedException();n }nn public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)n {n throw new NotImplementedException();n }nn public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)n {n throw new NotImplementedException();n }nn public override bool DeleteUser(string username, bool deleteAllRelatedData)n {n throw new NotImplementedException();n }nn public override bool EnablePasswordResetn {n get { throw new NotImplementedException(); }n }nn public override bool EnablePasswordRetrievaln {n get { throw new NotImplementedException(); }n }nn public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)n {n throw new NotImplementedException();n }nn public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)n {n throw new NotImplementedException();n }nn public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)n {n throw new NotImplementedException();n }nn public override int GetNumberOfUsersOnline()n {n throw new NotImplementedException();n }nn public override string GetPassword(string username, string answer)n {n throw new NotImplementedException();n }nn public override MembershipUser GetUser(string username, bool userIsOnline)n {n throw new NotImplementedException();n }nn public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)n {n throw new NotImplementedException();n }nn public override string GetUserNameByEmail(string email)n {n throw new NotImplementedException();n }nn public override int MaxInvalidPasswordAttemptsn {n get { throw new NotImplementedException(); }n }nn public override int MinRequiredNonAlphanumericCharactersn {n get { throw new NotImplementedException(); }n }nn public override int MinRequiredPasswordLengthn {n get { throw new NotImplementedException(); }n }nn public override int PasswordAttemptWindown {n get { throw new NotImplementedException(); }n }nn public override MembershipPasswordFormat PasswordFormatn {n get { throw new NotImplementedException(); }n }nn public override string PasswordStrengthRegularExpressionn {n get { throw new NotImplementedException(); }n }nn public override bool RequiresQuestionAndAnswern {n get { throw new NotImplementedException(); }n }nn public override bool RequiresUniqueEmailn {n get { throw new NotImplementedException(); }n }nn public override string ResetPassword(string username, string answer)n {n throw new NotImplementedException();n }nn public override bool UnlockUser(string userName)n {n throw new NotImplementedException();n }nn public override void UpdateUser(MembershipUser user)n {n throw new NotImplementedException();n }nn public override bool ValidateUser(string username, string password)n {n return true;n n }n }n}nn
n
nnn
nnnThe next important class is the role provider class, in the model’s folder create a class named CustomRole that inherit from RoleProvider class like so:
n
using System;nusing System.Web.Security;nnnamespace AuthApp.Modelsn{n public class CustomRole:RoleProvidern {n public override void AddUsersToRoles(string[] usernames, string[] roleNames)n {n throw new NotImplementedException();n }nn public override string ApplicationNamen {n getn {n throw new NotImplementedException();n }n setn {n throw new NotImplementedException();n }n }nn public override void CreateRole(string roleName)n {n throw new NotImplementedException();n }nn public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)n {n throw new NotImplementedException();n }nn public override string[] FindUsersInRole(string roleName, string usernameToMatch)n {n throw new NotImplementedException();n }nn public override string[] GetAllRoles()n {n throw new NotImplementedException();n }nn public override string[] GetRolesForUser(string username)n {n throw new NotImplementedException();nn }nn public override string[] GetUsersInRole(string roleName)n {n throw new NotImplementedException();n }nn public override bool IsUserInRole(string username, string roleName)n {n return true;n }nn public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)n {n throw new NotImplementedException();n }nn public override bool RoleExists(string roleName)n {n return true;n }n }n}nn
n
n
nWeb Config Modificationnn
nIn order to make use of the custom membership and custom role in our application we need to add it to our web config. First, add these two lines to the appSettings section like so:
n
n n
n
nWithin the system.web section add these lines of code:n
n
n nn n nn nnn n n nn nn nn
n
nnn
nnn
n
nApplication Entities
nCreate a folder named Entities in the root of the web application. We add classes that represent all the tables needed for our application.
n
n
nApplcationUsern
nThis table will be used for storing user information. Add a class within the Entities folder named ApplicationUser like so:
n
namespace AuthApp.Entitiesn{n public class ApplicationUsern {n public int ApplicationUserId { get; set; }n [Required]n [StringLength(50)]n public string Username { get; set; }n [Required]n [StringLength(50)]n public string Password { get; set; }n }n}n
n
n
nRole
nThis table will act as datastore for various roles in the application. The structure is very simple:
n
public class Rolen {n public int RoleId { get; set; }n [Required]n [StringLength(50)]n public string Name { get; set; }n }nn
n
n
n
nUserRole
nThe UserRole table will house userid and associated roles. Since a user can belong to multiple roles, it is better to have this table like this:
n
public class UserRolen {n public int UserRoleId { get; set; }n public int UserId { get; set; }n public int RoleId { get; set; }n public virtual Role Role { get; set; }n public virtual ApplicationUser User { get; set; }n }nn
n
n
nResourcesn
nThe Resources table will be used to store the application resources – in this case the views. The structure is also fairly simple
n
public class Resourcen {n public int ResourceId { get; set; }n [Required]n [StringLength(50)]n public string Controller { get; set; }n [Required]n [StringLength(50)]n public string Action { get; set; }n [StringLength(50)]n public string Area { get; set; }n }nn
n
nRoleResource
nThe RoleResource table will hold the roles and associated resources. Since multiple resources can be assigned to a role hence this structure:n
n
public class RoleResourcen {n public int RoleResourceId { get; set; }n public int RoleId { get; set; }n public int ResourceId { get; set; }n public virtual Resource Resource { get; set; }n public virtual Role Role { get; set; }n }nn
n
nResource-Based Implementation
nI will not be writing about how authentication is done, although you will find it in the sample app in my github repository.
n
nWe will implement this by using ASP.Net MVC ActionFilterAttribute. Action Filters are attributes which inherit from the ActionFilterAttribute class, and can execute either before, during or after an action execution.
nnnnn
nThe first thing we need to do is to create a class that inherit from ActionFilterAttribute, then fetch all controllers, actions and areas that a logged in user have access to and then check it against the current executing controller, action and area. Deny or allow user before any action is executed depend on what actionresult permission.
n
n
nThe ActionFilter Class
nCreate a folder named Infrastructure in the root of the project and add a class named FilterResourceAttribute then override the OnActionExecuting method like so:nn
n
public class FilterResourceAttribute: ActionFilterAttributen {n public override void OnActionExecuting(ActionExecutingContext filterContext)n {n base.OnActionExecuting(filterContext);n }n }n
n
nThe first thing we need to do here is to detect the name of the controller, action and area names that is been requested. To do this let’s add this to our class:
n
var areaName = string.Empty;n var routeData = filterContext.RouteData;n areaName = routeData.DataTokens["area"] as string;n var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;n var actionName = filterContext.ActionDescriptor.ActionName;nn var requiredPermission = $"{controllerName}-{actionName}";n if (!string.IsNullOrWhiteSpace(areaName)) //Area may be an empty stringn {n requiredPermission += $"-{areaName}";n }nn
n
nThe next thing is to check if the current user has permission to view the controller, action or area been accessed. That will be done like this:n
n
if (!srv.hasPermission(requiredPermission))n {n filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary {{ "action", "Unauthorized" }, { "controller", "Error" }, { "area", "" } });n }n
n
nThe code above checked if user does not has permission and send the user to an unthorized page if need be.n
nThe full Actionfilter class should now look like this:n
n
public class FilterResourceAttribute: ActionFilterAttributen {n AuthorizationService srv = new AuthorizationService(new Domain.AuthAppContext());n public override void OnActionExecuting(ActionExecutingContext filterContext)n {n var areaName = string.Empty;n var routeData = filterContext.RouteData;n areaName = routeData.DataTokens["area"] as string;n var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;n var actionName = filterContext.ActionDescriptor.ActionName;nn var requiredPermission = $"{controllerName}-{actionName}";n if (!string.IsNullOrWhiteSpace(areaName))n {n requiredPermission += $"-{areaName}";n }nn if (!srv.hasPermission(requiredPermission))n {n filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary {{ "action", "Unauthorized" }, { "controller", "Error" }, { "area", "" } });n }n n base.OnActionExecuting(filterContext);n }n }nn
n
nAuthorizationService Classn
n
public class AuthorizationServicen {n private AuthAppContext db;n public AuthorizationService(AuthAppContext _db)n {n this.db = _db;n }nn public bool hasPermission(string resource)n {n if (!HttpContext.Current.User.Identity.IsAuthenticated)n {n return false;n }n var userName = HttpContext.Current.User.Identity.Name;nn var loggedInUser = db.Users.Where(x => x.Username == userName).FirstOrDefault();nn List userpages = null;nn if (loggedInUser == null) return false;nn var userResource = (from rr in db.RoleResourcesn join ur in db.UserRoles on rr.RoleId equals ur.RoleIdn where ur.UserId == loggedInUser.ApplicationUserIdn select rr.ResourceId).ToList();nn if (userResource.Any())n {nnn userpages = (from m in db.Resourcesn where userResource.Contains(m.ResourceId)n select new RequiredPermissionVMn {n controllerName = m.Controller,n actionName = m.Action,n areaName = m.Arean }).ToList();n }nn if (!userpages.Any()) return false;nn return userpages.Any(x => x.requiredPermision.ToLower() == resource.ToLower());n }n }n
n
n
nUsage
nTo use the Fiter Attribute just decorate each controller with the attribute like so:n
n
[Authorize]n [FilterResource]n public class PagesController : Controllern {n // GET: Pagesn public ActionResult Index()n {n return View();n }nn public ActionResult Portfolio()n {n return View();n }n } nn
n
nNow, when we run the app, the system will look to see which user we currently logged in, and if we try to access a page that that user didn’t have access to, we will get kicked back out to the unauthorized page.nnnnnAs always, you can check out the sample project which contains both the login and seeded db on GitHub, and please let me know what you think on how to make this better.
n
nHappy Coding!

