ASP.NET MVC 5: Role Based Accessibility

Role based accessibility is another integral part of web development because it provides encapsulation for designated information accessibility to designated credentials. Microsoft MVC paradigm provides a very simple and effective mechanism to achieve role based accessibility. So, for today's discussion, I will be demonstrating role based accessibility using ASP.NET MVC 5 technology.

login
The following are some prerequisites before you proceed any further in this tutorial:

Prerequisites:

Before moving further, you should have knowledge of the following technologies: 
  1. ASP.NET MVC 5.
  2. ADO.NET.
  3. Entity Framework.
  4. OWIN.
  5. Claim Base Identity Model.
  6. C# programming.
  7. C# LINQ.

You can download the complete source code for this tutorial from here or you can follow the step by step discussion below. The sample code is developed in Microsoft Visual Studio 2013 Ultimate. I am using SQL Server 2008 as database.

Let's Begin now.

1. Firstly, you need to create a sample database with "Login" & "Role" tables, I am using the following scripts to generate my sample database. My database name is "RoleBaseAccessibility", below is the snippet for it:

  1. USE [RoleBaseAccessibility]    
  2.  GO    
  3.  /****** Object: ForeignKey [R_10]  Script Date: 04/30/2016 16:32:55 ******/    
  4.  IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[R_10]') AND parent_object_id = OBJECT_ID(N'[dbo].[Login]'))    
  5.  ALTER TABLE [dbo].[Login] DROP CONSTRAINT [R_10]    
  6.  GO    
  7.  /****** Object: StoredProcedure [dbo].[LoginByUsernamePassword]  Script Date: 04/30/2016 16:32:59 ******/    
  8.  IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[LoginByUsernamePassword]') AND type in (N'P', N'PC'))    
  9.  DROP PROCEDURE [dbo].[LoginByUsernamePassword]    
  10.  GO    
  11.  /****** Object: Table [dbo].[Login]  Script Date: 04/30/2016 16:32:55 ******/    
  12.  IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[R_10]') AND parent_object_id = OBJECT_ID(N'[dbo].[Login]'))    
  13.  ALTER TABLE [dbo].[Login] DROP CONSTRAINT [R_10]    
  14.  GO    
  15.  IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Login]') AND type in (N'U'))    
  16.  DROP TABLE [dbo].[Login]    
  17.  GO    
  18.  /****** Object: Table [dbo].[Role]  Script Date: 04/30/2016 16:32:55 ******/    
  19.  IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Role]') AND type in (N'U'))    
  20.  DROP TABLE [dbo].[Role]    
  21.  GO    
  22.  /****** Object: Table [dbo].[Role]  Script Date: 04/30/2016 16:32:55 ******/    
  23.  SET ANSI_NULLS ON    
  24.  GO    
  25.  SET QUOTED_IDENTIFIER ON    
  26.  GO    
  27.  IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Role]') AND type in (N'U'))    
  28.  BEGIN    
  29.  CREATE TABLE [dbo].[Role](    
  30.       [role_id] [int] IDENTITY(1,1) NOT NULL,    
  31.       [role] [nvarchar](max) NOT NULL,    
  32.   CONSTRAINT [PK_Role] PRIMARY KEY CLUSTERED     
  33.  (    
  34.       [role_id] ASC    
  35.  )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]    
  36.  ) ON [PRIMARY]    
  37.  END    
  38.  GO    
  39.  SET IDENTITY_INSERT [dbo].[Role] ON    
  40.  INSERT [dbo].[Role] ([role_id], [role]) VALUES (1, N'Admin')    
  41.  INSERT [dbo].[Role] ([role_id], [role]) VALUES (2, N'User')    
  42.  SET IDENTITY_INSERT [dbo].[Role] OFF    
  43.  /****** Object: Table [dbo].[Login]  Script Date: 04/30/2016 16:32:55 ******/    
  44.  SET ANSI_NULLS ON    
  45.  GO    
  46.  SET QUOTED_IDENTIFIER ON    
  47.  GO    
  48.  SET ANSI_PADDING ON    
  49.  GO    
  50.  IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Login]') AND type in (N'U'))    
  51.  BEGIN    
  52.  CREATE TABLE [dbo].[Login](    
  53.       [id] [int] IDENTITY(1,1) NOT NULL,    
  54.       [username] [varchar](50) NOT NULL,    
  55.       [password] [varchar](50) NOT NULL,    
  56.       [role_id] [int] NOT NULL,    
  57.   CONSTRAINT [PK_Login] PRIMARY KEY CLUSTERED     
  58.  (    
  59.       [id] ASC    
  60.  )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]    
  61.  ) ON [PRIMARY]    
  62.  END    
  63.  GO    
  64.  SET ANSI_PADDING OFF    
  65.  GO    
  66.  SET IDENTITY_INSERT [dbo].[Login] ON    
  67.  INSERT [dbo].[Login] ([id], [username], [password], [role_id]) VALUES (1, N'admin', N'admin', 1)    
  68.  INSERT [dbo].[Login] ([id], [username], [password], [role_id]) VALUES (2, N'user', N'user', 2)    
  69.  SET IDENTITY_INSERT [dbo].[Login] OFF    
  70.  /****** Object: StoredProcedure [dbo].[LoginByUsernamePassword]  Script Date: 04/30/2016 16:32:59 ******/    
  71.  SET ANSI_NULLS ON    
  72.  GO    
  73.  SET QUOTED_IDENTIFIER ON    
  74.  GO    
  75.  IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[LoginByUsernamePassword]') AND type in (N'P', N'PC'))    
  76.  BEGIN    
  77.  EXEC dbo.sp_executesql @statement = N'-- =============================================    
  78.  -- Author:          <Author,,Name>    
  79.  -- Create date: <Create Date,,>    
  80.  -- Description:     <Description,,>    
  81.  -- =============================================    
  82.  CREATE PROCEDURE [dbo].[LoginByUsernamePassword]     
  83.       @username varchar(50),    
  84.       @password varchar(50)    
  85.  AS    
  86.  BEGIN    
  87.       SELECT id, username, password, role_id    
  88.       FROM Login    
  89.       WHERE username = @username    
  90.       AND password = @password    
  91.  END    
  92.  '     
  93.  END    
  94.  GO    
  95.  /****** Object: ForeignKey [R_10]  Script Date: 04/30/2016 16:32:55 ******/    
  96.  IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[R_10]') AND parent_object_id = OBJECT_ID(N'[dbo].[Login]'))    
  97.  ALTER TABLE [dbo].[Login] WITH CHECK ADD CONSTRAINT [R_10] FOREIGN KEY([role_id])    
  98.  REFERENCES [dbo].[Role] ([role_id])    
  99.  ON UPDATE CASCADE    
  100.  ON DELETE CASCADE    
  101.  GO    
  102.  IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[R_10]') AND parent_object_id = OBJECT_ID(N'[dbo].[Login]'))    
  103.  ALTER TABLE [dbo].[Login] CHECK CONSTRAINT [R_10]    
  104.  GO     

Here I have created a simple login & role tables with sample data and a store procedure to retrieve the data.

2. Create new visual studio web MVC project and name it "RoleBaseAccessibility".

3. You need to create "ADO.NET" database connectivity. You can visit here for details.

4. You also need to create basic "Login" interface, I am not going to show you how you can create a basic login application by using Claim Base Identity Model. You can either download source code for this tutorial or you can go through detail tutorial here for better understanding.

5. Now, open "App_Start->Startup.Auth.cs" file and replace it with following code:

  1. using Microsoft.AspNet.Identity;    
  2.  using Microsoft.AspNet.Identity.EntityFramework;    
  3.  using Microsoft.AspNet.Identity.Owin;    
  4.  using Microsoft.Owin;    
  5.  using Microsoft.Owin.Security.Cookies;    
  6.  using Microsoft.Owin.Security.DataProtection;    
  7.  using Microsoft.Owin.Security.Google;    
  8.  using Owin;    
  9.  using System;    
  10.  using RoleBaseAccessibility.Models;    
  11.  namespace RoleBaseAccessibility    
  12.  {    
  13.    public partial class Startup    
  14.    {    
  15.      // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864    
  16.      public void ConfigureAuth(IAppBuilder app)    
  17.      {    
  18.        // Enable the application to use a cookie to store information for the signed in user    
  19.        // and to use a cookie to temporarily store information about a user logging in with a third party login provider    
  20.        // Configure the sign in cookie    
  21.        app.UseCookieAuthentication(new CookieAuthenticationOptions    
  22.        {    
  23.          AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,    
  24.          LoginPath = new PathString("/Account/Login"),    
  25.          LogoutPath = new PathString("/Account/LogOff"),    
  26.          ExpireTimeSpan = TimeSpan.FromMinutes(5.0),    
  27.          ReturnUrlParameter = "/Home/Index"    
  28.        });    
  29.        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);    
  30.        // Uncomment the following lines to enable logging in with third party login providers    
  31.        //app.UseMicrosoftAccountAuthentication(    
  32.        //  clientId: "",    
  33.        //  clientSecret: "");    
  34.        //app.UseTwitterAuthentication(    
  35.        //  consumerKey: "",    
  36.        //  consumerSecret: "");    
  37.        //app.UseFacebookAuthentication(    
  38.        //  appId: "",    
  39.        //  appSecret: "");    
  40.        //app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()    
  41.        //{    
  42.        //  ClientId = "",    
  43.        //  ClientSecret = ""    
  44.        //});    
  45.      }    
  46.    }    
  47.  }    

In above code following line of code will redirect the user to home page if he/she tries to access a link which is not authorized to him/her:

  1. ReturnUrlParameter = "/Home/Index"  

6. Create new controller, name it "AccountController.cs" under "Controller" folder and replace it with the following code:

  1. //-----------------------------------------------------------------------    
  2.  // <copyright file="AccountController.cs" company="None">    
  3.  //   Copyright (c) Allow to distribute this code.    
  4.  // </copyright>    
  5.  // <author>Asma Khalid</author>    
  6.  //-----------------------------------------------------------------------    
  7.  namespace RoleBaseAccessibility.Controllers    
  8.  {    
  9.    using System;    
  10.    using System.Collections.Generic;    
  11.    using System.Linq;    
  12.    using System.Security.Claims;    
  13.    using System.Web;    
  14.    using System.Web.Mvc;    
  15.    using Microsoft.AspNet.Identity;    
  16.    using Microsoft.Owin.Security;    
  17.    using RoleBaseAccessibility.Models;    
  18.    /// <summary>    
  19.    /// Account controller class.    
  20.    /// </summary>    
  21.    public class AccountController : Controller    
  22.    {    
  23.      #region Private Properties    
  24.      /// <summary>    
  25.      /// Database Store property.    
  26.      /// </summary>    
  27.      private RoleBaseAccessibilityEntities databaseManager = new RoleBaseAccessibilityEntities();    
  28.      #endregion    
  29.      #region Default Constructor    
  30.      /// <summary>    
  31.      /// Initializes a new instance of the <see cref="AccountController" /> class.    
  32.      /// </summary>    
  33.      public AccountController()    
  34.      {    
  35.      }    
  36.      #endregion    
  37.      #region Login methods    
  38.      /// <summary>    
  39.      /// GET: /Account/Login    
  40.      /// </summary>    
  41.      /// <param name="returnUrl">Return URL parameter</param>    
  42.      /// <returns>Return login view</returns>    
  43.      [AllowAnonymous]    
  44.      public ActionResult Login(string returnUrl)    
  45.      {    
  46.        try    
  47.        {    
  48.          // Verification.    
  49.          if (this.Request.IsAuthenticated)    
  50.          {    
  51.            // Info.    
  52.            return this.RedirectToLocal(returnUrl);    
  53.          }    
  54.        }    
  55.        catch (Exception ex)    
  56.        {    
  57.          // Info    
  58.          Console.Write(ex);    
  59.        }    
  60.        // Info.    
  61.        return this.View();    
  62.      }    
  63.      /// <summary>    
  64.      /// POST: /Account/Login    
  65.      /// </summary>    
  66.      /// <param name="model">Model parameter</param>    
  67.      /// <param name="returnUrl">Return URL parameter</param>    
  68.      /// <returns>Return login view</returns>    
  69.      [HttpPost]    
  70.      [AllowAnonymous]    
  71.      [ValidateAntiForgeryToken]    
  72.      public ActionResult Login(LoginViewModel model, string returnUrl)    
  73.      {    
  74.        try    
  75.        {    
  76.          // Verification.    
  77.          if (ModelState.IsValid)    
  78.          {    
  79.            // Initialization.    
  80.            var loginInfo = this.databaseManager.LoginByUsernamePassword(model.Username, model.Password).ToList();    
  81.            // Verification.    
  82.            if (loginInfo != null && loginInfo.Count() > 0)    
  83.            {    
  84.              // Initialization.    
  85.              var logindetails = loginInfo.First();    
  86.              // Login In.    
  87.              this.SignInUser(logindetails.username, logindetails.role_id, false);    
  88.              // setting.    
  89.              this.Session["role_id"] = logindetails.role_id;    
  90.              // Info.    
  91.              return this.RedirectToLocal(returnUrl);    
  92.            }    
  93.            else    
  94.            {    
  95.              // Setting.    
  96.              ModelState.AddModelError(string.Empty, "Invalid username or password.");    
  97.            }    
  98.          }    
  99.        }    
  100.        catch (Exception ex)    
  101.        {    
  102.          // Info    
  103.          Console.Write(ex);    
  104.        }    
  105.        // If we got this far, something failed, redisplay form    
  106.        return this.View(model);    
  107.      }    
  108.      #endregion    
  109.      #region Log Out method.    
  110.      /// <summary>    
  111.      /// POST: /Account/LogOff    
  112.      /// </summary>    
  113.      /// <returns>Return log off action</returns>    
  114.      [HttpPost]    
  115.      [ValidateAntiForgeryToken]    
  116.      public ActionResult LogOff()    
  117.      {    
  118.        try    
  119.        {    
  120.          // Setting.    
  121.          var ctx = Request.GetOwinContext();    
  122.          var authenticationManager = ctx.Authentication;    
  123.          // Sign Out.    
  124.          authenticationManager.SignOut();    
  125.        }    
  126.        catch (Exception ex)    
  127.        {    
  128.          // Info    
  129.          throw ex;    
  130.        }    
  131.        // Info.    
  132.        return this.RedirectToAction("Login""Account");    
  133.      }    
  134.      #endregion    
  135.      #region Helpers    
  136.      #region Sign In method.    
  137.      /// <summary>    
  138.      /// Sign In User method.    
  139.      /// </summary>    
  140.      /// <param name="username">Username parameter.</param>    
  141.      /// <param name="role_id">Role ID parameter</param>    
  142.      /// <param name="isPersistent">Is persistent parameter.</param>    
  143.      private void SignInUser(string username, int role_id, bool isPersistent)    
  144.      {    
  145.        // Initialization.    
  146.        var claims = new List<Claim>();    
  147.        try    
  148.        {    
  149.          // Setting    
  150.          claims.Add(new Claim(ClaimTypes.Name, username));    
  151.          claims.Add(new Claim(ClaimTypes.Role, role_id.ToString()));    
  152.          var claimIdenties = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);    
  153.          var ctx = Request.GetOwinContext();    
  154.          var authenticationManager = ctx.Authentication;    
  155.          // Sign In.    
  156.          authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, claimIdenties);    
  157.        }    
  158.        catch (Exception ex)    
  159.        {    
  160.          // Info    
  161.          throw ex;    
  162.        }    
  163.      }    
  164.      #endregion    
  165.      #region Redirect to local method.    
  166.      /// <summary>    
  167.      /// Redirect to local method.    
  168.      /// </summary>    
  169.      /// <param name="returnUrl">Return URL parameter.</param>    
  170.      /// <returns>Return redirection action</returns>    
  171.      private ActionResult RedirectToLocal(string returnUrl)    
  172.      {    
  173.        try    
  174.        {    
  175.          // Verification.    
  176.          if (Url.IsLocalUrl(returnUrl))    
  177.          {    
  178.            // Info.    
  179.            return this.Redirect(returnUrl);    
  180.          }    
  181.        }    
  182.        catch (Exception ex)    
  183.        {    
  184.          // Info    
  185.          throw ex;    
  186.        }    
  187.        // Info.    
  188.        return this.RedirectToAction("Index""Home");    
  189.      }    
  190.      #endregion    
  191.      #endregion    
  192.    }    
  193.  }   

In above code, the following piece of code is important i.e.

  1. #region Sign In method.    
  2.      /// <summary>    
  3.      /// Sign In User method.    
  4.      /// </summary>    
  5.      /// <param name="username">Username parameter.</param>    
  6.      /// <param name="role_id">Role ID parameter</param>    
  7.      /// <param name="isPersistent">Is persistent parameter.</param>    
  8.      private void SignInUser(string username, int role_id, bool isPersistent)    
  9.      {    
  10.        // Initialization.    
  11.        var claims = new List<Claim>();    
  12.        try    
  13.        {    
  14.          // Setting    
  15.          claims.Add(new Claim(ClaimTypes.Name, username));    
  16.          claims.Add(new Claim(ClaimTypes.Role, role_id.ToString()));    
  17.          var claimIdenties = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);    
  18.          var ctx = Request.GetOwinContext();    
  19.          var authenticationManager = ctx.Authentication;    
  20.          // Sign In.    
  21.          authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, claimIdenties);    
  22.        }    
  23.        catch (Exception ex)    
  24.        {    
  25.          // Info    
  26.          throw ex;    
  27.        }    
  28.      }    
  29.      #endregion     

Here, along with claiming "Username" in OWIN security layer, we are also claiming "role_id" to provide role base accessibility:

  1. claims.Add(new Claim(ClaimTypes.Role, role_id.ToString()));   

7. Now, create new controller under "Controller" folder, name it "HomeController.cs" and replace it with the following code i.e.

  1. // <copyright file="HomeController.cs" company="None">    
  2.  //   Copyright (c) Allow to distribute this code.    
  3.  // </copyright>    
  4.  // <author>Asma Khalid</author>    
  5.  //-----------------------------------------------------------------------    
  6.  namespace RoleBaseAccessibility.Controllers    
  7.  {    
  8.    using System;    
  9.    using System.Collections.Generic;    
  10.    using System.Linq;    
  11.    using System.Web;    
  12.    using System.Web.Mvc;    
  13.    /// <summary>    
  14.    /// Home controller class.    
  15.    /// </summary>    
  16.    [Authorize]    
  17.    public class HomeController : Controller    
  18.    {    
  19.      #region Index method.    
  20.      /// <summary>    
  21.      /// Index method.    
  22.      /// </summary>    
  23.      /// <returns>Returns - Index view</returns>    
  24.      public ActionResult Index()    
  25.      {    
  26.        return this.View();    
  27.      }    
  28.      #endregion    
  29.      #region Admin Only Link    
  30.      /// <summary>    
  31.      /// Admin only link method.    
  32.      /// </summary>    
  33.      /// <returns>Returns - Admin only link view</returns>    
  34.      [Authorize(Roles = "1")]    
  35.      public ActionResult AdminOnlyLink()    
  36.      {    
  37.        return this.View();    
  38.      }    
  39.      #endregion    
  40.    }    
  41.  }     

In above code, we have simply created two views, one is accessible to all the user which is "Index" view and second method is accessible to only "Admin" role user with role_id = 1. Following piece of code will translate the role base accessibility in OWIN security layer i.e.

  1. [Authorize(Roles = "1")]   

If you want to define role accessibility for multiple roles you can achieve it like following:

  1. [Authorize(Roles = "1, 2, 3")]   

where, 2, 3 are role id(s).

8. Replace the following code in "_LoginPartial.cshtml" file:

  1. @*@using Microsoft.AspNet.Identity*@    
  2.  @if (Request.IsAuthenticated)    
  3.  {    
  4.    using (Html.BeginForm("LogOff""Account", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" }))    
  5.    {    
  6.      @Html.AntiForgeryToken()    
  7.      <ul class="nav navbar-nav navbar-right">    
  8.        @if (Convert.ToInt32(this.Session["role_id"]) == 1)    
  9.        {     
  10.          <li>    
  11.            @Html.ActionLink("Admin Only Link""AdminOnlyLink""Home")    
  12.          </li>    
  13.        }    
  14.        <li>    
  15.          @Html.ActionLink("Hello " + User.Identity.Name + "!""Index""Home", routeValues: null, htmlAttributes: new { title = "Manage" })    
  16.        </li>    
  17.        <li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>    
  18.      </ul>    
  19.    }    
  20.  }    
  21.  else    
  22.  {    
  23.    <ul class="nav navbar-nav navbar-right">    
  24.      <li>@Html.ActionLink("Log in""Login""Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>    
  25.    </ul>    
  26.  }    

9. Execute the project and you will see the following:

Execute
10. When you login as admin account you will able to see a link that only admin can see as follow:

login
login
11. When you login as non-admin account you won't see the link that admin can see but, if you try to open the link which supposedly you do not have access of, you will be redirected to your home page as follow:

output
output
Conclusion

In this tutorial you learned how to use create role based accessibility. You also learned how you can encapsulate role based accessibility for multiple roles.

Read more articles on ASP.NET: