Role Based Custom Authorization In MVC 5

In this, Article we are going to learn how to implement role based custom authentication in MVC 5

Prerequisites

As in the previous article, we learn how to implement basic authentication.

This article will explain how to create your role base custom authorization. Custom authentication filter runs before any filter or action method. Custom authentication confirms is the user is valid or invalid.

Let us create an example.

In this example we allow users to access pages after login but on the basis of their roles.

First, open Visual Studio 2019 and create an MVC 5 application.

Create User class in a model folder and paste the below code.

public class User
{
   public int Id { get; set; }
   public string UserId { get; set; }
   public string UserName { get; set; }
   public string Password { get; set; }
   public int RoleId { get; set; }
}

Create Role class in a model folder and paste the below code.

public class Role
{
   public int Id { get; set; }
   public string Name { get; set; }
}

Create DbContext class now. This class will be used in the database migration process.

public class SqlDbContext : DbContext
{
    public SqlDbContext() : base("name=SQLConnection")
    {
    }
    public DbSet<User> Users { get; set; }
    public DbSet<Role> Roles { get; set; }
}

Add connection string in Web.config file

<connectionStrings>
   <add name="SQLConnection" connectionString="Data Source=your server name; Integrated Security=true;Initial Catalog=customDB;" providerName="System.Data.SqlClient" />
 </connectionStrings>
We can enable the DB migration first. Choose Package Manager Console from Tools -> NuGet Package Manager menu item.
enable-migrations
add-migration CustomAuth
update-database
Write the above command to create your Database.
Create Account Controller and paste the below code.
public class AccountController : Controller
   {
   [HttpGet]
   public ActionResult Login()
   {
     return View();
   }

   [HttpPost]
   public ActionResult Login(User model)
   {
     if (ModelState.IsValid)
     {
       using (var context = new SqlDbContext())
       {
         User user = context.Users
                            .Where(u => u.UserId == model.UserId && u.Password == model.Password)
                            .FirstOrDefault();

         if (user != null)
         {
           Session["UserName"] = user.UserName;
           Session["UserId"] = user.UserId;
           return RedirectToAction("Index", "Home");
         }
         else
         {
           ModelState.AddModelError("", "Invalid User Name or Password");
           return View(model);
         }
       }
     }
     else
     {
       return View(model);
     }
   }

   [HttpPost]
   [ValidateAntiForgeryToken]
   public ActionResult LogOff()
   {
     Session["UserName"] = string.Empty;
     Session["UserId"] = string.Empty;
     return RedirectToAction("Index", "Home");
   }
}

Create a Login view and add the below code.

@model CustomAuthenication.Models.User
@{
    ViewBag.Title = "Login";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Login</h2>
@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    
    <div class="form-horizontal">
        <h4>User</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.UserId, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.UserId, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.UserId, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Password, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Password, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Password, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Create a custom authentication filter in the helper folder and add the below code.

namespace CustomAuthenication.Helper
{
  public class CustomAuthenticationFilter : ActionFilterAttribute, IAuthenticationFilter
  {
    public void OnAuthentication(AuthenticationContext filterContext)
    {
      if (string.IsNullOrEmpty(Convert.ToString(filterContext.HttpContext.Session["UserName"])))
      {
        filterContext.Result = new HttpUnauthorizedResult();
      }
    }
    public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
    {
      if (filterContext.Result == null || filterContext.Result is HttpUnauthorizedResult)
      {
        filterContext.Result = new RedirectToRouteResult(
        new RouteValueDictionary
        {
                     { "controller", "Account" },
                     { "action", "Login" }
        });
      }
    }
  }
}

Create a custom authorize attribute filter in the helper folder and add the below code.

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    private readonly string[] allowedroles;
    public CustomAuthorizeAttribute(params string[] roles)
    {
      this.allowedroles = roles;
    }
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
      bool authorize = false;
      var userId = Convert.ToString(httpContext.Session["UserId"]);
      if (!string.IsNullOrEmpty(userId))
        using (var context = new SqlDbContext())
        {
          var userRole = (from u in context.Users
                          join r in context.Roles on u.RoleId equals r.Id
                          where u.UserId == userId
                          select new
                          {
                            r.Name
                          }).FirstOrDefault();
          foreach (var role in allowedroles)
          {
            if (role == userRole.Name) return true;
          }
        }


      return authorize;
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
      filterContext.Result = new RedirectToRouteResult(
         new RouteValueDictionary
         {
                    { "controller", "Home" },
                    { "action", "UnAuthorized" }
         });
    }
}

In Home controller add the below code.

[CustomAuthenticationFilter]
 public class HomeController : Controller
 {
   [CustomAuthorize("Admin")]
   public ActionResult Index()
   {
     return View();
   }
   [CustomAuthorize("Admin","SuperAdmin")]
   public ActionResult About()
   {
     ViewBag.Message = "Your application description page.";

     return View();
   }
   [CustomAuthorize("SuperAdmin")]
   public ActionResult Contact()
   {
     ViewBag.Message = "Your contact page.";

     return View();
   }
   public ActionResult UnAuthorized()
   {
     ViewBag.Message = "Un Authorized Page!";

     return View();
   }
 }

Create a partial view for the Logout link and login link.

@if (!string.IsNullOrEmpty(Convert.ToString(Session["UserName"])))
{
  using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" }))
  {
    @Html.AntiForgeryToken()
    <ul class="nav navbar-nav navbar-right">
      <li><a href="#">Welcome : @Session["UserName"]</a></li>
      <li><a href="javascript:document.getElementById('logoutForm').submit()">Logout</a></li>
    </ul>
  }
}
else
{
  <ul class="nav navbar-nav navbar-right">
    <li>@Html.ActionLink("Log In", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
  </ul>
}

Call this partial view from _Layout.cshtml.

<div class="navbar-collapse collapse">
   <ul class="nav navbar-nav">
      <li>@Html.ActionLink("Home", "Index", "Home")</li>
      <li>@Html.ActionLink("About", "About", "Home")</li>
      <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
   </ul>
    @Html.Partial("LoginPartial")
</div>

My tables contains the following data

As you can see in the above image super admin is Hafeezjaha and admin is shaikh, In our home controller we apply custom authorization in that admin can only access the index and about us page, if the admin clicks on the contact us page then get an unauthorized error similarly super admin can access about us and contact us page if super admin clicks on the index page then get an unauthorized error.

Output

Also check, Custom Authentication In .NET Core 5.0

Submit a Comment

Your email address will not be published. Required fields are marked *

Subscribe

Select Categories