MVC 5, OWIN and Active Directory Authentication

Update: I’ve replaced the Task.Run calls with Task.FromResult to improve performance.

I’ve seen several questions on Stack Overflow asking how to do “forms authentication” using the new MVC OWIN stack. This is actually really easy, and requires only a small amount of customization of the starter template you get when you make a New Project using Individual Accounts. Keep in mind there will be a bit of work required if you want to get around some of the other components of the default project if you don’t want to require users sign up for a new account with a fake password. That is a project for another time.

First things first, this solution uses System.DirectoryServices.AccountManagement, so you will need to add that to your project.

Since we will need a PrincipalContext for the all of this, we can use a request-wide singleton produced by the pipeline for us by editing App_Start\Startup.Auth.cs.

using System.DirectoryServices.AccountManagement;
public void ConfigureAuth(IAppBuilder app) {
// This is the important part, this is how you will configure the PrincipalContext used throughout your app
app.CreatePerOwinContext(() => new PrincipalContext(ContextType.Domain));
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
}

That was easy, right? Next we need to tackle App_Start\IdentityConfig.cs, since that contains the ApplicationUserManager with a lot of methods you can override (thanks abstract!). Seriously, if you ever get a chance, check out the source or MSDN docs for it.

The key function we want to play with is CheckPasswordAsync. We will also need to modify the constructor so we have access to our PrincipalContext. Also, there is the “Create” method we need to tweak to accept our new constructor.

 public class ApplicationUserManager : UserManager<ApplicationUser>
    {
    private readonly PrincipalContext _context ;
    public ApplicationUserManager(IUserStore<ApplicationUser> store, PrincipalContext context)
      : base(store)
      {
        _context = context;
      }

    public override async Task<bool> CheckPasswordAsync(ApplicationUser user, string password)
    {
        return await Task.FromResult(_context.ValidateCredentials(user.UserName, password, ContextOptions.Negotiate));
    }

    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) 
    {
        var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()), context.Get<PrincipalContext>());
        //...SNIP...    
    }
}

That’s it! We simply override the password checking algorithm and checkthe password against AD. The reason for using ContextOptions.Negotiate is because it is required to get IIS Express to work correctly.

If you wanted to get extra fancy, you could add a property to the ApplicationUser to say whether or not that user should authenticate against AD. Then you could have a password checking method that falls back to the built-in authentication method, or even just fall back if AD authentication fails.

public override async Task<bool> CheckPasswordAsync(ApplicationUser user, string password)
{
    if (user.IsADUser)
    {
        return await Task.FromResult(_context.ValidateCredentials(user.UserName, password, ContextOptions.Negotiate));
    }
    return await base.CheckPasswordAsync(user, password);
}

Happy Coding!

Advertisements