Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to integrate Identity Server with Active Directory #2636

Closed
mehdihadeli opened this issue Jan 14, 2020 · 12 comments
Closed

How to integrate Identity Server with Active Directory #2636

mehdihadeli opened this issue Jan 14, 2020 · 12 comments

Comments

@mehdihadeli
Copy link

Hi,
I want to integrate my identity server with active directory. is there any module in abp for this purpose or I have to use built-in identity server features?

@jortygu
Copy link

jortygu commented Jan 15, 2020

my solution is add WsFederation privider , then i can login in adfs login page,and then redirect to my own web application with authentication info.

context.Services.AddAuthentication()
     .AddWsFederation("adfs", "adfs", options => {
          options.MetadataAddress = "https://adfstest/federationmetadata/2007-06/federationmetadata.xml";
          options.Wtrealm = "urn:identityServer";
     });

@mehdihadeli
Copy link
Author

@jortygu I can't use ADFS, I want to use LDAP.

@hikalkan hikalkan reopened this Jan 19, 2020
@hikalkan
Copy link
Member

@mehdihadeli have you searched for this on web? You know because we are using standard AspNet Core Identity library and IDS4 for the authentication system. Maybe you can configure it in your solution. If not, we try to help.

@gterdem
Copy link
Contributor

gterdem commented Jan 19, 2020

I think @mehdihadeli wants to use LDAP userstore for authenticating the users for IdentityServer4 which will cause some problems (if @hikalkan can confirm) since users won't be stored in IdentityServer4 entities but somewhere else.

You can create a new UserStore which has some methods like bool ValidateCredentials and match the user's username and password via LDAP (like LdapEntry.Bind method).
You may also need to manually implement a new ProfileService service with IProfileService and handle the issued claims for requests.

An other option is saving all the AD users to AbpIdentityServer users (which will be done once). And if AD is the primary store which the business runs in, you can write a background worker to sync AD users and IdentityServer users.

@hikalkan
Copy link
Member

I should check this (added to milestone 2.2). If needed, we can add extension points to the Identity and Account modules (like we did for AspNet Boilerplate before).

@hikalkan hikalkan added this to the 2.2 milestone Jan 19, 2020
@hikalkan
Copy link
Member

BTW, how this is done if we would use a standard AspNet Core application with AspNet Core Identity? Anyone tried?

@mehdihadeli
Copy link
Author

mehdihadeli commented Jan 19, 2020

@hikalkan Yes, I searched about it and I decided to use LDAP for authentication my user and I want to store my AD user in a register page using .Net core identity to control permissions management for my active user in my application in future. I read your identity server module, you created a custom ResourceOwnerPasswordValidator for simple authentication. I decided to extend this validator to support both LDAP and Simple user and password authentication, Actually, I want both these features in my IDP and I want to store my users on .net core identity. Do you have any solution for my case? Maybe my design is incorrect.
These repositories link1 ,link2 are like the thing I want but not completely.
I think abp need to implement this feature in its core for integrating LDAP with identity server and .net core identity. what is your opinion @hikalkan about this implementation?

@gterdem
Copy link
Contributor

gterdem commented Feb 21, 2020

Hello @mehdihadeli
What is you current status about the issue?

@mehdihadeli
Copy link
Author

mehdihadeli commented Feb 25, 2020

@gterdem Hi, I created a custom user manager for this purpose and I replaced it with default user manager:

 context.Services.Replace(ServiceDescriptor
     .Transient<UserManager<IdentityUser>, LdapUserManager<IdentityUser>>());
public class LdapUserManager<TUser> : Microsoft.AspNetCore.Identity.UserManager<TUser>
       where TUser : IdentityUser
   {
       private readonly IEventService _events;
       private readonly ILdapManager _ldapManager;
       private readonly AbpLdapOptions _ldapOptions;

       public LdapUserManager(
           IUserStore<TUser> store, IOptions<IdentityOptions> optionsAccessor, IPasswordHasher<TUser> passwordHasher,
           IEnumerable<IUserValidator<TUser>> userValidators,
           IEnumerable<IPasswordValidator<TUser>> passwordValidators, ILookupNormalizer keyNormalizer,
           IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<TUser>> logger,
           IEventService events,
           ILdapManager ldapManager,
           IOptions<AbpLdapOptions> ldapOptions
       ) : base(
           store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services,
           logger
       )
       {
           _events = events;
           _ldapManager = ldapManager;
           _ldapOptions = ldapOptions.Value;
       }

       public override async Task<bool> CheckPasswordAsync(TUser user, string password)
       {
           if (string.IsNullOrEmpty(_ldapOptions.DomainName))
           {
               throw new InvalidOperationException("The LDAP Hostname cannot be empty or null.");
           }

           if (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(user.UserName))
           {
               throw new InvalidOperationException("The LDAP User and Password cannot be empty or null.");
           }

           var userName = string.IsNullOrWhiteSpace(_ldapOptions.DomainName)
               ? user.UserName
               : $"{user.UserName}@{_ldapOptions.DomainName}";

           var success = _ldapManager.Authenticate(userName, password);
           if (success)
           {
               Logger.LogInformation("Credentials validated for username: {username}", userName);
               await _events.RaiseAsync(new UserLoginSuccessEvent(userName, user.Id.ToString(), userName,
                   interactive: false));
               return true;
           }

           Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials",
               userName);
           await _events.RaiseAsync(new UserLoginFailureEvent(userName, "invalid credentials",
               interactive: false));
           return false;
       }
//...
}

@gterdem
Copy link
Contributor

gterdem commented Feb 25, 2020

@mehdihadeli Yeah, seems about right. Does it work for you?

@mehdihadeli
Copy link
Author

@mehdihadeli Yeah, seems about right. Does it work for you?

yes it worked correctly

@gterdem
Copy link
Contributor

gterdem commented Feb 25, 2020

@mehdihadeli Good to hear that. Closing the issue.

@gterdem gterdem closed this as completed Feb 25, 2020
@hikalkan hikalkan modified the milestones: 2.3, 2.2 Feb 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants