Active Directory Integration with a .NET Application


In a recent project, I tackled the task of integrating OWIN (Open Web Interface for .NET) claims-based identity with an on-premise Active Directory. In this blog post, I will look into basic details of how to work with AD (Active Directory) Domain Services in a .NET application. System.DirectoryServices.AccountManagement namespace provides a set of APIs that can be used to access users, security groups and other directory objects stored in Active Directory, which I will demonstrate below.

To get started you need to add a reference to System.DirectoryServices.AccountManagement assembly, which is included in the .NET Framework.

Visual Studio Add Reference to System.DirectoryServices.AccountManagement Assembly

For a .NET Core project, Microsoft.Windows.Compatibility NuGet package must be installed in order to access the namespace. Additionally, .NET Core version should be updated to 2.0.6 or later. Example shown below uses NuGet Package Manager Console.

PM> Install-Package Microsoft.Windows.Compatibility

Next, let’s create a simple provider class that can be leveraged in any .NET project. To take it one step further, abstraction in the form of an interface can be created, which will make our code more decoupled and easier to unit test. Having said that, at the minimum, the interface needs to contain methods to retrieve user principal object, validate credentials and retrieve user’s security groups.

public interface IActiveDirectoryProvider
{
    UserPrincipal FindUserByName(string userName);

    bool ValidateUserCredentials(string userName, string password);

    IEnumerable<Principal> GetUserSecurityGroups(string userName);
}

Now it is time to create an interface implementation. An instance of PrincipalContext class is a required dependency. This class encapsulates the domain against which all operations are performed. A good approach is to inject an instance of this class using the constructor.

public class ActiveDirectoryProvider : IActiveDirectoryProvider
{
    private PrincipalContext _principalContext;

    public ActiveDirectoryProvider(PrincipalContext principalContext)
    {
        _principalContext = principalContext;
    }

    public UserPrincipal FindUserByName(string userName)
    {
        return UserPrincipal.FindByIdentity(_principalContext, userName);
    }

    public bool ValidateUserCredentials(string userName, string password)
    {
        return _principalContext.ValidateCredentials(userName, password);
    }

    public IEnumerable<Principal> GetUserSecurityGroups(string userName)
    {
        var userPrincipal = UserPrincipal.FindByIdentity(_principalContext, userName);

        if (userPrincipal == null)
        {
                throw new InvalidOperationException("User does not exist.");
        }

        return userPrincipal.GetAuthorizationGroups().ToList();
    }
}

Now let’s turn our attention to PrincipalContext. In order to initialize a new instance of this class, we must specify a context type, and in most cases, ContextType.Domain should be used. However, for testing purposes, ContextType.Machine will allow authentication against accounts on a local development machine.

PrincipalContext principalContext = new PrincipalContext(ContextType.Domain);

The following is a simple example of how our provider can be leveraged in an MVC Controller or some other .NET component. Of course, you might choose to take advantage of the dependency injection technique to handle the initialization of ActiveDirectoryProvider.

IActiveDirectoryProvider adProvider = new ActiveDirectoryProvider(principalContext);

var isAuthenticated = false;

var user = adProvider.FindUserByName(viewModel.UserName);

if (user != null)
{
    isAuthenticated = adProvider.ValidateUserCredentials(viewModel.UserName, viewModel.Password);
    
    if (isAuthenticated)
    {
        var userGroups = adProvider.GetUserSecurityGroups(viewModel.UserName);

        // Create new ClaimsIdentity using the security groups information ...
    }
}

System.DirectoryServices.AccountManagement namespace provides a great set of APIs to integrate any .NET application with Active Directory. For further discovery of classes and functionality, please refer to Microsoft .NET documentation.

 

View more blogs and tutorials about Sitecore

Learn about our Sitecore work

 

Categories: NET Development, Identity

SEARCH ARTICLES