MVC 4 authentication with Active Directory or Membership database

Jastol picture Jastol · Jun 13, 2013 · Viewed 7.5k times · Source

I´m building a web application which could get accessed in two ways. Everyone who is working in the same organisation as I can use our active directory to access the application.

Everyone from outside should join the application through a separate membership database. Everyone should have a account in the membership database with his roles, so the ad connection is just a bonus to make it easier to keep the password and username in mind. I searched the internet but couldn't find a comparable situation. This is my first time working with ad.

Does anyone know of a framework that can be used or give me a hint on how I could try to solve the problem?

At moment I implemented the membership connection with System.Web.WebData.SimpleMembershipProvider and it works fine.

In the later development of the application I also need some other connections to the ad to check some information but that is just a problem for another day.

Thanks for the help.

Answer

Tomi Lammi picture Tomi Lammi · Jun 13, 2013

Open up your web.config.

First of all you'll need connectionString for your ActiveDirectory:

  <connectionStrings>
    ...
    <add name="ADConnectionString" connectionString=LDAP://*adserver*/DC=*domain* />
    ...
  </connectionStrings>

Scroll down to the <membership> tag. Make sure you have defaultProvider attribute set for the <membership>, like:

<membership defaultProvider="SimpleMembershipProvider">

Then add new provider for AD members inside <providers>:

    <add name="ADMembershipProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider" connectionStringName="ADConnectionString" attributeMapUsername="sAMAccountName" />

That should do the trick for web.config. Now we need to auth AD users on Log in. Go to your AccountController Login action. First we try to authenticate user via ActiveDirectory, there is handy class called PrincipalContext in System.DirectoryServices.AccountManagement namespace. If that fails we use the default membership provider:

        public ActionResult Login(LoginModel model, string returnUrl)
        {
            try
            {
                // try to auth user via AD
                using (PrincipalContext pc = new PrincipalContext(ContextType.Domain))
                {
                    if (pc.ValidateCredentials(model.UserName, model.Password))
                    {
                        FormsAuthentication.SetAuthCookie(model.UserName, false);
                        return RedirectToAction("Index", "Home");
                    }
                }
                // try the default membership auth if active directory fails

                if (Membership.ValidateUser(model.UserName, model.Password))
                {
                    FormsAuthentication.SetAuthCookie(model.UserName, false);

                    if (Url.IsLocalUrl(returnUrl))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        return RedirectToAction("Index", "Home");
                    }
                }
                else
                {
                    ModelState.AddModelError("", "Login failed");
                }
            }
            catch
            {
            }
            GetErrorsFromModelState();
            return View(model);
        }

For your later requirements you can get the current logged in ActiveDirectory user with UserPrincipal class:

using (var context = new PrincipalContext( ContextType.Domain)) 
{
    using (var aduser = UserPrincipal.FindByIdentity( context,IdentityType.SamAccountName, HttpContext.User.Identity.Name))
    {
        ...
    }
}

Hope this helps and I didn't miss anything.