Thursday, June 29, 2006

ASP.NET Authentication Using Active Directory

Well, here's my first post. I'm creating this blog to keep track of useful tips and tricks I find during the course of my professional duties as a .NET web developer working in downtown Los Angeles. Today's post is about ASP.NET security using Active Directory.

The general idea is to authorize only those users who belong to a particular Active Directory group, so naturally this approach only applies to intranet development.

First off we create our web application. It doesn't matter what it does, as the focus of this post is on security. Let's assume we're dealing with an application that prints "Hello World" and leave it at that.

When a user tries to open an aspx page in their browser, a number of interesting things happen. Here are the ones we are interested in:

1) IIS Authenticates the user.
2) An application event fires, which can be handled in Global.asax:

Application_AuthenticateRequest(Object sender, EventArgs e)

3) If the user is authenticated, the page displays; otherwise IIS (or whatever web server you are using) sends an error code:

HTTP 401.3 - Access denied by ACL on resource

Our goal is to ensure that the authentication is determined by the user's membership in our defined Active Directory group.

The details of Active Directory membership verification differ from intranet to intranet - I'll leave that exercise to the reader. However once you know for sure that a user does belong to a particular group, the rest is fairly trivial.

Set up your virtual directory to deny anonymous users and enable integrated authentication. This guarantees that only intranet users can get to the next step - authorization.

Next, set your web.config to use Windows auth:

<authentication mode="Windows"/>

This ensures that the user's identity - available at

HttpContext.Current.User.Identity

- is the NT Identity, and not the anonymous, empty identity presented to the application when using Forms or None authentication options. We need this identity to resolve the Active Directory group membership.


Now go into Global.asax and modify the AuthorizationRequest event handler.

The general idea is to determine AD group membership within this event handler, and then use that information to authorize the user if required. The question is, how can we do this? The ASP.NET security model does not provide a simple way to do this. However, since we now know the user should be permitted or denied from the site, we can exploit other features of the security model to achieve our goal. Specifically, ASP.NET has robust role-based security support - so we will use our knowledge to assign AD group-members a role which will have access, and simply do nothing for users who do not belong to AD.

To role-protect your web application, modify your web.config as follows:

<authorization>
<allow roles="CustomRole">
<deny users="*"> <!-- Deny everyone else -->
</authorization>


The role doesn't have to be "CustomRole" - it can be "MyBigBadRole" or "XMLModifier" or whatever. The point is that now, only users who are in that role will be able to access the page. Go ahead and load your app. You will not be able to get to the page, because currently you are not in that role. Be careful to use a role name that is non-generic - "Administrator" or "MyRole" is a bad role, because someone else in your organization might be using it.

So now that the application is secured, how do we give the role to AD group members?

It's fairly straightforward. All we do is assign a new principal to the current context.


using System.Security.Principal;

...

if(UserIsInADGroup())
{
IIdentity currentIdent = HttpContext.Current.User.Identity;
GenericPrincipal gp;
gp = new GenericPrincipal(currentIdent, new string [] { "CustomRole" });
HttpContext.Current.User = gp;
}


And we're done!

Recap:

1) Secure your application using role-based authentication.
2) Handle the Application_AuthenticationRequest
3) If the user turns out to be in the AD group, assign them the appropriate role by creating a new GenericPrinciple.
4) Profit!