Implement Active Directory login in an existing ASP.NET MVC 4.6 web project

Implement Active Directory login in an existing ASP.NET MVC 4.6 web project



I have to change the existing (Windows) login for my ASP.NET MVC + Knockout application with Active Directory authentication.
It consists of mvc controllers and webapi controllers. Both have to be authenticated.



I thought I would do this by changing to forms authentication and create a login page and when the users clicks login, query Active Directory with the System.DirectoryServices.DirectoryEntry.
Then the other processes like change password, register etc. would also get a custom html page and do their actions via the System.DirectoryServices.DirectoryEntry on our Active Directory.


forms authentication


System.DirectoryServices.DirectoryEntry


System.DirectoryServices.DirectoryEntry



(that is, I could not find any other way that people would do it, and I did find some who would do it like this, and it sounds the same like previous forms authentications I've seen. In this case the user/passwords would not be in a database table but in Active Directory. Same idea, swap database table by active directory).


forms authentication



To see how this would be on a brandnew project, I created a new ASP.NET MVC project and choose 'work- or school acounts' (which says 'for applications that authenticate users with active directory) and choose 'on premise'.
However, then I have to provide these items:



I don't know what to do with that. The only thing I have is an active directory url like ldap://etc..


ldap://etc.



Is this another/newer/better way of doing active directory login? Or the only right one (is forms authentication wrong?) or the wrong one?



I'm confused.





What do you mean by "I have to change the existing (Windows) login for my ASP.NET MVC + Knockout application with Active Directory authentication". For me, they both are same, as windows authentication is carried out by AD.
– r2018
Sep 1 at 10:32




3 Answers
3



I have used the following approach in order to Implement Active Directory Authentication in ASP.NET MVC 5 and that is working like a charm:


ASP.NET MVC 5



Step 1: Modify the Login methods in the AccountController as shown below (also add the necessary references):


AccountController


[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)

try

if (!ModelState.IsValid)

return View(model);


// Check if the User exists in LDAP
if (Membership.GetUser(model.UserName) == null)

ModelState.AddModelError("", "Wrong username or password");
return this.View(model);


ApplicationGroupManager groupManager = new ApplicationGroupManager();

// Validate the user using LDAP
if (Membership.ValidateUser(model.UserName, model.Password))

FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
// FormsAuthentication.SetAuthCookie(model.UserName, false);

// Check if the User exists in the ASP.NET Identity table (AspNetUsers)
string userName = model.UserName.ToString().ToLower(new CultureInfo("en-US", false)); // When UserName is entered in uppercase containing "I", the user cannot be found in LDAP
//ApplicationUser user = UserManager.FindByName(userName);
ApplicationUser user = await UserManager.FindByNameAsync(userName); //Asynchronous method

if (user == null) // If the User DOES NOT exists in the ASP.NET Identity table (AspNetUsers)

// Create a new user using the User data retrieved from LDAP
// Create an array of properties that we would like and add them to the search object
string requiredProperties = new string "samaccountname", "givenname", "sn", "mail", "physicalDeliveryOfficeName", "title" ;
var userInfo = CreateDirectoryEntry(model.UserName, requiredProperties);

user = new ApplicationUser();

// For more information about "User Attributes - Inside Active Directory" : http://www.kouti.com/tables/userattributes.htm
user.UserName = userInfo.GetDirectoryEntry().Properties["samaccountname"].Value.ToString();
user.Name = userInfo.GetDirectoryEntry().Properties["givenname"].Value.ToString();
user.Surname = userInfo.GetDirectoryEntry().Properties["sn"].Value.ToString();
user.Email = userInfo.GetDirectoryEntry().Properties["mail"].Value.ToString();
user.EmailConfirmed = true;
//user.PasswordHash = null;
//user.Department = GetDepartmentId(userInfo.GetDirectoryEntry().Properties["physicalDeliveryOfficeName"].Value.ToString());

//await Register(user);
var result = await UserManager.CreateAsync(user); //Asynchronous method

//If the User has succesfully been created
//if (result.Succeeded)
//
// //var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// //var callbackUrl = Url.Action("ConfirmEmail", "Account", new userId = user.Id, code = code , protocol: Request.Url.Scheme);
// //await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking this link: <a href="" + callbackUrl + "">link</a>");
// //ViewBag.Link = callbackUrl;
// //return View("DisplayEmail");
//

// Define user group (and roles)
var defaultGroup = "751b30d7-80be-4b3e-bfdb-3ff8c13be05e"; // Id of the ApplicationGroup for the Default roles
//groupManager.SetUserGroups(newUser.Id, new string defaultGroup );
await groupManager.SetUserGroupsAsync(user.Id, new string defaultGroup ); //Asynchronous method
//groupManager.SetGroupRoles(newGroup.Id, new string role.Name );

// !!! THERE IS NO NEED TO ASSIGN ROLES AS IT IS ASSIGNED AUTOMATICALLY IN ASP.NET Identity 2.0
//else // If the User exists in the ASP.NET Identity table (AspNetUsers)
//
// //##################### Some useful ASP.NET Identity 2.0 methods (for Info) #####################
// //ApplicationGroupManager gm = new ApplicationGroupManager();
// //string roleName = RoleManager.FindById("").Name; // Returns Role Name by using Role Id parameter
// //var userGroupRoles = gm.GetUserGroupRoles(""); // Returns Group Id and Role Id by using User Id parameter
// //var groupRoles = gm.GetGroupRoles(""); // Returns Group Roles by using Group Id parameter
// //string groupRoleNames = groupRoles.Select(p => p.Name).ToArray(); // Assing Group Role Names to a string array
// //###############################################################################################

// // Assign Default ApplicationGroupRoles to the User
// // As the default roles are already defined to the User after the first login to the system, there is no need to check if the role is NULL (otherwise it must be checked!!!)
// //var groupRoles = groupManager.GetGroupRoles("751b30d7-80be-4b3e-bfdb-3ff8c13be05e"); // Returns Group Roles by using Group Id parameter
// var groupRoles = await groupManager.GetGroupRolesAsync("751b30d7-80be-4b3e-bfdb-3ff8c13be05e"); // Returns Group Roles by using Group Id parameter (Asynchronous method)

// foreach (var role in groupRoles)
//
// //Assign ApplicationGroupRoles to the User
// string roleName = RoleManager.FindById(role.Id).Name;
// UserManager.AddToRole(user.Id, roleName);
//
//

//Sign in the user
await SignInAsync(user, model.RememberMe);

if (this.Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\"))

return this.Redirect(returnUrl);
//return RedirectToLocal(returnUrl);

return this.RedirectToAction("Index", "Home");

else

ModelState.AddModelError("", "Wrong username or password");
return this.View(model);


catch (Exception ex)

TempData["ErrorMessage"] = ex.Message.ToString();
return View("Error", TempData["ErrorMessage"]);



/* Since ASP.NET Identity and OWIN Cookie Authentication are claims-based system, the framework requires the app to generate a ClaimsIdentity for the user.
ClaimsIdentity has information about all the claims for the user, such as what roles the user belongs to. You can also add more claims for the user at this stage.
The highlighted code below in the SignInAsync method signs in the user by using the AuthenticationManager from OWIN and calling SignIn and passing in the ClaimsIdentity. */
private async Task SignInAsync(ApplicationUser user, bool isPersistent)

AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() IsPersistent = isPersistent , identity);


static SearchResult CreateDirectoryEntry(string sAMAccountName, string requiredProperties)

DirectoryEntry ldapConnection = null;

try

// Create LDAP connection object
//ldapConnection = new DirectoryEntry("alpha.company.com");
ldapConnection = new DirectoryEntry("LDAP://OU=Company_Infrastructure, DC=company, DC=mydomain", "******", "******");
//ldapConnection.Path = connectionPath;
ldapConnection.AuthenticationType = AuthenticationTypes.Secure;

DirectorySearcher search = new DirectorySearcher(ldapConnection);
search.Filter = String.Format("(sAMAccountName=0)", sAMAccountName);

foreach (String property in requiredProperties)
search.PropertiesToLoad.Add(property);

SearchResult result = search.FindOne();
//SearchResultCollection searchResultCollection = search.FindAll();

if (result != null)

//foreach (String property in requiredProperties)
// foreach (Object myCollection in result.Properties[property])
// Console.WriteLine(String.Format("0,-20 : 1",
// property, myCollection.ToString()));
// return searchResultCollection;
return result;

else

return null;
//Console.WriteLine("User not found!");

//return ldapConnection;

catch (Exception e)

Console.WriteLine("Exception caught:nn" + e.ToString());

return null;



Note: In order to force signout in LDAP authentication, add FormsAuthentication.SignOut() line to the LogOff() method as shown below:


LDAP


FormsAuthentication.SignOut()


LogOff()


[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()

AuthenticationManager.SignOut();
FormsAuthentication.SignOut(); //In order to force logout in LDAP authentication
return RedirectToAction("Login", "Account");




Step 2: Update your LoginViewModel (or whatever your Account model class is named) to contain only this LoginModel class:


LoginViewModel


LoginModel


public class LoginViewModel

[Required]
public string UserName get; set;

[Required]
[EmailAddress]
public string Email get; set;

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

public bool RememberMe get; set;



On the other hand, add the custom properties i.e. Name, Surname, UserName, Department, etc. to the necessary model i.e. ApplicationUser, RegisterViewModel.


ApplicationUser


RegisterViewModel






Step 3: Finally, update your Web.config file to include these elements:


Web.config




<connectionStrings>
<!-- for LDAP -->
<add name="ADConnectionString" connectionString="LDAP://**.**.***:000/DC=abc,DC=xyz" />
</connectionStrings>

<system.web>
<!-- For LDAP -->
<httpCookies httpOnlyCookies="true" />
<authentication mode="Forms">
<forms name=".ADAuthCookie" loginUrl="~/Account/Login" timeout="30" slidingExpiration="true" protection="All" />
</authentication>
<membership defaultProvider="ADMembershipProvider">
<providers>
<clear />
<add name="ADMembershipProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider" connectionStringName="ADConnectionString" attributeMapUsername="sAMAccountName" connectionUsername="******" connectionPassword="******" />
</providers>
</membership>

...
</system.web>







Hope this helps...





Thanks. So you use the forms authentication way. Is that recently decided?
– Michel
Aug 27 at 11:16





@Michel It depends on your requirements. I have used Form and LDAP Authentication, bot of them works fine. Please note that I also added some extra methods that are so useful ;) I hope this works for you as well. I am waiting for result :)
– Murat Yıldız
Aug 27 at 13:32




I have used Active Directory Authetication at work.
I created application MVC with Windows Authetication and it was done. Application automatically displays AD login with domain.
Choose membership: [Authorize(Roles=@"DomainNameGroupName")]
Ye can see domain and groups in cmd: net user Username /domain
You haven't to use LDAP.


Active Directory Authetication


MVC


Windows Authetication


AD


[Authorize(Roles=@"DomainNameGroupName")]


LDAP



With LDAP see:
see here


LDAP





From Review: Would you please edit your answer and improve the explanation of your solution? I'm having a bad time trying to understand it... Thanks
– sɐunıɔןɐqɐp
Aug 26 at 13:19





Hi @mrsWhite, What do you mean with "Application automatically display AD login with domain". Is that a login window, or a default browser popup?
– Michel
Aug 27 at 11:15





If you use AD, you don't need to log in in Internet explorer but you must enable AvctiveX in IE settings. In Chrome you don't need enable activex
– mrsWhite
Aug 28 at 7:07



i have a mixed system running. Users from a Database (external users) mixed with AD users (internal users) that can login to our system. to communicate with the AD i use a nuget package called LinqToLdap (https://github.com/madhatter22/LinqToLdap). this uses the LDAP protocol so it can be used for authenticating against Unix Ldap servers also.



this is the Authentication method


public bool AuthenticateUser(string userName, string password)

InitConfig();
using (var context = new DirectoryContext(_config))

var user = context.Query<LdapUserInfo>().FirstOrDefault(x => x.UserPrincipalName.Equals(userName));
var dn = user?.DistinguishedName;
if (string.IsNullOrWhiteSpace(dn))
return false;

using (var ldap = new LdapConnection(new LdapDirectoryIdentifier(_myConfig.Server)))

ldap.SessionOptions.ProtocolVersion = 3;

ldap.AuthType = AuthType.Basic;
ldap.Credential = _credentials;
ldap.Bind();

try

ldap.AuthType = AuthType.Basic;
ldap.Bind(new NetworkCredential(dn, password));
return true;

catch (DirectoryOperationException)

catch (LdapException)



return false;



private void InitConfig()

if (_config != null)
return;

_config = new LdapConfiguration();
_credentials = new NetworkCredential(_myConfig.Username, _myConfig.Password, _myConfig.Domain);
_config.AddMapping(new AutoClassMap<LdapGroupInfo>(), _myConfig.NamingContext, new "*" );
_config.AddMapping(new AutoClassMap<LdapUserInfo>(), _myConfig.NamingContext, new "*" );

_config.ConfigureFactory(_myConfig.Server).AuthenticateAs(_credentials).AuthenticateBy(AuthType.Basic);



Required, but never shown



Required, but never shown






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

How do I collapse sections of code in Visual Studio Code for Windows?

ャフサォクコ ケウ,コ,ワ メ,ロスョノ゙,クネ,フムカヤヲニ,エコ゚ツ ウイオン゙ケワサネォキモュキォウイノンコチ゚メヌナイゥフュ,カヒウネェ ネ,ホノケ,ムュキ ッボーミュハ,チ ツス ィ メウイマヤ,゙ウチ ヅ ロ,ォジヌェ ャヌット ェ,マャ,チナエヒネソキツテ トホヲヲミーァ