20

I'm learning to use .NET Identity. I have to authenticate to Active Directory. For that purpose I am trying to use ActiveDirecotoryMembershipProvider. I have to:

  1. Authenticate user/password against Active Directory (AD).
  2. Check whether user is present in my own database.

I configured it in my web.config to use ActiveDirectoryMembershipProvider as the default membership provider. Then I overrode PasswordSignInAsync method in my ApplicationSignInManager class (which inherits SignInManager) as follows -

public override Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
{
    var adok = Membership.Provider.ValidateUser(userName, password);
    if (adok)
    {
        var user = UserManager.FindByName(userName);
        if (user == null)
            return Task.FromResult<SignInStatus>(SignInStatus.Failure);
        else
        {
            base.SignInAsync(user, isPersistent, shouldLockout);
            return Task.FromResult<SignInStatus>(SignInStatus.Success);
        }
    }
    else
        return Task.FromResult<SignInStatus>(SignInStatus.Failure);
}

This seems to work. But I think it's not the right way to do it. Is there a better way to achieve this?

Here is how I called the above mentioned:

var result = await SignInManager.PasswordSignInAsync(username, password, isPersistent: false, shouldLockout: false);
switch (result)
{
    case SignInStatus.Success:
        return RedirectToAction("Index", "Home");
    case SignInStatus.LockedOut:
        return View("Lockout");
    case SignInStatus.Failure:
    default:
        ModelState.AddModelError("", "Invalid login attempt.");
        return View();
}

According to the answers I got, I should not call the Membership Validate method inside PasswordSignInAsync. I agree with that. In fact, I think overriding the method is wrong as well.

It was also suggested that I use UserLogins where I would give my AD an provider ID. But the only way I can think of using this is as follows -

IList<UserLoginInfo> loginInfos = await SignInManager.UserManager.GetLoginsAsync(username);
var valid = false;
foreach(var info in loginInfos)
{
    valid = Membership.Providers[info.ProviderKey].ValidateUser(username, password);
    if (valid)
        break;
}

So, if I want to authenticate a user against multiple Providers, I can create a provider key for each of them and assign those provider keys to the users. And this code will validate the user against them. But where should I put this code? What convention should I follow?

I am not keen on coding the AD validation myself, because I think ActiveDirectoryMembershipProvider can do a better job than my own code. Also for both cases I have to add reference to System.DirectoryServices anyway.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
th1rdey3
  • 4,176
  • 7
  • 30
  • 66
  • 2
    don't just down vote, leave a comment so that i can understand your reasoning behind the down vote. – th1rdey3 Apr 29 '15 at 03:44
  • I think maybe you were down voted as there does not seem to be a clear problem to your post. You have code that works, but want suggestions on other avenues. This would be better suited to the Code Review portion of the site: http://codereview.stackexchange.com/ – Pynt May 06 '15 at 13:46
  • Why? I have clearly stated what I want in my question. I may have code that works but that doesn't mean it's correct. – th1rdey3 May 06 '15 at 18:21
  • have a look at this one http://stackoverflow.com/questions/13767439/using-windows-authentication-with-active-directory-groups-as-roles/17226421#17226421 – ozhug May 07 '15 at 05:34
  • @ozhug, I have seen this. It uses windows authentication. But I need forms authentication in two levels as I mentioned in my question. – th1rdey3 May 07 '15 at 05:44

2 Answers2

5

You don't want to mix MembershipProviders with identity. What you most likely want to do, is treat logging into ActiveDirectory similar to how identity treats other external logins (like google/facebook).

This basically boils down to storing the AD username as a login:

userManager.AddLogin(<userId>, new UserLoginInfo("ActiveDirectory", "<ADUserName>")

If you only login via AD, then you could indeed override PasswordSignIn to explicitly validate against AD

Otherwise, you'd want this in only the specific login flow for AD, preserving the existing local password/social login functionality.

Community
  • 1
  • 1
Hao Kung
  • 28,040
  • 6
  • 84
  • 93
  • I am not sure how to configure Active Directory as External Login. Can you elaborate on that? – th1rdey3 May 11 '15 at 05:38
  • When a user is created or an existing user wants to link an AD login, you need expose this in your app UI flow. That is when you would call the AddLogin code above. – Hao Kung May 11 '15 at 05:42
  • There's no magic to configure, you just need to pick a providerID constant for AD in your app. The external login table already is setup by identity, you just need to use the feature. – Hao Kung May 11 '15 at 05:43
  • I am not using the built in database or EF. The database in use is custom. So basically I am implementing it from ground up. Where do I put the above mentioned code? – th1rdey3 May 11 '15 at 05:53
  • That's code that typically goes in the controller/page (application code) as opposed to identity code – Hao Kung May 11 '15 at 06:28
  • where should I call `Membership.Provider.ValidateUser(userName, password)` ? – th1rdey3 May 11 '15 at 09:13
  • You don't, you should just directly validate against AD yourself in code instead of using ADMembershipProvider, the link in my answer shows some relevant apis which might be useful – Hao Kung May 12 '15 at 04:18
  • Even if I code it myself where would I put that code? In the controller? or somewhere else? – th1rdey3 May 12 '15 at 04:40
  • Controller or a new method in your ApplicationUserManager probably makes the most sense – Hao Kung May 14 '15 at 15:37
0

Try it once following step, may be it will helpful you.

https://msdn.microsoft.com/en-us/library/ff650307.aspx

Ajay2707
  • 5,690
  • 6
  • 40
  • 58
  • 1
    The link you provided does not use [ASP.Net Identity](http://www.asp.net/identity/overview/getting-started/introduction-to-aspnet-identity) – th1rdey3 May 07 '15 at 05:46