Authentication Filter In MVC With An Example

In this post, we will see what authentication filter is and how to create an MVC application with custom authentication filter.

Introduction

 
ASP.NET MVC filters are used to add extra logic at the different levels of MVC Framework request processing. Authentication Filter runs before any other filter or action method. Authentication confirms if you are a valid or invalid user. These filters implement the “IAuthenticationFilter” interface and “ActionFilterAttribute” base class.
 

Create an MVC project in Visual Studio

 
We can create an MVC application in Visual Studio. I am using the latest Visual Studio 2019. You may use any version of the IDE.
Choose ASP.NET Web Application template and select MVC option.
 
Authentication Filter in MVC with an Example
 
In this application, we will check the user authentication before every request execution. Hence, we need a database and a “User” table inside the database. We will validate the user information before every request. We will use Entity Framework to connect with SQL database. We will create the database and table using the code-first approach and data migration process.
 
We can install EntityFramework NuGet package in our project.
 
Authentication Filter in MVC with an Example 
 
Create a “User” model inside the “Models” folder now.
 
User.cs
  1. using System.ComponentModel.DataAnnotations;  
  2.   
  3. namespace CustomAuthenticationFilter.Models  
  4. {  
  5.     public class User  
  6.     {  
  7.         public int Id { getset; }  
  8.         [Display(Name = "User Id")]  
  9.         public string UserId { getset; }  
  10.         public string UserName { getset; }  
  11.         public string Password { getset; }  
  12.     }  
  13. }  
Create a DbContext class also.
 
SqlDbContext.cs
  1. using System.Data.Entity;    
  2.     
  3. namespace CustomAuthenticationFilter.Models    
  4. {    
  5.     public class SqlDbContext : DbContext    
  6.     {    
  7.         public SqlDbContext() : base("name=SqlConnection")    
  8.         {    
  9.         }    
  10.         public DbSet<User> Users { getset; }    
  11.     }    
  12. }    
We have used a connection string “SqlConnection” inside the above DbContext class. We can create connection string inside the Web.Config also.
 
Authentication Filter in MVC with an Example
  1. <connectionStrings>    
  2.     <add name="SqlConnection"    
  3.          connectionString="Data Source=Murugan; Initial Catalog=SarathlalDB; Integrated Security=SSPI"    
  4.          providerName="System.Data.SqlClient"    
  5.          />    
  6.   </connectionStrings>    
We can create SQL database and table using database migration process. We can enable the DB migration first. Choose “Package Manager Console” from “Tools -> NuGet Package Manager” menu item.
 
Authentication Filter in MVC with an Example
 
Use the below command to enable the migration.
 
“enable-migrations”
 
The above command will generate a “Configuration.cs” file inside the “Migration” folder.
 
We can use the below command to add new migration.
 
“add-migration Initial”
 
The above command will create a new migration file suffix with “_Initial” and timestamp inside the “Migrations” folder.
 
We can use “Sql” command to insert a default record (Seed data) to User table while migration happens.
 
Authentication Filter in MVC with an Example
  1. public partial class Initial : DbMigration  
  2.    {  
  3.        public override void Up()  
  4.        {  
  5.            CreateTable(  
  6.                "dbo.Users",  
  7.                c => new  
  8.                    {  
  9.                        Id = c.Int(nullable: false, identity: true),  
  10.                        UserId = c.String(),  
  11.                        UserName = c.String(),  
  12.                        Password = c.String(),  
  13.                    })  
  14.                .PrimaryKey(t => t.Id);  
  15.   
  16.            Sql("Insert into Users (UserId,UserName,Password) Values ('sarath','Sarath Lal','password123')");  
  17.        }  
  18.          
  19.        public override void Down()  
  20.        {  
  21.            DropTable("dbo.Users");  
  22.        }  
  23.    }  
The above SQL command will insert one record to the User table during migration process.
 
We can use the below command in Package Manage Console to create a database and table.
 
“update-database”
 
You can see in the SQL server that a new database and table are created with a default record.
 
We can create an “Account” controller inside the “Controller” folder to control the login process.
 
Copy the below code and paste inside the controller class.
 
AccountController.cs
  1. using CustomAuthenticationFilter.Models;  
  2. using System.Linq;  
  3. using System.Web.Mvc;  
  4.   
  5. namespace CustomAuthenticationFilter.Controllers  
  6. {  
  7.     public class AccountController : Controller  
  8.     {  
  9.         [HttpGet]  
  10.         public ActionResult Login()  
  11.         {  
  12.             return View();  
  13.         }  
  14.   
  15.         [HttpPost]  
  16.         public ActionResult Login(User model)  
  17.         {  
  18.             if (ModelState.IsValid)  
  19.             {  
  20.                 using (var context = new SqlDbContext())  
  21.                 {  
  22.                     User user = context.Users  
  23.                                        .Where(u => u.UserId == model.UserId && u.Password == model.Password)  
  24.                                        .FirstOrDefault();  
  25.   
  26.                     if (user != null)  
  27.                     {  
  28.                         Session["UserName"] = user.UserName;  
  29.                         return RedirectToAction("Index""Home");  
  30.                     }  
  31.                     else  
  32.                     {  
  33.                         ModelState.AddModelError("""Invalid User Name or Password");  
  34.                         return View(model);  
  35.                     }  
  36.                 }  
  37.             }  
  38.             else  
  39.             {  
  40.                 return View(model);  
  41.             }  
  42.         }  
  43.   
  44.         [HttpPost]  
  45.         [ValidateAntiForgeryToken]  
  46.         public ActionResult LogOff()  
  47.         {  
  48.             Session["UserName"] = string.Empty;  
  49.             return RedirectToAction("Index""Home");  
  50.         }  
  51.     }  
  52. }  
We have checked the user id and password in the database for authentication purposes. Also, we created a logout action to delete the user name stored in a session storage in login action.
 
We can create a Login view and add the below code.
 
Login.cshtml
  1. @model CustomAuthenticationFilter.Models.User  
  2.   
  3. @{  
  4.     ViewBag.Title = "Login";  
  5. }  
  6. <h2>Login</h2>  
  7. @using (Html.BeginForm())  
  8. {  
  9.     @Html.AntiForgeryToken()  
  10.   
  11.     <div class="form-horizontal">  
  12.   
  13.         @Html.ValidationSummary(true""new { @class = "text-danger" })  
  14.         <div class="form-group">  
  15.             @Html.LabelFor(model => model.UserId, htmlAttributes: new { @class = "control-label col-md-2" })  
  16.             <div class="col-md-10">  
  17.                 @Html.TextBoxFor(model => model.UserId, new { htmlAttributes = new { @class = "form-control" } })  
  18.                 @Html.ValidationMessageFor(model => model.UserId, ""new { @class = "text-danger" })  
  19.             </div>  
  20.         </div>  
  21.         <div class="form-group">  
  22.             @Html.LabelFor(model => model.Password, htmlAttributes: new { @class = "control-label col-md-2" })  
  23.             <div class="col-md-10">  
  24.                 @Html.PasswordFor(model => model.Password, new { htmlAttributes = new { @class = "form-control" } })  
  25.                 @Html.ValidationMessageFor(model => model.Password, ""new { @class = "text-danger" })  
  26.             </div>  
  27.         </div>  
  28.         <div class="form-group">  
  29.             <div class="col-md-offset-2 col-md-10">  
  30.                 <input type="submit" value="Proceed" class="btn btn-default" />  
  31.             </div>  
  32.         </div>  
  33.     </div>  
  34. }  
  35. <div>  
  36.     @Html.ActionLink("Back to List""Index")  
  37. </div>  
  38. <script src="~/Scripts/jquery-1.10.2.min.js"></script>  
  39. <script src="~/Scripts/jquery.validate.min.js"></script>  
  40. <script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>  

Create custom authentication filter

 
We can create an important portion in our project, custom authentication filter. Create a new “Infrastructure” folder and create “CustomAuthFilter.cs” class inside it.
 
CustomAuthFilter.cs
  1. using System;  
  2. using System.Web.Mvc;  
  3. using System.Web.Mvc.Filters;  
  4. using System.Web.Routing;  
  5.   
  6. namespace CustomAuthenticationFilter.Infrastructure  
  7. {  
  8.     public class CustomAuthFilter : ActionFilterAttribute, IAuthenticationFilter  
  9.     {  
  10.         public void OnAuthentication(AuthenticationContext filterContext)  
  11.         {  
  12.             if (string.IsNullOrEmpty(Convert.ToString(filterContext.HttpContext.Session["UserName"])))  
  13.             {  
  14.                 filterContext.Result = new HttpUnauthorizedResult();  
  15.             }  
  16.         }  
  17.         public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)  
  18.         {  
  19.             if (filterContext.Result == null || filterContext.Result is HttpUnauthorizedResult)  
  20.             {  
  21.                 //Redirecting the user to the Login View of Account Controller  
  22.                 filterContext.Result = new RedirectToRouteResult(  
  23.                 new RouteValueDictionary  
  24.                 {  
  25.                      { "controller""Account" },  
  26.                      { "action""Login" }  
  27.                 });  
  28.             }  
  29.         }  
  30.     }  
  31. }  
We have implemented “ActionFilterAttribute” class and “IAuthenticationFilter” interface in the above class. Please note that there are two important methods implemented - “OnAuthentication” and “OnAuthenticationChallenge”. Inside OnAuthentication, we have checked the session value “UserName” to see if it is empty or not. If the value is empty, it will throw the result as “HttpUnauthorizedResult” and then, the second method OnAuthenticationChallenge will be executed. This method will redirect the request to a specific action and controller. In this example, we will redirect to “Login” action in “Account” controller.
 
Now, we can decorate this filter inside the “Home” controller.
 
HomeController.cs
  1. using CustomAuthenticationFilter.Infrastructure;  
  2. using System.Web.Mvc;  
  3.   
  4. namespace CustomAuthenticationFilter.Controllers  
  5. {  
  6.     [CustomAuthFilter]  
  7.     public class HomeController : Controller  
  8.     {  
  9.         public ActionResult Index()  
  10.         {  
  11.             return View();  
  12.         }  
  13.   
  14.         public ActionResult About()  
  15.         {  
  16.             ViewBag.Message = "Your application description page.";  
  17.   
  18.             return View();  
  19.         }  
  20.   
  21.         public ActionResult Contact()  
  22.         {  
  23.             ViewBag.Message = "Your contact page.";  
  24.   
  25.             return View();  
  26.         }  
  27.     }  
  28. }  
We can add a partial view to show the username in the right-top corner along with logout link. This will show only after a successful login. If the user is not yet logged in, this will show as a simple login link. We can add the logic for that. We will create this partial view inside the shared views folder.
 
_LoginPartial.cshtml
  1. @if (!string.IsNullOrEmpty(Convert.ToString(Session["UserName"])))  
  2. {  
  3.     using (Html.BeginForm("LogOff""Account", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" }))  
  4.     {  
  5.         @Html.AntiForgeryToken()  
  6.         <ul class="nav navbar-nav navbar-right">  
  7.             <li><a href="#">Welcome : @Session["UserName"]</a></li>  
  8.             <li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>  
  9.         </ul>  
  10.     }  
  11. }  
  12. else  
  13. {  
  14.     <ul class="nav navbar-nav navbar-right">  
  15.         <li>@Html.ActionLink("Log in""Login""Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>  
  16.     </ul>  
  17. }  
We can call this partial view from _Layout.cshtml.
  1. <div class="navbar-collapse collapse">  
  2.                <ul class="nav navbar-nav">  
  3.                    <li>@Html.ActionLink("Home""Index""Home")</li>  
  4.                    <li>@Html.ActionLink("About""About""Home")</li>  
  5.                    <li>@Html.ActionLink("Contact""Contact""Home")</li>  
  6.                </ul>  
  7.                @Html.Partial("_LoginPartial")  
  8.            </div>  
We have completed the entire coding part. We can run the application now. Please note that we have added authentication filter attribute to the entire home controller. Hence, though the index action in home controller is the default route, it will be redirected to login action automatically.
 
Authentication Filter in MVC with an Example
You can also notice a login link in the right top corner of the page.
 
Authentication Filter in MVC with an Example
Once the user enters the correct user id and password, we can navigate to other pages as well. You can see the username also visible on the right-top corner now.
 
Authentication Filter in MVC with an Example
 

Conclusion

 
In this post, we have created an MVC application in Visual Studio and created a custom authentication filter and used this filter attribute to control the other actions like index, about and contact in the home controller. We have also seen how to create a database and table from the code-first approach using db migration process.