2008-07-10

Creating an Active Directory Authentication Service

This should be as easy as a one liner, and guess what - it is :-)

Following the Dependency Inversion Principle, we define a high level policy class - well actually it is an interface.

public interface AuthenticationService
{
    bool Authenticate(string username, string password);
}

This hides the implementation detail from consumers.

Next we need to implement the actual ActiveDirectoryAutenticationService

using System.DirectoryServices.AccountManagement;

public class ActiveDirectoryAuthenticationService : AuthenticationService
{
    private readonly PrincipalContext _principalContext;

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

    public bool Authenticate(string username, string password)
    {
        return _principalContext.ValidateCredentials(username, password, ContextOptions.Negotiate); 
    }
}

If you are using an IOC Container, like Windsor from the Castle Project, the configuration looks the this.
This should go in your main or global.asax, depending on the project type.

using System
using System.DirectoryServices.AccountManagement;

PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, Environment.UserDomainName)
IWindsorContainer container = new new WindsorContainer();
container.Register(
                    Component.For<PrincipalContext>().Instance(principalContext),
                    Component.For<AuthenticdationService>().ImplementedBy<ActiveDirectoryAuthenticationService>()
                  );

Now you are ready to use the AuthenticationService.

public class Consumer
{
    private readonly AuthenticationService _authenticationService;

    public Consumer(AuthenticationService authenticationService)
    {
        _authenticationService = authenticationService;
    }

    public void DoSomethingWhereAuthenticationIsNeeded(string username, string password)
    {
        if (!_authenticationService.Authenticate(username, password)) throw new SecurityException("Invalid username or password.");
    }
}

The end result is a code with low coupling, with an abstraction for a key business concern.

No comments: