Extending Windows Authentication in ASP.NET MVC 3 Application

Wowe picture Wowe · Jan 26, 2012 · Viewed 7.3k times · Source

after a lot of googling and reading several solutions on how to manage mixed mode authentication in ASP.NET apps, I still have no fitting solution for my problem.

I've got to implement an intranet application for a bunch of different user groups. Until now i've used windows authenthication which was very simple to implement. My problems arise when it comes to authorizing usergroups for special application functionalities.

Using [Authorize(Users = "DOMAIN\\USER")] works great but due to that i have no access to the active directory managament, it is impossible to me to configure rolemanagement in the way I need it for my application.

What I'd like to do is defining custom roles and memberships in addition to the ones that are defined within the active directory (is such an extension possible? e.g. by implementing an own membershipprovider?).

What do you think is the best solution for my problem. Do I really have to implement a complex mixed mode authentication with forms authentication in addition to windows authentication?

Used Technologies:

  • MS SQL Server 2008
  • MS VS 2010
  • ASP.NET MVC 3 - Razor View Engine
  • Telerik Extensions for ASP.NET MVC
  • IIS 7 on Windows Server 2008

EDIT (final solution thanks to the help of dougajmcdonald):

After pointing me to use a custom IPrincipal implementation I've found some solutions here and here. Putting everything together I came to the following solution:

1.Create a custom principal implementation:

public class MyPrincipal: WindowsPrincipal
{
    List<string> _roles;

    public MyPrincipal(WindowsIdentity identity) : base(identity) {
        // fill roles with a sample string just to test if it works
        _roles = new List<string>{"someTestRole"}; 
        // TODO: Get roles for the identity out of a custom DB table
    }

    public override bool IsInRole(string role)
    {
        if (base.IsInRole(role) || _roles.Contains(role))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

2.Integrate my custom principal implementation into the application through extending the "Global.asax.cs" file:

    protected void Application_AuthenticateRequest(object sender, EventArgs e)
    {
        if (Request.IsAuthenticated)
        {
            WindowsIdentity wi = (WindowsIdentity)HttpContext.Current.User.Identity;
            MyPrincipal mp = new MyPrincipal(wi);
            HttpContext.Current.User = mp;
        }
    }

3.Use my custom roles for authorization in my application

public class HomeController : Controller
{
    [Authorize(Roles= "someTestRole")]
    public ActionResult Index()
    {
        ViewBag.Message = "Welcome to ASP.NET MVC!";

        return View();
    }
}

It works!!! yeah!

Answer

dougajmcdonald picture dougajmcdonald · Jan 26, 2012

I'm not sure if this still applies in MVC, but in Webforms one way to do this would be as follows:

  1. Create a new IPrincipal implementation perhaps extending WindowsPrincipal
  2. In this class, give it a collection of roles (your own custom roles)
  3. Populate those roles, by perhaps getting them from the DB.
  4. Override IsInRole to return true if the role provided is EITHER true from the base call (WindowsAuthentication/Role) OR from your own custom role collection.

This way you can still hook into Principal.IsInRole("MyRole") and also the principal [PrincipalPermission()] annotation.

Hope it helps.

EDIT in answer to q's:

To integrate the principal into the authorisation you need to write your own method for OnAuthenticate in the global.asax for the type of authentication, so I would guess for you, something like this:

void WindowsAuthentication_OnAuthenticate(object sender, WindowsAuthenticationEventArgs e)
{
    // ensure we have a name and made it through authentication
    if (e.Identity != null && e.Identity.IsAuthenticated)
    {
        //create your principal, pass in the identity so you know what permissions are tied to
        MyCustomePrincipal opPrincipal = new MyCustomePrincipal(e.Identity);            
        //assign your principal to the HttpContext.Current.User, or perhaps Thread.Current
        HttpContext.Current.User = opPrincipal;    
    }
}

I believe Authorize came in at a later date to the PrincipalPermission, but I'm not too sure as to when/why of the differences I'm afraid :( - sorry!