Custom Authentication Filter In ASP.NET MVC

In this article, you will learn about custom authentication filter in ASP.NET MVC.

Introduction

 
This article will explain the custom authentication filter in ASP.NET MVC. I will discuss with an example. Authentication filter was introduced in ASP.NET MVC 5 and provides great improvement for authenticating a user. As of now, there is no in-built authentication filter in MVC. So, if you want to use authentication filter, then the one and only way is to create a custom authentication filter and use that filter in your application. I suggest you please read the following series of articles for better understanding.

The real reason behind Authentication Filters?

 
Before the introduction of authentication filter in ASP.NET MVC, we developers used authorization filter for two different purposes - authentication and authorization. It was convenient because authorization filters were executed before executing any other action filters. For example, before executing the actual action method, we can use an authorization filter to redirect an unauthenticated user to a login page or some error page.
 
But now, we can separate the authentication-related tasks to a new custom authentication filter and perform the authorization related tasks using the authorization filters only. So, in simple words, we can say it is basically about the separation of concerns which will provide the developers with a focus on one aspect using one filter only.
 

Create Custom Authentication Filter in ASP.NET MVC?

 
To create a custom authentication filter in ASP.NET MVC, we need to create a class by implementing the IAuthenticationFilter Interface. This IAuthenticationFilter interface has 2 methods.
  1. OnAuthentication
  2. OnAuthenticationChallege
Step 1
 
Open Visual Studio 2015 or an editor of your choice and create a new project.
 
Step 2
 
Choose the "web application" project and give an appropriate name to your project.
 
Custom Authentication Filter in ASP.NET MVC
 
Step 3
 
Select the "empty" template, check on the MVC box and click OK.
 
Custom Authentication Filter in ASP.NET MVC
 
Step 4
 
Right-click on the Models folder and add a database model. Add Entity Framework now. For that, right-click on Models folder, select Add, then select New Item.
 
Custom Authentication Filter in ASP.NET MVC
 
You will get a window; from there, select Data from the left panel and choose ADO.NET Entity Data Model, give it the name EmployeeModel (this name is not mandatory, you can give any name) and click "Add"
 
Custom Authentication Filter in ASP.NET MVC
 
After you click on "Add a window", the wizard will open. Choose EF Designer from the database and click "Next".
 
Custom Authentication Filter in ASP.NET MVC
 
After clicking on "Next", a window will appear. Choose New Connection. Another window will appear. Add your server name - if it is local, then enter a dot (.). Choose your database and click "OK".
 
Custom Authentication Filter in ASP.NET MVC
 
The connection will be added. If you wish, save the connection name as you want. You can change the name of your connection below. It will save the connection in the web config. Now, click "Next".
 
Custom Authentication Filter in ASP.NET MVC
 
After clicking on NEXT, another window will appear. Choose the database table name as shown in the below screenshot and click "Finish".
 
Custom Authentication Filter in ASP.NET MVC
 
Entity Framework gets added and the respective class gets generated under the Models folder.
 
Custom Authentication Filter in ASP.NET MVC
 
Step 5
 
Right-click on Controllers folder add a controller.
 
Custom Authentication Filter in ASP.NET MVC
 
A window will appear. Choose MVC5 Controller with views, using Entity Framework and click "Add".
 
Custom Authentication Filter in ASP.NET MVC
 
After clicking on "Add", another window will appear choose Model Class and data context class and click "Add". The EmployeesController will be added under the Controllers folder with respective views and views folder create details, edit and delete.
 
Custom Authentication Filter in ASP.NET MVC
 
Modify Employees Controller Code
  1. using MvcRoleBasedAuthentication_Demo.Models;  
  2. using System.Data.Entity;  
  3. using System.Linq;  
  4. using System.Net;  
  5. using System.Web.Mvc;  
  6.    
  7. namespace MvcRoleBasedAuthentication_Demo.Controllers  
  8. {  
  9.    
  10.     public class EmployeesController : Controller  
  11.     {  
  12.         private EmployeeContext db = new EmployeeContext();  
  13.    
  14.         [Authorize(Roles ="Admin,Employee")]  
  15.         public ActionResult Index()  
  16.         {  
  17.             var employees = db.Employees.Include(e => e.Department);  
  18.             return View(employees.ToList());  
  19.         }  
  20.    
  21.         [Authorize(Roles = "Admin,Employee")]  
  22.         public ActionResult Details(int? id)  
  23.         {  
  24.             if (id == null)  
  25.             {  
  26.                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);  
  27.             }  
  28.             Employee employee = db.Employees.Find(id);  
  29.             if (employee == null)  
  30.             {  
  31.                 return HttpNotFound();  
  32.             }  
  33.             return View(employee);  
  34.         }  
  35.    
  36.         [Authorize(Roles = "Employee")]  
  37.         public ActionResult Create()  
  38.         {  
  39.             ViewBag.DepartmentId = new SelectList(db.Departments, "DeptId""DepartmentName");  
  40.             return View();  
  41.         }  
  42.    
  43.         [Authorize(Roles = "Employee")]  
  44.         [HttpPost]  
  45.         [ValidateAntiForgeryToken]  
  46.         public ActionResult Create([Bind(Include = "EmpId,Name,Gender,Age,Position,Office,HireDate,Salary,DepartmentId")] Employee employee)  
  47.         {  
  48.             if (ModelState.IsValid)  
  49.             {  
  50.                 db.Employees.Add(employee);  
  51.                 db.SaveChanges();  
  52.                 return RedirectToAction("Index");  
  53.             }  
  54.    
  55.             ViewBag.DepartmentId = new SelectList(db.Departments, "DeptId""DepartmentName", employee.DepartmentId);  
  56.             return View(employee);  
  57.         }  
  58.    
  59.         [Authorize(Roles = "Admin,Employee")]  
  60.         public ActionResult Edit(int? id)  
  61.         {  
  62.             if (id == null)  
  63.             {  
  64.                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);  
  65.             }  
  66.             Employee employee = db.Employees.Find(id);  
  67.             if (employee == null)  
  68.             {  
  69.                 return HttpNotFound();  
  70.             }  
  71.             ViewBag.DepartmentId = new SelectList(db.Departments, "DeptId""DepartmentName", employee.DepartmentId);  
  72.             return View(employee);  
  73.         }  
  74.    
  75.         [Authorize(Roles = "Admin,Employee")]  
  76.         [HttpPost]  
  77.         [ValidateAntiForgeryToken]  
  78.         public ActionResult Edit([Bind(Include = "EmpId,Name,Gender,Age,Position,Office,HireDate,Salary,DepartmentId")] Employee employee)  
  79.         {  
  80.             if (ModelState.IsValid)  
  81.             {  
  82.                 db.Entry(employee).State = EntityState.Modified;  
  83.                 db.SaveChanges();  
  84.                 return RedirectToAction("Index");  
  85.             }  
  86.             ViewBag.DepartmentId = new SelectList(db.Departments, "DeptId""DepartmentName", employee.DepartmentId);  
  87.             return View(employee);  
  88.         }  
  89.    
  90.         [Authorize(Roles = "Admin,Employee")]  
  91.         public ActionResult Delete(int? id)  
  92.         {  
  93.             if (id == null)  
  94.             {  
  95.                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);  
  96.             }  
  97.             Employee employee = db.Employees.Find(id);  
  98.             if (employee == null)  
  99.             {  
  100.                 return HttpNotFound();  
  101.             }  
  102.             return View(employee);  
  103.         }  
  104.    
  105.         [Authorize(Roles = "Admin,Employee")]  
  106.         [HttpPost, ActionName("Delete")]  
  107.         [ValidateAntiForgeryToken]  
  108.         public ActionResult DeleteConfirmed(int id)  
  109.         {  
  110.             Employee employee = db.Employees.Find(id);  
  111.             db.Employees.Remove(employee);  
  112.             db.SaveChanges();  
  113.             return RedirectToAction("Index");  
  114.         }  
  115.    
  116.         protected override void Dispose(bool disposing)  
  117.         {  
  118.             if (disposing)  
  119.             {  
  120.                 db.Dispose();  
  121.             }  
  122.             base.Dispose(disposing);  
  123.         }  
  124.     }  
  125. }  
Similarly “Add” two more controller
  1. Home Controller
  2. Account Controller
Step 6
 
Right-click on Controllers folder add Home controller first.
 
Custom Authentication Filter in ASP.NET MVC
 
A window will appear. Choose MVC5 Controller-Empty and click "Add".
 
Custom Authentication Filter in ASP.NET MVC
 
After clicking on "Add", another window will appear with DefaultController. Change the name to HomeController and click "Add". The HomeController will be added under the Controllers folder. Don’t change the Controller suffix for all controllers, change only the highlight, and instead of Default, just change Home.
 
Custom Authentication Filter in ASP.NET MVC
  1. using MvcCustomAuthenticationFilter_Demo.Models;  
  2. using System.Web.Mvc;  
  3.    
  4. namespace MvcCustomAuthenticationFilter_Demo.Controllers  
  5. {  
  6.     public class HomeController : Controller  
  7.     {  
  8.         [CustomAuthenticationFilter]  
  9.         public ActionResult Index()  
  10.         {  
  11.             return View();  
  12.         }  
  13.    
  14.         public ActionResult About()  
  15.         {  
  16.             return View();  
  17.         }  
  18.    
  19.         [CustomAuthenticationFilter]  
  20.         public ActionResult Contact()  
  21.         {  
  22.             return View();  
  23.         }  
  24.     }  
  25. }  
Similarly add another controller AccountController.
  1. using MvcCustomAuthenticationFilter_Demo.Models;  
  2. using System.Web.Mvc;  
  3.    
  4. namespace MvcCustomAuthenticationFilter_Demo.Controllers  
  5. {  
  6.     public class AccountController : Controller  
  7.     {  
  8.         // GET: Account  
  9.         public ActionResult Login()  
  10.         {  
  11.             return View();  
  12.         }  
  13.    
  14.         [HttpPost]  
  15.         [ValidateAntiForgeryToken]  
  16.         public ActionResult Login(UserLogin login)  
  17.         {  
  18.             if (ModelState.IsValid)  
  19.             {  
  20.                 if (login.Username.ToLower()=="admin" && login.Password=="admin")  
  21.                 {  
  22.                     Session["Username"] = login.Username;  
  23.                     return RedirectToAction("Index""Home");  
  24.                 }  
  25.                 else  
  26.                 {  
  27.                     ModelState.AddModelError("""Invalid User Name or Password");  
  28.                     return View(login);  
  29.                 }  
  30.    
  31.             }  
  32.             return View(login);  
  33.         }  
  34.     }  
  35. }  
Step 7
 
Right-click on Models folder and create a UserRoleProvider class.
 
Custom Authentication Filter in ASP.NET MVC
 
Custom Authentication Filter in ASP.NET MVC
 
UserRoleProvider Class
  1. using System;  
  2. using System.Linq;  
  3. using System.Web.Security;  
  4.    
  5. namespace MvcRoleBasedAuthentication_Demo.Models  
  6. {  
  7.     public class UserRoleProvider : RoleProvider  
  8.     {  
  9.         public override string ApplicationName  
  10.         {  
  11.             get  
  12.             {  
  13.                 throw new NotImplementedException();  
  14.             }  
  15.    
  16.             set  
  17.             {  
  18.                 throw new NotImplementedException();  
  19.             }  
  20.         }  
  21.    
  22.         public override void AddUsersToRoles(string[] usernames, string[] roleNames)  
  23.         {  
  24.             throw new NotImplementedException();  
  25.         }  
  26.    
  27.         public override void CreateRole(string roleName)  
  28.         {  
  29.             throw new NotImplementedException();  
  30.         }  
  31.    
  32.         public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)  
  33.         {  
  34.             throw new NotImplementedException();  
  35.         }  
  36.    
  37.         public override string[] FindUsersInRole(string roleName, string usernameToMatch)  
  38.         {  
  39.             throw new NotImplementedException();  
  40.         }  
  41.    
  42.         public override string[] GetAllRoles()  
  43.         {  
  44.             throw new NotImplementedException();  
  45.         }  
  46.    
  47.         public override string[] GetRolesForUser(string username)  
  48.         {  
  49.             using (EmployeeContext _Context=new EmployeeContext())  
  50.             {  
  51.                 var userRoles = (from user in _Context.Users  
  52.                                  join roleMapping in _Context.UserRoleMappings  
  53.                                  on user.Id equals roleMapping.UserId  
  54.                                  join role in _Context.Roles  
  55.                                  on roleMapping.RoleId equals role.Id  
  56.                                  where user.Username == username  
  57.                                  select role.RoleName).ToArray();  
  58.                 return userRoles;  
  59.             }               
  60.         }  
  61.    
  62.         public override string[] GetUsersInRole(string roleName)  
  63.         {  
  64.             throw new NotImplementedException();  
  65.         }  
  66.    
  67.         public override bool IsUserInRole(string username, string roleName)  
  68.         {  
  69.             throw new NotImplementedException();  
  70.         }  
  71.    
  72.         public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)  
  73.         {  
  74.             throw new NotImplementedException();  
  75.         }  
  76.    
  77.         public override bool RoleExists(string roleName)  
  78.         {  
  79.             throw new NotImplementedException();  
  80.         }  
  81.     }  
  82. }  
Step 8
 
Create CustomAuthenticationFilter class and implement ActionFilterAttribute and IAuthenticationFilter interface methods.
 
  1. using System;  
  2. using System.Web.Mvc;  
  3. using System.Web.Mvc.Filters;  
  4. using System.Web.Routing;  
  5.   
  6. namespace MvcCustomAuthenticationFilter_Demo.Models  
  7. {  
  8.     public class CustomAuthenticationFilter : ActionFilterAttribute, IAuthenticationFilter  
  9.     {  
  10.         void IAuthenticationFilter.OnAuthentication(AuthenticationContext filterContext)  
  11.         {  
  12.             if (string.IsNullOrEmpty(Convert.ToString(filterContext.HttpContext.Session["Username"])))  
  13.             {  
  14.                 filterContext.Result = new HttpUnauthorizedResult();  
  15.             }  
  16.         }  
  17.   
  18.         void IAuthenticationFilter.OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)  
  19.         {  
  20.             if (filterContext.Result == null || filterContext.Result is HttpUnauthorizedResult)  
  21.             {  
  22.                 filterContext.Result = new RedirectToRouteResult(  
  23.                     new RouteValueDictionary {  
  24.                     { "controller""Account" },  
  25.                     { "action""Login" } });  
  26.             }  
  27.         }  
  28.     }  
  29. }  
 
Step 9
 
Open web config file and write following code.
  1. <authentication mode="Forms">  
  2.       <forms loginUrl="Account/Login"></forms>  
  3.     </authentication>  
  4.     <roleManager defaultProvider="userRoleProvider" enabled="true">  
  5.       <providers>  
  6.         <clear/>  
  7.         <add name="userRoleProvider" type="MvcRoleBasedAuthentication_Demo.Models.UserRoleProvider"/>  
  8.       </providers>  
  9.     </roleManager>  
Step 10
 
Right-click on the Login action method in Account Controller and create Login View.
  1. @model MvcCustomAuthenticationFilter_Demo.Models.UserLogin  
  2.    
  3. @{  
  4.     ViewBag.Title = "Login";  
  5. }  
  6.    
  7. @using (Html.BeginForm())  
  8. {  
  9.     @Html.AntiForgeryToken()  
  10.     <div class="panel custom-panel">  
  11.         <div class="panel-heading">  
  12.             <h3>Log in</h3>  
  13.         </div>  
  14.         <div class="panel-body">  
  15.             <div class="form-group">  
  16.                 @Html.LabelFor(m => m.Username)  
  17.                 @Html.TextBoxFor(m => m.Username, new { @class = "form-control" })  
  18.                 @Html.ValidationMessageFor(m => m.Username)  
  19.             </div>  
  20.             <div class="form-group">  
  21.                 @Html.LabelFor(m => m.Password)  
  22.                 @Html.PasswordFor(m => m.Password, new { @class = "form-control" })  
  23.                 @Html.ValidationMessageFor(m => m.Password)  
  24.             </div>  
  25.             <div class="form-group">  
  26.                 <button type="submit" class="btn btn-primary">Login</button>  
  27.             </div>  
  28.         </div>  
  29.     </div>  
  30. }  
Now, run the application and you will see that when you want to access the Index and Contact page, it will navigate to the Login page of Account Controller. But you can access the “About” page as I have not applied custom authentication filter on this action method. Now login with the proper credentials and once you log in, you can access each page of the application.