User authentication against AD and Roles based authorization in ASP.NET MVC – Part I

It is a common scenario in an enterprise application when as per the requirement we have to authenticate the users against their AD account and authorize them against application specific roles. Especially for the applications that are intranet facing, the customer generally opts for utilizing the existing infrastructure to support Single Sign On kind of model.

For authorization, based on where/how the roles will be stored we have following options to choose:

1. The roles can be maintained using Groups in AD. (If a role has a purpose of only authorization and it is not dependent on any other application specific data)

OR

2. The roles can be maintained in the DB (For example – A Supervisor can only work on requests for HR LOB or A Development Manager can only approve requests for IT LOB etc…)

In either cases, we have to write specific business logic to wire the relationship between user and roles.

Let’s start implementing option 1:

Step 1: In the web.config file, we have to enable Windows authentication:

<authentication mode="Windows">

</authentication>

Step 2:

Create a customized version of Authorize Attribute which will check if the logged in user belongs to an AD group. For simplicity I am maintaining the roles in web.config via a appsettings key. A sample code example is as below:

namespace MyApplication

{

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using System.Configuration;

using System.DirectoryServices;

/// <summary>

/// Pass app settings key value in 'Roles' parameter. this will retrieve the actual users value from configuration file.(web.config)

/// </summary>

public sealed class ADGroupAuthorizeAttribute : AuthorizeAttribute

{

public ADGroupAuthorizeAttribute()

: base()

{

}

protected override bool AuthorizeCore(HttpContextBase httpContext)

{

#if DEBUG

return true;

#endif

////Get the allowed groups from web.config - This can be changed to suit specific needs.

string groupPath = ConfigurationManager.AppSettings[this.Roles];

////This is the currently logged in user

string userId = httpContext.User.Identity.Name;

////This code assumes that you have a Utilities class with ADDomain, LdapConnectionString, LdapAccessUserName and LdapAccessUserPassword

var addomain = string.IsNullOrEmpty(Utilities.ADDomain) ? "mydomain\\" : Utilities.ADDomain.ToLower();

if (string.IsNullOrEmpty(groupPath) || string.IsNullOrEmpty(userId))

{

return false;

}

if (userId.ToLower().Contains(addomain))

{

userId = userId.ToLower().Replace(addomain, string.Empty);

}

DirectoryEntry root = new DirectoryEntry(Utilities.LdapConnectionString, Utilities.LdapAccessUserName, Utilities.LdapAccessUserPassword);

DirectorySearcher search = new DirectorySearcher(root);

List<object> members = new List<object>();

search.SearchScope = SearchScope.Subtree;

search.Filter = "(&(objectClass=group)(cn=" + groupPath + "))";

var result = search.FindOne();

if (result != null)

{

var userName = this.Locateuser(userId);

if (!string.IsNullOrEmpty(userName))

{

var admembers = result.Properties["member"];

if (admembers != null && admembers.Count > 0)

{

for (int i = 0; i < admembers.Count; i++)

{

var admember = admembers[i];

var splitted = admember.ToString().Split(new[] { ',' });

var aduser = splitted[0] + splitted[1];

aduser = aduser.Split(new[] { '=' })[1].Replace("\\", ",");

if (aduser.ToLower() == userName.ToLower())

{

return true;

}

else

{

continue;

}

}

}

else

{

return false;

}

}

else

{

return false;

}

}

return false;

}

private string Locateuser(string userId)

{

string account = userId.Replace(Utilities.ADDomain, string.Empty);

try

{

DirectorySearcher search = new DirectorySearcher(new DirectoryEntry(Utilities.LdapConnectionString, Utilities.LdapAccessUserName, Utilities.LdapAccessUserPassword));

search.Filter = "(SAMAccountName=" + account + ")";

search.PropertiesToLoad.Add("displayName");

SearchResult result = search.FindOne();

if (result != null)

{

return result.Properties["displayname"][0].ToString().ToUpper();

}

else

{

return string.Empty;

}

}

catch (Exception ex)

{

string debug = ex.Message;

return string.Empty;

}

}

}

}

Step 3: [ADGroupAuthorizeAttribute ()] for each of the action where you want to authorize based on AD Groups.

In the next post, I will focus on creating a custom Role Provider and a custom authorize attribute which can be used to redirect to an error page when the user is not authenticated / authorized.