Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

MVC Dynamic Authorization

0.00/5 (No votes)
14 Jul 2013 1  
A simple way to implement Dynamic Authorization with the ability to assign permissions for actions to roles or users.

Introduction

In MVC the default method to perform authorization is hard coding the "Authorize" attribute in the controllers, for each action, in this article I will explain a simple way to implement "Dynamic Authorization" with the ability to assign permissions for actions to roles or users.  

Using the code 

First I will explain my user authentication and role assigning model, I have used Forms Authentication this scenario, here is my sample login action: 

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
    //sample data
    Dictionary<string, string> users = new Dictionary<string, string>();
    users.Add("admin", "admin-pass");
    
    string roles;

    if (users[model.UserName] == model.Password)
    {
        Session["User"] = model.UserName;
        roles = "admin;customer";                
        // put the roles of the user in the Session            
        Session["Roles"] = roles;

        HttpContext.Items.Add("roles", roles);

        //Let us now set the authentication cookie so that we can use that later.
        FormsAuthentication.SetAuthCookie(model.UserName, false);

        //Login successful lets put him to requested page
        string returnUrl = Request.QueryString["ReturnUrl"] as string;

        return RedirectToLocal(returnUrl);

        if (returnUrl != null)
        {
            Response.Redirect(returnUrl);
        }
        else
        {
            //no return URL specified so lets kick him to home page
            Response.Redirect("Default.aspx");
        }
    }
    else
    {
        // If we got this far, something failed, redisplay form
        ModelState.AddModelError("", 
          "The user name or password provided is incorrect");
        return View(model);
    }
}

All the actions that need authentication have to be loaded in a list, and also all of the roles and actions that each role has access to, I have put some sample code to simulate them "AllRoles" and "NeedAuthenticationActions". Then we need to create a base class for controllers in which I have overridden the OnActionExecuting method, in which the user will be authorized based on its current role and whether he/she has logged in or not, the action may also has no need to be authorized.

public class ControllerBase : Controller
{
private string ActionKey;

//sample data for the roles of the application
Dictionary<string, List<string>> AllRoles = 
           new Dictionary<string, List<string>>();

protected void initRoles()
{
    AllRoles.Add("role1", new List<string>() { "Controller1-View", 
      "Controller1-Create", "Controller1-Edit", "Controller1-Delete" });
    AllRoles.Add("role2", new List<string>() { "Controller1-View", "Controller1-Create" });
    AllRoles.Add("role3", new List<string>() { "Controller1-View" });
}
//sample data for the pages that need authorization
List<string> NeedAuthenticationActions = 
  new List<string>() { "Controller1-Edit", "Controller1-Delete"};  


protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
    ActionKey = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName +
                       "-" + filterContext.ActionDescriptor.ActionName;
                
    string role = Session["Roles"].ToString();//getting the current role
    if (NeedAuthenticationActions.Any(s => s.Equals(ActionKey, StringComparison.OrdinalIgnoreCase)))
    {
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            string redirectUrl = string.Format("?returnUrl={0}", 
                    filterContext.HttpContext.Request.Url.PathAndQuery);
            filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + redirectUrl, true);
        }
        else //check role
        {
            if (!AllRoles[role].Contains(ActionKey))
            {
                filterContext.HttpContext.Response.Redirect("~/NoAccess", true);
            }
        }
    }
}

Points of Interest

Using this scenario there is no need to hard code the Authorize attribute and role or user names in the controller class, and all of them may be loaded from any source and be used dynamically.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here