UPD There is a part 2 of this blog-post explaining how to do roles and fixing a minor issue with authentication.

UPD If you are on Windows 10 and get “System.IO.FileNotFoundException: The system cannot find the file specified”, have a look on this page. Thanks to David Engel for this link.

A while back I had to implement a login system that relied on in-house Active Directory. I did spend some time on figuring out how to work this in the nicest possible ways.

One of the approaches I used in the past is to slam Windows Authentication on top of the entire site and be done with it. But this is not very user-friendly – before showing anything, you are slammed with a nasty prompt for username/password. And you need to remember to include your domain name in some cases. I totally did not want that on a new green-field project. So here goes the instructions on how to do a nice authentication against your Windows Users or in-house hosted Active Directory.

For this project I’ll use Visual Studio 2015. But steps for VS2013 will be the same. Can’t say anything nice about any earlier versions of Visual Studio – I don’t use them anymore.

First create an MVC project and make sure you select “No Authentication” when you create the project:

2016-03-09 22_41_57-Start Page - Microsoft Visual Studio (Administrator)

Just after creation of a new project, I usually update all NuGet packages – it is easier to update packages when project is empty, rather later down the line. You don’t have to do this.

Now install new NuGet packages. You will need the following and all their dependencies:

  • Microsoft.Owin.Security.Cookies
  • Microsoft.Owin.Host.SystemWeb

Now in your App_Start folder create Startup.cs file (does not have to be in this folder or with this name, only following convention). Put this into that file:

using Microsoft.Owin;
using Owin;

[assembly: OwinStartupAttribute(typeof(MyProject.Startup))]
namespace MyProject
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
        }
    }
}

This will not compile because you don’t have ConfigureAuth method. Add another file in App_Start called Startup.Auth.cs and put this code there:

using System;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin;


namespace MyProject
{
    public static class MyAuthentication
    {
        public const String ApplicationCookie = "MyProjectAuthenticationType";
    }

    public partial class Startup
    {
        public void ConfigureAuth(IAppBuilder app)
        {
            // need to add UserManager into owin, because this is used in cookie invalidation
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = MyAuthentication.ApplicationCookie,
                LoginPath = new PathString("/Login"),
                Provider = new CookieAuthenticationProvider(),
                CookieName = "MyCookieName",
                CookieHttpOnly = true,
                ExpireTimeSpan = TimeSpan.FromHours(12), // adjust to your needs
            });
        }
    }
}

Let’s see what we’ve done so far. Startup.cs file contains OwinStartupAttribute attribute on top of the class – this tells OWIN system what Configuration method required by OWIN is named MyProject.Startup. There are other ways of doing this. Second file Startup.Auth.cs contains OWIN configuration for authentication – this is very similar to what you’d see if you created a project with ASP.Net Identity authentication. Only I’ve replaced the name of AuthenticationType with my own, stored in statically-available constant – we’ll need this value elsewhere later. Also we are saying that OWIN should redirect unauthenticated requests to /Login, but we don’t have anything there at the moment. Let’s create a controller for that.

using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;


namespace ActiveDirectoryAuthentication.Controllers
{
    public class LoginController : Controller
    {
        [AllowAnonymous]
        public virtual ActionResult Index()
        {
            return View();
        }


        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public virtual ActionResult Index(LoginViewModel model)
        {
            //TODO process login
        }
    }


    public class LoginViewModel
    {
        [Required, AllowHtml]
        public string Username { get; set; }

        [Required]
        [AllowHtml]
        [DataType(DataType.Password)]
        public string Password { get; set; }
    }
}

And Index.cshtml will be like this:

@model ActiveDirectoryAuthentication.Controllers.LoginViewModel

@{
    ViewBag.Title = "Login";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Login</h2>

<div class="row">
    <div class="col-md-8">
        <section id="loginForm">
            @using (Html.BeginForm("Index", "Login", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
            {
                @Html.AntiForgeryToken()
                <hr />
                @Html.ValidationSummary(true, "", new { @class = "text-danger" })
                <div class="form-group">
                    @Html.LabelFor(m => m.Username, new { @class = "col-md-2 control-label" })
                    <div class="col-md-10">
                        @Html.TextBoxFor(m => m.Username, new { @class = "form-control" })
                        @Html.ValidationMessageFor(m => m.Username, "", new { @class = "text-danger" })
                    </div>
                </div>
                <div class="form-group">
                    @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
                    <div class="col-md-10">
                        @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
                        @Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-md-offset-2 col-md-10">
                        <input type="submit" value="Log in" class="btn btn-default" />
                    </div>
                </div>
            }
        </section>
    </div>
</div>

This will give us a nice login form:

2016-03-09 23_26_12-Login - My ASP.NET Application

To check if our authentication stuff works let’s put [Authorize] attribute on one of the existing actions in HomeController:

[Authorize]
public ActionResult About()
{
    ViewBag.Message = "Your application description page.";

    return View();
}

Now if you navigate to /Home/About you should be redirected to login page. It is important that this works. If you don’t get login prompt, something is wrong and you need to go back re-do missing steps. Two possible problems I can see here: /Login route does not point to LoginController or OWIN startup configuration is not called.

All of above was easy – now we need to come up with logic that actually does checking of username and password combination against AD.

Before we start any further add System.DirectoryServices.AccountManagement as a Reference to your project. And now create this class:

using System;
using System.DirectoryServices.AccountManagement;
using System.Security.Claims;
using Microsoft.Owin.Security;
using MyProject;


namespace ActiveDirectoryAuthentication.Models
{
    public class AdAuthenticationService
    {
        public class AuthenticationResult
        {
            public AuthenticationResult(string errorMessage = null)
            {
                ErrorMessage = errorMessage;
            }

            public String ErrorMessage { get; private set; }
            public Boolean IsSuccess => String.IsNullOrEmpty(ErrorMessage); 
        }

        private readonly IAuthenticationManager authenticationManager;

        public AdAuthenticationService(IAuthenticationManager authenticationManager)
        {
            this.authenticationManager = authenticationManager;
        }


        /// <summary>
        /// Check if username and password matches existing account in AD. 
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public AuthenticationResult SignIn(String username, String password)
        {
#if DEBUG
            // authenticates against your local machine - for development time
            ContextType authenticationType = ContextType.Machine; 
#else
            // authenticates against your Domain AD
            ContextType authenticationType = ContextType.Domain;
#endif
            PrincipalContext principalContext = new PrincipalContext(authenticationType);
            bool isAuthenticated = false;
            UserPrincipal userPrincipal = null;
            try
            {
                isAuthenticated = principalContext.ValidateCredentials(username, password, ContextOptions.Negotiate);
                if (isAuthenticated)
                {
                    userPrincipal = UserPrincipal.FindByIdentity(principalContext, username);
                }
            }
            catch (Exception)
            {
                isAuthenticated = false;
                userPrincipal = null;
            }

            if (!isAuthenticated || userPrincipal == null)
            {
                return new AuthenticationResult("Username or Password is not correct");
            }

            if (userPrincipal.IsAccountLockedOut())
            {
                // here can be a security related discussion weather it is worth 
                // revealing this information
                return new AuthenticationResult("Your account is locked.");
            }

            if (userPrincipal.Enabled.HasValue && userPrincipal.Enabled.Value == false)
            {
                // here can be a security related discussion weather it is worth 
                // revealing this information
                return new AuthenticationResult("Your account is disabled");
            }

            var identity = CreateIdentity(userPrincipal);

            authenticationManager.SignOut(MyAuthentication.ApplicationCookie);
            authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, identity);


            return new AuthenticationResult();
        }


        private ClaimsIdentity CreateIdentity(UserPrincipal userPrincipal)
        {
            var identity = new ClaimsIdentity(MyAuthentication.ApplicationCookie, ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
            identity.AddClaim(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "Active Directory"));
            identity.AddClaim(new Claim(ClaimTypes.Name, userPrincipal.SamAccountName));
            identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userPrincipal.SamAccountName));
            if (!String.IsNullOrEmpty(userPrincipal.EmailAddress))
            {
                identity.AddClaim(new Claim(ClaimTypes.Email, userPrincipal.EmailAddress));
            }

            // add your own claims if you need to add more information stored on the cookie

            return identity;
        }
    }
}

Then in you LoginController do the following changes:

using System;
using System.ComponentModel.DataAnnotations;
using System.Web;
using System.Web.Mvc;
using ActiveDirectoryAuthentication.Models;
using Microsoft.Owin.Security;


namespace ActiveDirectoryAuthentication.Controllers
{
    public class LoginController : Controller
    {
        [AllowAnonymous]
        public virtual ActionResult Index()
        {
            return View();
        }


        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public virtual ActionResult Index(LoginViewModel model, string returnUrl)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }

            // usually this will be injected via DI. but creating this manually now for brevity
            IAuthenticationManager authenticationManager = HttpContext.GetOwinContext().Authentication;
            var authService = new AdAuthenticationService(authenticationManager);

            var authenticationResult = authService.SignIn(model.Username, model.Password);

            if (authenticationResult.IsSuccess)
            {
                // we are in!
                return RedirectToLocal(returnUrl);
            }

            ModelState.AddModelError("", authenticationResult.ErrorMessage);
            return View(model);
        }


        private ActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            return RedirectToAction("Index", "Home");
        }


        [ValidateAntiForgeryToken]
        public virtual ActionResult Logoff()
        {
            IAuthenticationManager authenticationManager = HttpContext.GetOwinContext().Authentication;
            authenticationManager.SignOut(MyAuthentication.ApplicationCookie);

            return RedirectToAction("Index");
        }
    }


    public class LoginViewModel
    {
        [Required, AllowHtml]
        public string Username { get; set; }

        [Required]
        [AllowHtml]
        [DataType(DataType.Password)]
        public string Password { get; set; }
    }
}

Now you will be able to login with your own PC password. On my home PC I have no Domain, so when I’m creating a authenticationType I show ContextType.Machine – this will check username/password against users on your local machine. This is handy for development time if your machine is not in domain. When you deploy to the server most likely you’ll have to change this to ContextType.Domain. In the code above I do this change by compilation type – if you compile in Debug, you’ll get Machine authentication; for Release configuration you’ll get Domain authentication. In my latest project that uses this type of authentication I specify what type of authentication I need in web.config. You can do it, but leave it for later, once you have everything running as required.

So once you authenticated an authentication cookie is set on your requests. The only minor problem is that we don’t see logged-in username anywhere and no way to log-out. Let’s make this happen.

In Views/Shared create new Razor file _LoginPartial.cshtml:

@if (Request.IsAuthenticated)
{
    using (Html.BeginForm("Logoff", "Login", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" }))
    {
        @Html.AntiForgeryToken()

        <ul class="nav navbar-nav navbar-right dropdown">
            <li class="dropdown">
                <a href="#" class="dropdown-toggle" data-toggle="dropdown">@User.Identity.Name<span class="caret"></span></a>
                <ul class="dropdown-menu" role="menu">
                    <li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>
                </ul>
            </li>
        </ul>
    }
}
else
{
    <ul class="nav navbar-nav navbar-right">
        <li>@Html.ActionLink("Log in", "Index", "Login", null, new { id = "loginLink" })</li>
    </ul>
}

In Views/Shared/_Layout.cshtml add @Html.Partial("_LoginPartial"):

.......SNIP........
<div class="navbar-collapse collapse">
    <ul class="nav navbar-nav">
        <li>@Html.ActionLink("Home", "Index", "Home")</li>
        <li>@Html.ActionLink("About", "About", "Home")</li>
        <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
    </ul>
    @Html.Partial("_LoginPartial")
</div>
.......SNIP........

And now you should be able to login with your machine/Domain user credentials and logout. You may ask about password reset, but I have not implemented this yet, though I might need it pretty soon – I’ll blog about this separately.

In case you struggle, I have posted full solution to GitHub repository.

  • Michael Chora

    If you follow this step by step.. where you first make the LoginController.cs the following code needs to be updated slightly:

    public virtual ActionResult Index(LoginViewModel model)
    {
    //TODO process login
    }

    the ‘//TODO process login’ needs to be ‘return null;’ in order to debug the application.

    • MV

      Thank you!

  • Иван Иванов

    works fine with local username. i changed compilation debug to “false” in my web.config and i cannot login with my domain credentials. What else i need to do to make it work?

    • What is the value of `authenticationType` in line `PrincipalContext principalContext = new PrincipalContext(authenticationType);` in `AdAuthenticationService.SignIn()`?

      Though `debug=”false”` should not make any difference – if this is the only thing changed.

  • Stefan Kjellberg

    First, very good and informative.
    Problem I have is that on IIS Express Everything runs fine, but when shifting over to Local IIS I end up with a 500..

    • What does 500 say? can you get exception message?
      I almost never use IIS Express, so this project was done on local IIS and I have a production project with this code running on IIS with no problem.

      • Stefan Kjellberg

        I fixed it by setting up a new minimal root web site in Local IIS, then publish from VS.
        So my settings where wrong, have not had time to check exactly what diffs there where.
        All Auth settings in root was deactivated, but anonymous.

        • Good staff. Thanks for sharing – I’ll update the article when I have a bit more time.

    • Иван Иванов

      I have the same thing, works only in iis express, on local iis i get
      Username or Password is not correct. no exception

      • I can only think of IIS permissions. I run ApplicationPool as a LocalSystem – maybe this can help?

  • Николай Мохов

    Thank you for an excellent article! Is it possible to transfer the code to the ASP.NET 1.0 Core? And also is it possible to integrate with ASP.NET Identity 3?

    • Sorry, don’t know. Have not had a chance to play with Core yet. I doubt that `System.DirectoryServices.AccountManagement` has been transferred to Core yet – and it is the key authentication component.

      • Николай Мохов

        I forgot to clarify that I do not use .NET Core and the full .NET Framework. I got access to the AD. I will continue on.

        • Zig

          Have you tried to make it work with core version of .NET?

  • Scott

    Is there a reason you have [AllowHtml]
    in the LoginViewModel? Isn’t that a security risk?

    • It is a risk only if you are going to render username or password in pages. But your password should never be displayed anywhere and stored only hashed. And if users have angle brackets in their password and you don’t have this attribute, they will never be able login. Not so much for username, but here usernames are already provided by Active Directory and there is only a limited set of special characters AD allows into username. So if somebody puts some malicious input into username, they’ll never login. Possibly they’ll generate an exception from MVC, but nothing else.

  • Luis Costa

    hello, I’m having an error in the class AuthenticationResult, in the VST2013 I’m stucked with the operator => is returning that ; is expected. “public Boolean IsSuccess => String.IsNullOrEmpty(ErrorMessage); ”
    could you help me?

    • It is C# 6 notation. Replace it with something like this:

      public Boolean IsSuccess()
      {
      return String.IsNullOrEmpty(ErrorMessage);
      }

      • Luis Costa

        Yes I had already substitute that. Thanks

  • Linn

    Hi, I seem to not be able to get the logoff part to work as it does not actually log the user out of the application. I implemented everything as was written here, but I am not sure what is causing the issues. Additionally, I seem to not be able to return to the correct ReturnUrl on successful login, it always takes the path of the fall back controller, even when I add an else statement to the RedirectToLocal(returnUrl). Any help is greatly appreciated.

    • I’ve seen a lot of problems with logoff when “SomeString” in `authenticationManager.SignOut(“SomeString”)` does not match “SomeString” in Startup.cs `AuthenticationType = “SomeString”,`. Check if that is the case for you.

      As for redirect to local – many reasons for that, can’t easily help you here.

    • Ivelin Denev

      In order returnUrl to work correctly, index method in the LoginController should be like this:

      public virtual ActionResult Index( string returnUrl )
      {
      ViewBag.ReturnUrl = returnUrl;
      return View();
      }

      And the index view login form should start with:

      @using (Html.BeginForm(“Index”, “Login”, new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = “form-horizontal”, role = “form” }))

      This fixed the issue for me.

      • David Engel

        I was able to get the redirect to URL working without the ViewBag using BeginForm in the index.cshtml like this:
        @using (Html.BeginForm(“Index”, “Login”, new { returnUrl = Request.QueryString[“ReturnUrl”] }, FormMethod.Post, new { @class = “form-horizontal”, role = “form” }))

        -Dave

  • Иван Иванов

    after user succesfully loged in, what is the best way to store user details in database (like Surname, GivenName), for logging? thanks.

    • why do you need to save user details in DB? they are in AD already. Also how is logging using DB?

      • Иван Иванов

        I create a simple application documentflow, for document management, i want to save the document and also a username who saved document

        • use this to get username:

          public static String GetUsername(this IPrincipal principal)
          {
          var claimsPrincipal = principal as ClaimsPrincipal;
          //TODO claimsPrincipal check for null

          var claimsIdentity = principal.Identity as ClaimsIdentity;
          //TODO claimsIdentity check for null
          var name = claimsIdentity.FindFirst(ClaimTypes.Name).Value;
          return name;
          }

          var currentUsername = HttpContext.Current.User.GetUsername();

  • Abhilash D K

    Hi. Thank you for a great post like this. Helped me understand a lot. I am able to login through my corporate domain account. I do have one requirement. I don’t want to present a Login screen. I would like to login directly i.e without entering user name and password. How can I achive this? I have tried setting and Providing an Authorize attribute on my HomeController it works. I am not using OWIN middleware there. But If I need the same kind of functionality using OWIN what changes are needed? Also I would like to Show/Hide menus based on the Roles the user is associated with. Could you shed some light on it? I used [Authorize(Roles=@”DOMAINNAMEAdministrators”)]. It did not work maybe because I am not in Administrators role. I tried [Authorize(Roles=@”DOMAINNAMEUsers”)]. It’s asking for UserName and Password. I don’t want that. It should display the HomeControllers Index Action if the user is in Users Role without asking for password. Please could you guide me how to do that?

    Thanks in Advance

    • Try removing “DOMAINNAME” from your filter.

      • Abhilash D K

        Hi Thank you. Can you guide me how to login automatically without using Login Form.

        • I did briefly look into this and was unable to produce a reliable solution. It all depends on too many factors, including browser and domain configuration. Unfortunately I’m unable to help you in this matter.

          • Abhilash D K

            Hi No Problem. Thank you very much.

          • hi. were you able to come up with a solution?

          • Abhilash D K

            No. I am not able to find a solution for this.

          • was wondering, regardless of browser and domain configuration, will auto login still be possible?

          • Possible, but I have not spent enough time/effort on this, so won’t be publishing any findings.

  • MV

    In addition to checking username and password, Is there a way to allow people to log in based on whether they are part of a certain AD security group?

    • See this answer: http://stackoverflow.com/a/25751247/809357
      I’ve not had a need in doing this, but you can add all user groups for user as Roles.

      in `private ClaimsIdentity CreateIdentity(UserPrincipal userPrincipal)` do this:

      var groups = user.GetAuthorizationGroups();
      foreach(GroupPrincipal group in groups)
      {
      identity.AddClaim(new Claim(ClaimTypes.Role, group.Name));
      }

      And then you’ll be able to use [Authorize(Roles=”Admin”)]

      Disclaimer – not tested, might not work as expected.

      • wesly arfan

        i have the same issue and i have tried to do like you suggest, it does not wok yet

      • wesly arfan

        when i tried your suggest on your project that you put in github, it is works.
        so i have to tried and find the differencces between your and mine.
        thank you.

  • wesly arfan

    i have implement and i think it was ok.
    when i want to set permission of user to access some controller, it does not work.

    [EnableQuery(MaxExpansionDepth = 4)]
    [Authorize(Roles = “asset5”)]
    public IQueryable GetWellActivityMonitoringMasterDataOData()
    {
    return db.WELL_ACTIVITY_MONITORING;
    }

    for example like that,
    i have a user that is member of group asset5 in active directory, why can not the user able to acces the GetWellActivityMonitoringMasterDataOData controller?

    • This is because I don’t do anything with the roles – I don’t read them, I did not add them to the cookie. See the comment below on how to get this implemented.

      • wesly arfan

        i have already try your suggest, but get nothing.

        I get error like this,

        A network-related or instance-specific error
        occurred while establishing a connection to SQL Server. The server was
        not found or was not accessible. Verify that the instance name is
        correct and that SQL Server is configured to allow remote connections.
        (provider: SQL Network Interfaces, error: 26 – Error Locating
        Server/Instance Specified)

        • That’s odd. It is trying to reach SQL Server – it should not every think about it. What line causes the exception?

          • wesly arfan

            i can’t see where exactly the error come.
            my program can not reach the code inside my controller.
            there is never haven my program execute the return View();
            my controller is like this

            [Authorize(Roles = “files-pep-pekarya”)]
            public ActionResult Index()
            {
            return View();
            }

          • wesly arfan

            i got it. that is because i add the following code in my webconfig
            s

          • I was about to suggest you to check your web.config, but you got it yourself. Glad it works for you!

          • wesly arfan

            thank you very much. you much help me.

  • David Engel

    I am using Windows 10 and VS2015. After following the instructions, my login would always return invalid user on IIS Express but when deployed to IIS server, it worked fine. I am using Machine authentication with local users. After some research, I found this related article:

    http://stackoverflow.com/questions/33717673/system-directoryservices-accountmanagement-principalcontext-broken-after-windows

    It explains that registry key edits are needed for the ValidateCredentials method to work after some Windows 10 updates in the answer from Doogal. Hope it helps.

    -Dave

    • Oh, that explains it – I had people asking me about that exception and I’m on Windows 7. Thanks very much – I’ve added this link to the article

  • Mark

    Great: this all works well. However, I need to be able to persist logins for a period of time, negating the need to login each time. IsPersistent = true does not seem to work as expected and some of the workarounds I’ve looked at assume a full authentication load rather than your simplified version. In a nutshell, how can I get logins to persist? Thanks

    • What’s the value for your `ExpireTimeSpan` in `AuthStartup.cs`? IsPersist is known to be flaky at best, so just set longer ExpireTimeStamp. No magic here – cookie is set by the same libraries used in ASP.Net Identity.

      • Mark

        TimeSpan.FromDays(90)

        • Do you have TimestampValidator configured in any way? How long does the cookie last?

          • Mark

            No – I have implemented your code exactly as is (which works great, BTW). Where would that go?

          • No, no need in Timestamp validator, but if it is configured – it can be a reason behind the log outs. How long does the login last? What is the actual cookie expiry date set?

          • Mark

            ExpiresUtc = DateTime.UtcNow.AddDays(1000)

            Is that correct?
            I have read that IsPersistent is a little flaky but this only seems to hold the login for no more than an hour.
            Appreciate the help.

          • No, I mean if you look on cookie records in HTTP Headers (using Fiddler or Developer tools in Chrome), what are the cookie properties? also when you are getting logged out, are cookies getting erased? also, does this answer help: http://stackoverflow.com/a/37090696/809357

  • sayish

    hi, will i am able to change domain user passwords and register user in active directory using this approach?

    • To change user passwords and register new users you need to add more code. Here we just check username/password against AD.

  • shivani goyal

    Hi, This article helped me a lot. But I am not able to login with my login details. I am running my application on remote machine using mstsc. which credentials should I use?
    Thanks in advance.

    • You need to use credentials the same as you use for remote machine

      • shivani goyal

        I have tried with both the remote machine credentials as well as with my pc credentials but in both the cases it says “Username or password is incorrect”

        • Is this domain account you use or just a machine one? have you accordingly set a value in your ContextType to be ContextType.Machine or ContextType.Domain?

          • shivani goyal

            I have tried with both domain as well machine. all permutations and combinations. Still unable to get in…..
            could you plz tell me where can I check active directory enty?
            or how does it work?
            from where AD authentication the existence of the user?
            Any help would be appreciable :)

          • Sorry, can’t be much help here – so many possibilities, not enough data in my hands.

          • shivani goyal

            I was not using proper credentials. It worked now.
            Thanx

          • Nice! It is always something very simple that gets you!

  • Richard

    Hi there,

    Thanks for this post! I have managed to integrate your code into a standard MVC 5 project, replacing the Individual logins (e.g. logins stored on SQL). The code works fine and I have managed to login against our local AD server.

    However I am finding that the session dose not remain “authorized” and User.Identity.IsAuthenticated dose not return true.

    I know this is probably because I am trying to retro fit an existing MVC 5 project but because of the amount of development which when into the original app I cant just start again. And I need to move it over to AD..

    Any pointers?
    Thanks

    • That’s strange, because I use `User.Identity.IsAuthenticated` in my projects and everything is as expected. One thing to check – in debugger inspect `User.Identity` object. It should actually be `ClaimsIdentity` with the claims. And also check if you have assigned a value to string `MyAuthentication.ApplicationCookie` – this is a description of authentication type. And this is what is looked at when executing `IsAuthenticated`: https://referencesource.microsoft.com/#mscorlib/system/security/claims/ClaimsIdentity.cs,459

  • Koolbaba Dev

    I downloaded your solution and converted down to .NET 4.5 in VS 2013. When I ran the app in my local, “userPrincipal = UserPrincipal.FindByIdentity(principalContext, username)” always returns null. When I changed the ContextType to ContextType.Domain, “isAuthenticated” always returns false. Am I missing something? Can you please shed some lights? Thank you!

  • Anthony Griggs

    Much Thanks! You made it real simple and had it up and going in about 15 Minutes! @disqus_ynXTw58tLd:disqus I had the same issue. For me the fix was simply to switch my ContextType.Machine to ContextType.Domain. Although in your case from what you have described it doesn’t look like it’s going to be that simple?

  • Andrej Milas

    Great article. It’s amazing how little information there is surrounding AD auth + claims management , Microsoft just kind of assumed we would all move toward Azure AD authentication. I think they consider Corp Intranet sites and on-prem AD services ancient history. Meanwhile devs get stuck trying to manage roles in Angular2 clients , with a mish mash of AD , indentity framework and magic.

    • Well, I’ve found all I needed for this article online. And with some tinkering managed to string it all together for a working solution. So information is there, it is just not well organised.

  • Bartosz Jarmuż

    Hello,
    This might be a silly question, but I am all new to this topic and I’m just started reading about authentication.
    There is a tutorial on the same thing but it is considerably different: http://www.schiffhauer.com/mvc-5-and-active-directory-authentication/

    The thing that puzzles me is that you do not mention setting up the AD connection string ever… so where/how does the server know against which domain to authenticate?

    Another thing is that the authentication code inside the Login action itself is considerably different, using different objects…

    What is the reason behind this differences?

    ===================
    Also – this is a great post and I also liked the series of related posts, thanks for that!

    • Difference is that your reference uses Membership Provider that is somewhat outdated and no longer actively developed. I’m using OWIN to set the cookie – this is more modern.

      As for where does it connect to – connects to the domain if machine is in the domain. Or to the localhost if you are using `.Machine` setting in the configuration.

  • taimo

    Works great, thanks.

    I came across your post about preventing multiple logins at http://tech.trailmax.info/2015/09/prevent-multiple-logins-in-asp-net-identity/. How should I implement UserManager class in order to add UserManager.UpdateSecurityStampAsync to controller, and ApplicationUserManager and ApplicationUser classes in Startup.Auth.cs?

  • Matt

    I was able to replicate your tutorial and it was great. I got it to work/validate locally on my computer with my login through ContextType.Domain. But when I package the project up and deploy it through IIS on Windows Server 2012 I go to run it and it tells me the ldap server was unavailable. Is this on the code, or something I need to setup in IIS for the code to run properly, or is it the connection to the domain? Initially it seems to be the connection but I’m just confused as to why it can work locally but not on the server.

    • Is your server part of domain? Is there Active Directory available in the domain? If not, it would not work with `ContextType.Domain`.

      • Matt

        There is an active directory domain tied to the computer. I use an account to log into the vm that goes through an active directory. but when i go to use that same log in on the application, it doesn’t work. the only time the login works on the vm is when I use ContextType.Computer and log in with the Administrator credentials.

  • EE

    where do i need to put this LDAP://tnb.my:389/ ?

  • Flexabust bergson

    Thanks for this! It works for me, but only problem is once I am logged in, I get Javascript syntax errors all over the place (which don’t occur when I am not logged in). Any ideas why? My redirections do not work any more, I try to click for example on “search a user” which redirects to a different controller but I get every time this error

  • Flexabust bergson

    My comment has been deleted maybe it will be back. Thanks for the post, it’s working but I got one issue, the ‘a’ tag inside of _LoginPartial.cshtml containing the user name has a hashtag inside href that makes my whole application crash. I get a javascript syntax error ‘unrecognized expression #’. Taking out href makes it work.

    • Ah, yeah, I’ve seen your comment in email notification, but could not find it here to reply. Good you found the solution!

      • Flexabust bergson

        Thanks! Any idea why that could’ve happened though?

        • Sorry, can’t be guessing anything without looking on your codebase.

          • Flexabust bergson

            Ok no problem, thanks again for this :)

  • Otman Ighoulassen

    Great Article ! Complete and straightforward.
    Thanks a lot.

  • Chris Wiles

    Thanks for putting this together and is working for a single domain. What should I do if I need to login with a different domain that’s in our forest? There will be cases where I’ll need to login as a different “elevated” user for specific domains, and be able to input the username in the “DOMAINusername” format. I’m only able to get the username to work if I enter it without the domain, and it only authenticates with the current domain that the application resides.