Click here to Skip to main content
14,934,046 members
Articles / Programming Languages / C#
Article
Posted 4 May 2020

Stats

7.7K views
8 bookmarked

Custom Authorization with User Groups using ASP.NET MVC

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
4 May 2020CPOL4 min read
An overview of custom authorization with user groups in MVC
This article explains about the implementation of custom authorization with the user roles using ASP.NET MVC. It also includes a demo part where I have explained how to allow users to access only specific pages based on their user roles.

Introduction

This article explains about the implementation of custom authorization with user roles using ASP.NET MVC. It prevents unauthorized users to access the pages which they are not supposed to be accessed.

Sample MVC Application for Demonstration

In this demo application, I’m using Visual Studio 2019. I am going to show you how users are disallowed to access the pages which they are not supposed to access. We are using Windows Authentication and users enrolled into a specific user group (Admin/NormalUser) can only be authorized to access the respective pages. For example, user belonging to Admin group can only access the Admin page whereas the user belongs to NormalUser group can only access the NormalUser page.

Step 1: Creating New User Roles in System

Create an Admin Group and Map the User

Launch the Computer Management window. Create a new user group “Admin” and map the windows user to the created group as shown in the below figure:

Image 1

Create a User Group and Map the User

Launch the Computer Management window. Create a new user group “User” and map the windows user to the created group as shown in the below figure:

Image 2

Image 3

Step 2: Creating a Project

In Visual Studio, create a new ASP.NET MVC Web Application (C#) project as shown in the following figure:

Image 4

Image 5

Image 6

Step 3: Configure the UserGroups in Web.config

Configure the newly created groups in config file as shown below:

XML
<appSettings>
   <add key="AdminUserRoles" value="Admin" />
   <add key="UserRoles" value="NormalUser" />
 </appSettings>

Step 4: Adding Enum Class

Add Enum class “Enums.cs” under the project and add the enum constants “UserRoles” as shown below:

C#
namespace CustomAuthorizationWithMVC
{
    public static class Enums
    {
        public enum UserGroups
        {
            /// <summary>
            /// No roles assigned
            /// </summary>
            None = 0,

            /// <summary>
            /// Admin role.
            /// </summary>
            Admin = 1,

            /// <summary>
            /// User role.
            /// </summary>
            NormalUser = 2,

            /// <summary>
            /// Both Administrator and  Normal user roles.
            /// </summary>
            All = 3
        }
    }
}

Step 5: Adding Login View Model

The following view model class helps the user to store the details of the Logged-in user.

C#
using static CustomAuthorizationWithMVC.Enums;

namespace CustomAuthorizationWithMVC.Models
{
    public class LogInViewModel
    {
        /// <summary>
        /// Gets or sets the LoggedInUser once Authenticated.
        /// </summary>
        public string LoggedInUser { get; set; }

        /// <summary>
        /// Gets or sets the Admin flag true/false.
        /// </summary>
        public bool IsAdmin { get; set; }

        /// <summary>
        /// Gets or sets the User Role Type.
        /// </summary>
        public UserGroups UserGroup { get; set; }
    }
}

Step 6: Adding Authorization Filter

Add the class file “UserAuthorizeAttribute.cs” under the project and add the below code inside the class file. This class inherits from “AuthorizeAttribute” and overrides the method “OnAuthorization” which validates the logged-in groups/roles with the one configured in the web.Config. If user is not enrolled in either of these groups/roles, then the user will be redirected to log-in page without getting access to the requested page.

C#
namespace UI.UserAuthorize
{
    using System;
    using System.Configuration;
    using System.Diagnostics;
    using System.Web.Mvc;
    using System.Web.Routing;
    using static CustomAuthorizationWithMVC.Enums;
    using AuthorizeUser.Common;

    public sealed class UserAuthorizeAttribute : AuthorizeAttribute
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="UserAuthorizeAttribute" /> class.
        /// </summary>
        /// <param name="allowedGroupTypes">allowed group types</param>
        public UserAuthorizeAttribute(params UserGroups[] userRoleTypes)
        {
            this.AllowedUserRoleTypes = userRoleTypes;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the admin user roles from configuration file.
        /// </summary>
        private string AdminUserRoles { get; } = 
                ConfigurationManager.AppSettings["AdminUserRoles"];

        /// <summary>
        /// Gets the user roles from configuration file.
        /// </summary>
        private string UserRoles { get; } = ConfigurationManager.AppSettings["UserRoles"];

        /// <summary>
        /// Gets or sets the allowed role types retrieved from controller or action method.
        /// </summary>
        private UserGroups[] AllowedUserRoleTypes { get; set; }
    
        #endregion

        #region Public Methods

        /// <summary>
        /// Method to do the authorization for user.
        /// </summary>
        /// <param name="filterContext">authorization context.</param>
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext != null)
            {
                var user = filterContext.RequestContext.HttpContext.User;

                base.OnAuthorization(filterContext);
               
                ////If user does not have any access, redirect him to login page.
                if (!AuthorizeUser.IsAdmin(AllowedUserRoleTypes, AdminUserRoles, user) 
                    && !AuthorizeUser.IsUser(AllowedUserRoleTypes, UserRoles, user))
                {                    
                    filterContext.Result = new RedirectToRouteResult(new
                        RouteValueDictionary(new { controller = "LogIn", action = "LogIn" }));
                }               
            }
        }
        #endregion        
    }
}

Step 7: Adding Authorization Helper Class

This class methods are used by Authorization filter class “UserAuthorizeAttribute” to check whether the logged-in user belongs to configured Admin group or NormalUser group.

C#
namespace AuthorizeUser.Common
{
    using System.Linq;
    using System.Security.Principal;
    using System.Text.RegularExpressions;
    using static CustomAuthorizationWithMVC.Enums;

    /// <summary>
    /// Class to check user group
    /// </summary>
    public static class AuthorizeUser
    {
        /// <summary>
        /// Method to check whether user is admin user
        /// </summary>
        /// <param name="allowedAuditUserGroupTypes">allowed audit user group types</param>
        /// <param name="admUserGroups">admin user groups</param>
        /// <param name="user">user</param>
        /// <returns>true or false</returns>
        public static bool IsAdmin(UserGroups[] allowedAuditUserGroupTypes,
               string auditAdminUserGroups, IPrincipal user)
        {
            bool isAdmin = false;
            var adminUserGroups = 
                Regex.Replace(auditAdminUserGroups, @"\s", string.Empty).Split(',');

            //If allowed group is configured for Administrator.
            if (allowedAuditUserGroupTypes.Any
               (allowedGroupType => allowedGroupType == UserGroups.Admin))
            {
                isAdmin = adminUserGroups.Any(admGrp => user.IsInRole(admGrp));
            }
            return isAdmin;
        }

        /// <summary>
        ///  Method to check whether user is audit user
        /// </summary>
        /// <param name="allowedAuditUserGroupTypes">allowed audit user group types</param>
        /// <param name="admUserGroups">admin user groups</param>
        /// <param name="user">user</param>
        /// <returns>true or false</returns>
        public static bool IsUser(UserGroups[] allowedAuditUserGroupTypes, 
             string auditUserGroups, IPrincipal user)
        {
            bool isUser = false;
            var userGroups = Regex.Replace(auditUserGroups, @"\s", string.Empty).Split(',');
            ////If allowed group is configured for Normal user.
            if (allowedAuditUserGroupTypes.Any
               (allowedGroupType => allowedGroupType == UserGroups.NormalUser))
            {
                isUser = userGroups.Any(usrGrp => user.IsInRole(usrGrp));
            }
            return isUser;
        }
    }
}

Step 8: Adding MVC Controllers

Add the following controller classes under Controllers folder in the project:

  • LogInController.cs
  • AdminController.cs
  • UserController.cs

LogInController.cs

The following controller class helps user to navigate to Login view and enable/disable the Login buttons based on the user roles. Admin login button functionality is to navigate to Admin page and NormalUser log in button is for navigating user to NormalUser page.

C#
using CustomAuthorizationWithMVC.Models;
using System.Configuration;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web.Mvc;
using static CustomAuthorizationWithMVC.Enums;

namespace CustomAuthorizationWithMVC.Controllers
{
    public class LogInController : Controller
    {        
        /// <summary>
        /// Gets the admin user groups from config file.
        /// </summary>
        private string AdminUserRoles { get; } = 
                ConfigurationManager.AppSettings["AdminUserRoles"];

        /// <summary>
        /// Gets the user groups from config file.
        /// </summary>
        private string UserRoles { get; } = ConfigurationManager.AppSettings["UserRoles"];

        // GET: LogIn
        public ActionResult LogIn()
        {
            // Check the user is enrolled into any of the Admin user roles.
            bool isAdmin = Regex.Replace(this.AdminUserRoles, @"\s", string.Empty).Split(',')
                            .Any(admRole => User.IsInRole(admRole));

            // Check the user is enrolled into any of the normal user roles.
            bool isUser = Regex.Replace(this.UserRoles, @"\s", string.Empty).Split(',')
                            .Any(usrRole => User.IsInRole(usrRole));

            LogInViewModel logInViewModel = new LogInViewModel()
            {
                LoggedInUser = User.Identity.Name,                
                UserGroup = this.GetUserRole(isAdmin, isUser)
            };
            return View(logInViewModel);
        }

        public ActionResult AdminView()
        {
            return this.RedirectToAction("RenderAdminView", "Admin");           
        }

        public ActionResult UserView()
        {
            return this.RedirectToAction("RenderUserView", "User");
        }

        private bool IsUserInGroup(string groupName)
        {
            return User.IsInRole(groupName);
        }
        private UserGroups GetUserRole(bool isAdmin, bool isUser)
        {
            if (isAdmin && isUser)
            {
                return Enums.UserGroups.All;
            }

            if (isAdmin)
            {
                return Enums.UserGroups.Admin;
            }

            if (isUser)
            {
                return Enums.UserGroups.NormalUser;
            }

            return Enums.UserGroups.None;
        }
    }
}

AdminController.cs

The following controller class helps the user to navigate to Admin page if he/she had enrolled into Admin role. By making the Authorization filter “UserAuthorize” annotation before the “AdminController” class (as highlighted bold in the below code) will validate the logged-in user against the configured Admin roles in web.cofig. If validation passed, it will execute the “RenderAdminView” method and Navigate to Admin page. Else, it redirects to LogIn page.

C#
using System.Web.Mvc;
using UI.UserAuthorize;
using static CustomAuthorizationWithMVC.Enums;

namespace CustomAuthorizationWithMVC.Controllers
{
    [UserAuthorize(UserGroups.Admin)]
    public class AdminController : Controller
    {
        
       public ActionResult RenderAdminView()
       {
            return View("Admin");
       }
    }
}

UserController.cs

The following controller class helps the user to navigate to User page if he/she has enrolled into NormalUser role. By making the Authorization filter “UserAuthorize” annotation before the “UserController” class (as highlighted bold in the below code) will validate the logged-in user against the configured user roles in web.config. If validation passed, it will execute the “RenderUserView” method and Navigate to User page. Else, it redirects to Login page.

C#
using System.Web.Mvc;
using UI.UserAuthorize;
using static CustomAuthorizationWithMVC.Enums;

namespace CustomAuthorizationWithMVC.Controllers
{
    [UserAuthorize(UserGroups.NormalUser)]
    public class UserController : Controller
    {
        public ActionResult RenderUserView()
        {
            return View("User");
        }
    }
}

Step 9: Adding MVC Views

Add the following views under Views folder in the project:

  • LogIn.cshtml
  • Admin.cshtml
  • User.cshtml

LogIn.cshtml

In this view, we received the “LogInViewModel” as model and validate the Logged-in user group against the enum constants. If logged-in user group is “Admin”, then “Login as Administrator” button is enabled and if logged-in user group is “NormalUser”, then the “Login as NormalUser” button is enabled.

ASP.NET
@model CustomAuthorizationWithMVC.Models.LogInViewModel
@{
    ViewBag.Title = "LogIn";
}

<h2>LogIn Page.</h2>
<div>
    @if (Model.UserGroup == Enums.UserGroups.Admin || Model.UserGroup == Enums.UserGroups.All)
    {
        <button id="btnAdministrator" 
        onclick="location.href = '@Url.Action("AdminView", "LogIn")'"
                class="btn btn-primary">Login as Administrator</button>
    }
    @if (Model.UserGroup == Enums.UserGroups.NormalUser || 
         Model.UserGroup == Enums.UserGroups.All)
    {
        <button id="btnAuditUser" 
        onclick="location.href = '@Url.Action("UserView", "LogIn")'" 
                class="btn btn-primary">Login as NormalUser</button>
    }
    @if (Model.UserGroup == Enums.UserGroups.None)
    {
        <button id="btnClose" onclick="window.close();" 
        class="btn btn-primary">Close</button>
    }
</div>

Admin.cshtml

This view will display the message as “Admin Page”. When Admin navigates to this view:

ASP.NET
@{
    ViewBag.Title = "Admin";
}

<h2>Admin Page.</h2>

User.cshtml

This view will display the message as “User Page”. When User navigates to this view:

ASP.NET
@{
    ViewBag.Title = "Admin";
}

<h2>NormalUser Page.</h2>

Step 10: Run the Application

I have enrolled only for “NormalUser” group. Hence, I can view only the “Login as Normal User” button in Login page when I launch it as shown below:

Image 7

And able to navigate to Normal User Page on click of Login button.

Image 8

Being a user of Normal Group, I may still try to access the Admin page by typing the URL in the browser. This is the scenario where the logged-in user is validated by authorization filter and will redirect me back to Login page as I’m the unauthorized user for Admin page as shown below:

Image 9

Image 10

History

  • 3rd May, 2020 - Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ayyappan Ramachandran
Team Leader
India India
Having 14+ years experience in IT industry and worked in technologies such as ASP.Net, MVC, Web API and client side scripting such as JavaScript, Jquery and Angular.

Comments and Discussions

 
QuestionI can't find windows login implementation Pin
Isaac Rabin5-May-20 18:46
MemberIsaac Rabin5-May-20 18:46 
AnswerRe: I can't find windows login implementation Pin
Ayyappan Ramachandran5-May-20 21:12
professionalAyyappan Ramachandran5-May-20 21:12 
GeneralRe: I can't find windows login implementation Pin
Ayyappan Ramachandran5-May-20 21:14
professionalAyyappan Ramachandran5-May-20 21:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.