Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OWIN with LDAP Authentication

Here is my scenario. I have an MVC 5 application that uses Owin as an authentication mechanism. The default template calls the SignInManager.PasswordSignInAsync in the Login action which I would like to overwrite to use LDAP to validate the user instead of looking into the database.

I am able to do the validation via:

PrincipalContext dc = new PrincipalContext(ContextType.Domain, "domain.com", "DC=domain,DC=com", "user_name", "password");
        bool authenticated = dc.ValidateCredentials(userName, password);

Then I can retrieve the UserPrincipal using:

UserPrincipal user = UserPrincipal.FindByIdentity(dc, IdentityType.SamAccountName, userName);

However, I am stuck here and I am not sure how to continue with signing in the user. The goal is that after I sign in the user, I would have access to User.Identity including all the roles the user is in. Essentially, the app should behave as if it uses Windows Authentication, but the credentials are provided by the user on the Login page.

You would probably ask why not user Windows Authentication directly. The app will be accessed from the outside of the network, but the requirements are to use AD authentication and authorization. Hence my predicament.

Any suggestions are highly appreciated.

Thank you.

like image 617
dpdragnev Avatar asked Oct 16 '15 22:10

dpdragnev


People also ask

What is OWIN authentication?

OWIN (Open Web Interface for . NET) is a standard for an interface between . NET Web applications and Web servers. It is a community-owned open-source project. The OAuth authorization framework enables a third-party application to obtain limited access to a HTTP service.

What is OWIN MVC?

OWIN is an interface between . NET web applications and web server. The main goal of the OWIN interface is to decouple the server and the applications. It acts as middleware. ASP.NET MVC, ASP.NET applications using middleware can interoperate with OWIN-based applications, servers, and middleware.


1 Answers

After many hours of research and trial and error, here is what I ended up doing:

  1. AccountController.cs - Create the application user and sign in

        ApplicationUser usr = new ApplicationUser() { UserName = model.Email };
        bool auth = await UserManager.CheckPasswordAsync(usr, model.Password);
        if (auth)
                    {
                        List claims = new List();
    
    
                foreach (var group in Request.LogonUserIdentity.Groups)
                {
                    string role = new SecurityIdentifier(group.Value).Translate(typeof(NTAccount)).Value;
                    string clean = role.Substring(role.IndexOf("\\") + 1, role.Length - (role.IndexOf("\\") + 1));
                    claims.Add(new Claim(ClaimTypes.Role, clean));
                }
                claims.Add(new Claim(ClaimTypes.NameIdentifier, model.Email));
                claims.Add(new Claim(ClaimTypes.Name, model.Email));
                ClaimsIdentity ci = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
                 AuthenticationManager.SignIn(new AuthenticationProperties()
                 {
                     AllowRefresh = true,
                     IsPersistent = false,
                     ExpiresUtc = DateTime.UtcNow.AddDays(7),
                 }, ci);
                 return RedirectToLocal(returnUrl);
                }
                else
                {
                    ModelState.AddModelError("", "Invalid login credentials.");
                    return View(model);
                }
    
  2. IdentityConfig.cs (CheckPasswordAsync) - Authenticate against LDAP

    public override async Task CheckPasswordAsync(ApplicationUser user, string password)
            {
                PrincipalContext dc = new PrincipalContext(ContextType.Domain, "domain", "DC=domain,DC=com", [user_name], [password]);
                bool authenticated = dc.ValidateCredentials(user.UserName, password);
                return authenticated;
            }
    
  3. Global.asax - if you are using the Anti Forgery Token in your login form

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;

At this point, you will are logged in and can access the User.Identity object. You can also mark controllers and actions with [Authorize(Roles = "some_role"]

It turned out that it was easier than I thought, it is just that not much is really written on the topic (at least I could not find anything).

Also, this code presumes that you are running the app from a server which has access to the Domain Controller on your network. If you are on a DMZ server, you need to discuss this strategy with your network admin for other options.

I hope this saves you some time. I am also eager to hear what the community thinks of this. Maybe there is a better way of handling this situation. If so, please share it here.

Thanks.

Daniel D.

like image 176
dpdragnev Avatar answered Sep 19 '22 15:09

dpdragnev