Léster
Léster

Reputation: 1279

Custom authentication and authorization in ASP.NET MVC5?

I have the following database structure DB structure, authentication/authorization part and I have to build authentication/authorization for an ASP.NET MVC 5 site.

The DB schema works as follows: every user belongs to a group, and each group can be granted/denied a permission. Each permission matches an action on a controller (let's say, a Petitions controller would have the following actions: List, View, Add, Edit, Delete, VoteFor, VoteAgainst, Reject and Approve, and each action is an entry in the Permissions table).

The purpose of all this is that, each time a user invokes an action, the site verifies that the user belongs to a group that has been granted permission over that action, and reacts accordingly.

An example: let's say the admin grants the PetitionsList, PetitionsView, PetitionsApprove and PetitionsReject permissions to the Managers group, and the PetitionsList, PetitionsView, PetitionsAdd, PetitionsVoteFor and PetitionsVoteAgainst to the Users group.

In this case, both groups can

Managers can

but they can't

In the same way, users can

but they can't:

and neither can edit or delete a petition.

I'd really like to leverage the attributes functionality in MVC 5. My idea is to build custom attributes that do all the authentication/authorization behind scenes. Something like this:

public class PetitionsController : Controller
{
    [MyCustomAuth(Permission="PetitionsList",Groups="Users,Managers")]
    public ActionResult List()
    {
        //show the list of petitions
    }

    [MyCustomAuth(Permission="PetitionsView",Groups="Users,Managers")]
    public ActionResult View()
    {
        //show a specific petition
    }

    [MyCustomAuth(Permission="PetitionsAdd",Groups="Users")]
    public ActionResult Add()
    {
        //show add petition form
    }

    [HttpPost]
    [MyCustomAuth(Permission="PetitionsAdd",Groups="Users")]
    public ActionResult Add(object[] params)
    {
        //save new petition
    }

    [MyCustomAuth(Permission="PetitionsEdit",Groups="Admins")]
    public ActionResult Edit(int id)
    {
        //show edit petition form
    }

    [HttpPost]
    [MyCustomAuth(Permission="PetitionsEdit",Groups="Admins")]
    public ActionResult Edit(object[] params)
    {
        //save changes to petition
    }

    [HttpPost]
    [MyCustomAuth(Permission="PetitionsDelete",Groups="Admins")]
    public ActionResult Delete(int_id)
    {
        //delete petition
    }

    [HttpPost]
    [MyCustomAuth(Permission="PetitionsVoteFor",Groups="Users")]
    public ActionResult VoteFor(int id)
    {
        //add vote supporting petition
    }

    [HttpPost]
    [MyCustomAuth(Permission="PetitionsVoteAgainst",Groups="Users")]
    public ActionResult VoteAgainst(int id)
    {
        //add vote against petition
    }

    [HttpPost]
    [MyCustomAuth(Permission="PetitionsApprove",Groups="Managers")]
    public ActionResult Approve(int id)
    {
        //approve petition
    }

    [HttpPost]
    [MyCustomAuth(Permission="PetitionsReject",Groups="Managers")]
    public ActionResult Reject(int id)
    {
        //reject petition
    }
}

Please note the MyCustomAuth attribute over every action. I want that attribute to do the heavy lifting of saying if the user is actually authorized to do that action, behind the scenes. Of course, in case it's not authenticated/authorized, the attribute should redirect to login page/401/somewhere else.

My question is, where do I start? Am I expected to implement some special interface/inherit from some class in MVC 5? Or do I have to write this from scratch?

Also, thanks in advance for reading this wall of text and for giving me any tips/pointers in the right direction.

Upvotes: 0

Views: 2344

Answers (1)

Ankush Jain
Ankush Jain

Reputation: 7079

I think you can start with below Custom Authorization Filter. It also handle cases for both - ajax request and full page request.

 public class MyCustomAuthAttribute : FilterAttribute, IAuthorizationFilter
    {
        public string Permission { get; set; }
        public string Groups { get; set; }

        public void OnAuthorization(AuthorizationContext filterContext)
        {
            bool isauthorized = CheckIfUserIsAuthorized();
            if (!isauthorized)
                context.Result = new HttpUnauthorizedResult(); // mark unauthorized

            // Only do something if we are about to give a HttpUnauthorizedResult and we are in AJAX mode.
            if (filterContext.Result is HttpUnauthorizedResult && filterContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.Result = new JsonResult
                {
                    Data = new { Success = false, Message = "Unauthorized Access" },
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };
            }
            else
            {
                base.OnAuthorization(filterContext);
                if (filterContext.Result is HttpUnauthorizedResult)
                {
                    HttpContext.Current.Session.Abandon();
                    System.Web.Security.FormsAuthentication.SignOut();
                    filterContext.Result = new RedirectResult("Your Login Page.");
                }
            }
        }

        private bool IsAuthorizedUser()
        {
            // use Permission, Groups and your logic
        }
    }

and use it the same way you mentioned:

 [MyCustomAuth(Permission="PetitionsEdit",Groups="Admins")]

Upvotes: 1

Related Questions