asp.net-mvcadfsws-federation

Add roles to ADFS IPrincipal


I have been looking for answer to this question for a few days now, but I have not found any success. I would post the links, but it would probably take up the entire page.

So here is what I have...

I have an MVC application, which uses the WC-Federation protocol. I have been able to configure the application, so that it authenticates the users, and returns the claims from ADFS. This works perfect. I can also extract all the claims with no issues. But I am doing this within one of the actions in the controller.

And here is what I want to do...

I want to use ADFS to authenticate the user, but I want to use my own internal roles to authorize the user to have access to specific controllers (e.g. [Authorize(Roles = "CoolRole")]). I want to be able to do this, because I already have a Web API that uses OAuth 2.0, with a backend SQL Server database to manage users and roles (internal and external user.) I now want a secure portal that will allow internal users to access the data with a single-sign-on experience. Looking at the Controller model, I noticed there are some properties associated with the authentication process (OnAuthentication, OnAuthenticationChallenge) and one for the authorization process (OnAuthorization.)

I don't necessarily need the code, but I feel like I've hit a brick all, and I need to be pointed in the right direction.

UPDATE

I tried this:

protected override void OnAuthorization(
       System.Web.Mvc.AuthorizationContext filterContext)
{
    //Private class to create a new IPrincipal based on my AppUserMgr
    var user =  _setCurrentUser(
                  (ClaimsIdentity)filterContext.HttpContext.User.Identity);
    filterContext.HttpContext.User = user;

    base.OnAuthorization(filterContext);
}

This returned a 401 (Unauthorized) response.

and...

protected override void OnAuthentication(
    System.Web.Mvc.Filters.AuthenticationContext filterContext)
{
    //Private class to create a new IPrincipal based on my AppUserMgr
    var user =  _setCurrentUser(
                  (ClaimsIdentity)filterContext.HttpContext.User.Identity);
    filterContext.Principal = user;

    base.OnAuthorization(filterContext);
}

This just calls the STS numerous times, before it fails. I even tried swapping after the assignment to after the base is called in both. No luck.

Prior to the previous ones, I also tried to add an AuthorizeFilter to the control, but that didn't help:

http://pratapreddypilaka.blogspot.in/2012/03/custom-filters-in-mvc-authorization.html


Solution

  • I found this link: http://brockallen.com/2013/01/17/adding-custom-roles-to-windows-roles-in-asp-net-using-claims/

    From there, I guessed my way through

    Here is the basics of what I did:

    I ended up overriding the OnAuthentication method of the Controller, but still made sure to call the base. I did this from within an extended class. Here is the concept:

    public class AdfsController : Controller
    {
        //Some code for adding the AppUserManager (used Unity)
        protected override void OnAuthentication(
                System.Web.Mvc.Filters.AuthenticationContext filterContext)
        {
            base.OnAuthentication(filterContext);
            //Private method to set the Principal
            _setCurrentUser(filterContext.Principal);
        }
    
        private void _setCurrentUser(IPrincipal principal)
        {
            //Put code to find to use your ApplicationUserManager or 
            //dbContext.  roles is a string array
    
            foreach(var role in roles)
            {
                ((ClaimsIdentity)((ClaimsPrincipal)principal).Identity)
                    .AddClaim(new Claim(ClaimTypes.Role, role));
            }
        }
    }
    

    In the Controller, you can now add the follow:

    public class HomeController : AdfsController
    {
        //I used a magic string for demo, but store these in my globals class
        [Authorize(Roles = "CoolRole")]
        public ActionResult Index()
        {
            return View();
        }
    }
    

    I tested this by checking a role assigned to the current user, and that worked! Then I changed the role to something like "reject", which the user was not assigned; and I received a 401 Unauthorized.