Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Amir 75 posts 224 karma points
    Oct 16, 2015 @ 15:33
    Amir
    0

    Umbraco 7.3 - Membership provider issue

    I just upgraded from 7.2.6 to 7.3 and by running solution I get following error:

    " Unable to cast object of type 'System.Web.Security.ActiveDirectoryMembershipProvider' to type 'Umbraco.Core.Security.UmbracoMembershipProviderBase'. "

    enter image description here

    Here is my membership provider section in web.config:

    <membership defaultProvider="MyADMembershipProvider" userIsOnlineTimeWindow="15">
      <providers>
        <!--  THIS IS THE NEW PROVIDER -->
        <add name="MyADMembershipProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0,   Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ADConnectionString" connectionUsername="******" connectionPassword="******" attributeMapUsername="********" />
        <!--  END OF AD PROVIDER CODE -->
      </providers>
    </membership>
    

    And "ADConnectionString" is as below:

    <add name="ADConnectionString" connectionString="LDAP://XXXXXXXX/ou=XXXXXXX,dc=XXXXX,dc=XX,dc=XX" />
    

    AD Membership provider is working like a charm with exact same configs in Umbraco 7.2.6.

    I tried to upgrade both from Nuget and manual but neither works.

    Am I missing anything?

  • Lokesh kumar Chippada 44 posts 249 karma points
    Oct 19, 2015 @ 08:33
    Lokesh kumar Chippada
    0

    same issue for me, Any fix for this?

    Please let me know

  • Amir 75 posts 224 karma points
    Oct 19, 2015 @ 08:55
    Amir
    0

    Not yet Kumar.

  • Lokesh kumar Chippada 44 posts 249 karma points
    Oct 19, 2015 @ 09:05
    Lokesh kumar Chippada
    0

    hopefully someone will come up with a solution :)

  • Christer Forsberg 1 post 21 karma points
    Oct 19, 2015 @ 13:25
    Christer Forsberg
    0

    Same issue here, but I use my own members and users membership provider. For me it's the users membership provider that crashes, but with exact same error message and line of code. No errors in UmbracoTraceLog.txt. I upgrade from 7.2.8 to 7.3.0.

  • Comment author was deleted

    Oct 19, 2015 @ 15:17

    As far as I can tell, membership providers are no longer being used for authentication in 7.3+:

    http://issues.umbraco.org/issue/U4-7032#comment=67-23307

    It's a lengthy read.

    TL;DR Edition:

    You'll need to implement a custom user store.

    http://issues.umbraco.org/issue/U4-7032#comment=67-23397

  • Amir 75 posts 224 karma points
    Oct 20, 2015 @ 11:13
    Amir
    0

    Thanks Kevin,

    I used your and Brad's sample and it works now for me :)

  • Amir 75 posts 224 karma points
    Oct 20, 2015 @ 11:24
    Amir
    1

    As the original thread is a bit too long, here is a brief solution for this problem:

    1- In your web.config restore the membership configs to it's default. Mine was as below:

        <membership defaultProvider="UmbracoMembershipProvider" userIsOnlineTimeWindow="15">
      <providers>
        <clear />
        <add name="UmbracoMembershipProvider" type="Umbraco.Web.Security.Providers.MembersMembershipProvider, Umbraco" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="8" useLegacyEncoding="true" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" defaultMemberTypeAlias="Member" passwordFormat="Hashed" />
        <add name="UsersMembershipProvider" type="Umbraco.Web.Security.Providers.UsersMembershipProvider, Umbraco" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="8" useLegacyEncoding="true" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" passwordFormat="Hashed" />
      </providers>
    </membership>
    

    2- Restore the DefaultBackofficeProvider in "umbracoSettings.config" to it's default

    <DefaultBackofficeProvider>UsersMembershipProvider</DefaultBackofficeProvider>  
    

    3- Create an Owinstartup class if you dont have any or add followings to your startup class:

    public class MyOwinStartup
    {
        public void Configuration(IAppBuilder app)
        {
            var applicationContext = ApplicationContext.Current;
    
            app.ConfigureUserManagerForUmbracoBackOffice<BackOfficeUserManager, BackOfficeIdentityUser>(
                ApplicationContext.Current,
                (options, context) =>
                {
                    var membershipProvider = MembershipProviderExtensions.GetUsersMembershipProvider().AsUmbracoMembershipProvider();
    
                    var store = new BackOfficeUserStore(
                                applicationContext.Services.UserService,
                                applicationContext.Services.ExternalLoginService,
                                membershipProvider);
    
                    return MyUserManager.InitUserManager(new MyUserManager(store), membershipProvider, options);
                });
    
            app.UseUmbracoBackOfficeCookieAuthentication(ApplicationContext.Current)
                .UseUmbracoBackOfficeExternalCookieAuthentication(ApplicationContext.Current);
        }
    }
    

    4- Create a custom Usermanager class as below:

    public class MyUserManager : BackOfficeUserManager
    {
        public MyUserManager(IUserStore<BackOfficeIdentityUser, int> store)
            : base(store)
        {
        }
    
        public override Task<bool> CheckPasswordAsync(BackOfficeIdentityUser user, string password)
        {
            // Validations coming here
            bool ret = LdapAuth(user.Name, password);
            return Task.FromResult(ret);
        }
    
        private bool LdapAuth(string username, string password)
        {
            bool resp = false;
            try
            {
                string ldapRoot = ConfigurationManager.ConnectionStrings["ADConnectionString"].ConnectionString;
                var entry = new DirectoryEntry(ldapRoot, username, password);
                try
                {
                    var search = new DirectorySearcher(entry) {Filter = "(SAMAccountName=" + username + ")"};
                    search.PropertiesToLoad.Add("cn");
                    SearchResult result = search.FindOne();
                    if (result != null)
                    {
                        // Login was successful
                        resp = true;
                    }
                }
                catch (Exception ex)
                {
                    // Login was invalid
                }
            }
            catch (Exception ex)
            {
                // Login was invalid
            }
            return resp;
        }
    
        public static MyUserManager InitUserManager(MyUserManager manager, MembershipProviderBase membershipProvider, IdentityFactoryOptions<BackOfficeUserManager> options)
        {
            // Configure validation logic for usernames
            manager.UserValidator = new UserValidator<BackOfficeIdentityUser, int>(manager)
            {
                AllowOnlyAlphanumericUserNames = false,
                RequireUniqueEmail = true
            };
    
            // Configure validation logic for passwords
            manager.PasswordValidator = new PasswordValidator
            {
                RequiredLength = membershipProvider.MinRequiredPasswordLength,
                RequireNonLetterOrDigit = membershipProvider.MinRequiredNonAlphanumericCharacters > 0,
                RequireDigit = false,
                RequireLowercase = false,
                RequireUppercase = false
            };
    
            //use a custom hasher based on our membership provider
            //THIS IS AN INTERNAL METHOD WHICH I PULL OUT INTO A CLASS BELOW
            //THIS SHOULD NOT BE NECESSARY IN v7.3.1
            manager.PasswordHasher = new MembershipPasswordHasher(membershipProvider);
    
            var dataProtectionProvider = options.DataProtectionProvider;
            if (dataProtectionProvider != null)
            {
                manager.UserTokenProvider = new DataProtectorTokenProvider<BackOfficeIdentityUser, int>(dataProtectionProvider.Create("ASP.NET Identity"));
            }
    
            manager.UserLockoutEnabledByDefault = true;
            manager.MaxFailedAccessAttemptsBeforeLockout = membershipProvider.MaxInvalidPasswordAttempts;
            //NOTE: This just needs to be in the future, we currently don't support a lockout timespan, it's either they are locked
            // or they are not locked, but this determines what is set on the account lockout date which corresponds to whether they are
            // locked out or not.
            manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromDays(30);
    
            //custom identity factory for creating the identity object for which we auth against in the back office
            manager.ClaimsIdentityFactory = new BackOfficeClaimsIdentityFactory();
    
            return manager;
        }
    }
    
    internal class MembershipPasswordHasher : IPasswordHasher
    {
        private readonly MembershipProviderBase _provider;
    
        public MembershipPasswordHasher(MembershipProviderBase provider)
        {
            _provider = provider;
        }
    
        public string HashPassword(string password)
        {
            return _provider.HashPasswordForStorage(password);
        }
    
        public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
        {
            return _provider.VerifyPassword(providedPassword, hashedPassword)
                ? PasswordVerificationResult.Success
                : PasswordVerificationResult.Failed;
        }
    }
    

    Note: Add System.DirectoryServices to your references and also to web.config under assemblies

    <add assembly="System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/> 
    
  • Lokesh kumar Chippada 44 posts 249 karma points
    Oct 22, 2015 @ 09:47
    Lokesh kumar Chippada
    0

    Thanks Kevin and Amir..

  • Shaun McNicholas 4 posts 73 karma points
    Nov 03, 2015 @ 23:16
    Shaun McNicholas
    0

    I have implemented this on top of a pre-existing 7.2.6 installation - upgraded everything to 7.3.1 and built out everything according to this setup including the classes etc... and it appears that everything is now working correctly.

    However I am still unable to log in using my credentials. And in the trace log I get the following:

    2015-11-03 17:59:55,286 [P16684/D20/T680] INFO Umbraco.Core.Security.BackOfficeSignInManager - Event Id: 0, state: Login attempt failed for username patrick-mcnicholas from IP address 127.0.0.1, the user is locked

    Is there something additional that I need to do to get the users unlocked in the database maybe?

  • Shaun McNicholas 4 posts 73 karma points
    Nov 03, 2015 @ 23:20
    Shaun McNicholas
    0

    This is my configuration: The owinstartup.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.DirectoryServices;
    using Umbraco.Web.Security.Identity;
    using Umbraco.Core.Security;
    
    public class MyOwinStartup
    {
        public void Configuration(Owin.IAppBuilder app)
        {
            var applicationContext = Umbraco.Core.ApplicationContext.Current;
    
            app.ConfigureUserManagerForUmbracoBackOffice<Umbraco.Core.Security.BackOfficeUserManager, Umbraco.Core.Models.Identity.BackOfficeIdentityUser>(
                Umbraco.Core.ApplicationContext.Current,
                (options, context) =>
                {
                    var membershipProvider = MembershipProviderExtensions.GetUsersMembershipProvider().AsUmbracoMembershipProvider();
    
                    var store = new BackOfficeUserStore(
                                applicationContext.Services.UserService,
                                applicationContext.Services.ExternalLoginService,
                                membershipProvider);
    
                    return MyUserManager.InitUserManager(new MyUserManager(store), membershipProvider, options);
                });
    
            app.UseUmbracoBackOfficeCookieAuthentication(Umbraco.Core.ApplicationContext.Current)
                .UseUmbracoBackOfficeExternalCookieAuthentication(Umbraco.Core.ApplicationContext.Current);
        }
    }
    

    Then the usermanager.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.DirectoryServices;
    using Umbraco.Core.Security;
    using Microsoft.AspNet.Identity;
    using Microsoft.AspNet.Identity.Owin;
    using Umbraco.Core.Models.Identity;
    using System.Threading.Tasks;
    using System.Configuration;
    
    public class MyUserManager : BackOfficeUserManager
    {
        public MyUserManager(IUserStore<BackOfficeIdentityUser, int> store)
            : base(store)
        {
        }
    
        public override Task<bool> CheckPasswordAsync(BackOfficeIdentityUser user, string password)
        {
            // Validations coming here
            bool ret = LdapAuth(user.Name, password);
            return Task.FromResult(ret);
        }
    
        private bool LdapAuth(string username, string password)
        {
            bool resp = false;
            try
            {
                string ldapRoot = ConfigurationManager.ConnectionStrings["ADConnectionString"].ConnectionString;
                var entry = new DirectoryEntry(ldapRoot, username, password);
                try
                {
                    var search = new DirectorySearcher(entry) { Filter = "(SAMAccountName=" + username + ")" };
                    search.PropertiesToLoad.Add("cn");
                    SearchResult result = search.FindOne();
                    if (result != null)
                    {
                        // Login was successful
                        resp = true;
                    }
                }
                catch (Exception ex)
                {
                    // Login was invalid
                }
            }
            catch (Exception ex)
            {
                // Login was invalid
            }
            return resp;
        }
    
        public static MyUserManager InitUserManager(MyUserManager manager, MembershipProviderBase membershipProvider, IdentityFactoryOptions<BackOfficeUserManager> options)
        {
            // Configure validation logic for usernames
            manager.UserValidator = new UserValidator<BackOfficeIdentityUser, int>(manager)
            {
                AllowOnlyAlphanumericUserNames = false,
                RequireUniqueEmail = true
            };
    
            // Configure validation logic for passwords
            manager.PasswordValidator = new PasswordValidator
            {
                RequiredLength = membershipProvider.MinRequiredPasswordLength,
                RequireNonLetterOrDigit = membershipProvider.MinRequiredNonAlphanumericCharacters > 0,
                RequireDigit = false,
                RequireLowercase = false,
                RequireUppercase = false
            };
    
            //use a custom hasher based on our membership provider
            //THIS IS AN INTERNAL METHOD WHICH I PULL OUT INTO A CLASS BELOW
            //THIS SHOULD NOT BE NECESSARY IN v7.3.1
            manager.PasswordHasher = new MembershipPasswordHasher(membershipProvider);
    
            var dataProtectionProvider = options.DataProtectionProvider;
            if (dataProtectionProvider != null)
            {
                manager.UserTokenProvider = new DataProtectorTokenProvider<BackOfficeIdentityUser, int>(dataProtectionProvider.Create("ASP.NET Identity"));
            }
    
            manager.UserLockoutEnabledByDefault = true;
            manager.MaxFailedAccessAttemptsBeforeLockout = membershipProvider.MaxInvalidPasswordAttempts;
            //NOTE: This just needs to be in the future, we currently don't support a lockout timespan, it's either they are locked
            // or they are not locked, but this determines what is set on the account lockout date which corresponds to whether they are
            // locked out or not.
            manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromDays(30);
    
            //custom identity factory for creating the identity object for which we auth against in the back office
            manager.ClaimsIdentityFactory = new BackOfficeClaimsIdentityFactory();
    
            return manager;
        }
    }
    
    internal class MembershipPasswordHasher : IPasswordHasher
    {
        private readonly MembershipProviderBase _provider;
    
        public MembershipPasswordHasher(MembershipProviderBase provider)
        {
            _provider = provider;
        }
    
        public string HashPassword(string password)
        {
            return _provider.HashPasswordForStorage(password);
        }
    
        public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
        {
            return _provider.VerifyPassword(providedPassword, hashedPassword)
                ? PasswordVerificationResult.Success
                : PasswordVerificationResult.Failed;
        }
    }
    

    Then this is in the WebConfig: Of course pointing to my corpldap server using the same string that worked in my 7.2.6 installation

    <add name="ADConnectionString" connectionString="LDAP://xxx.xxx.xxx:389/DC=xxx,DC=xxx,DC=xxx" />
    
  • Ben McKean 272 posts 549 karma points
    Jan 25, 2016 @ 15:16
    Ben McKean
    0

    I appreciate this is an older post but I have a general question about authenticating with AD for the back office. Do the users need to existing in Umbraco for authentication to work?

    I was hoping that an AD user could just log into Umbraco with their AD credentials and it just work however I've just tested it and and it appears the CheckPasswordAsync method is not hit unless the user already existings in Umbraco.

    Maybe its wishful thinking but would be great if all AD users of a AD particular role (group) were able to log into Umbraco.

  • Comment author was deleted

    Jan 25, 2016 @ 15:51

    @Ben,

    Yes I believe this only works if the user is in Umbraco.

    The user needs to get it's 'settings' from the user profile stored in Umbraco.

    i.e.

    • User type (admin, editor)
    • Content start node
    • Media start node
    • Sections they have access to

    Not sure AD can provide the above very easily (if at all).

  • Ben McKean 272 posts 549 karma points
    Jan 25, 2016 @ 18:39
    Ben McKean
    0

    Thanks Kevin.

    So I suppose the only way I can achieve what I'd like would be to create a member on a successful AD login if they don't already exist and populate their settings.

    What I'm trying to avoid is when a new user is added in AD that needs to be able to access Umbraco, another step in adding them to Umbraco

  • Jay 415 posts 641 karma points
    May 03, 2016 @ 11:59
    Jay
    0

    Hi All,

    I'm running on Umbraco 7.4.3 fresh copy and I've got all the setup codes from

    Shaun McNicholas above, I'm able to login using my usual Umbraco login.

    How do I login using an AD user login instead? Do i first have to create a User with my Umbraco Admin account to be the same username as the AD?

    Eg my AD username will be Tester22, do i need to create a user within Umbraco with the username Tester22?

    Thanks

  • Nadia 45 posts 122 karma points
    May 12, 2016 @ 21:37
    Nadia
    0

    Hi Everyone,

    I have implemented the solution and it works, but only if the user already exists in umbraco.

    In our old AD provider we also had logic in the validate User, that if the User didn't exist in Umbraco, it looked up the AD details and created the user.

    I can't quite work out where Umbraco is checking that the user exists. Can anyone recomend a way to fix this?

  • Ben McKean 272 posts 549 karma points
    May 13, 2016 @ 07:34
    Ben McKean
    0

    Hi Nadia,

    I believe this is what you're looking for:

    https://our.umbraco.org/documentation/Reference/Security/

    As I was adapting an existing site, I did the following which called my original ValidateUser method, and it was in my ValidateUser method where I created the user in Umbraco if they didn't already exist

    Or alternatively you could do your check in here to create a new user if the user isn't found in Umbraco

    So new code to check users password was:

    public Task<BackOfficeUserPasswordCheckerResult> CheckPasswordAsync(BackOfficeIdentityUser user, string password)
    {
        var adProvider = Membership.Providers["ActiveDirectoryUmbracoUserMembershipProvider"];
    
        // check the password using the membership provider
        var result = adProvider != null && adProvider.ValidateUser(user.UserName, password) ? Task.FromResult(BackOfficeUserPasswordCheckerResult.ValidCredentials) : Task.FromResult(BackOfficeUserPasswordCheckerResult.InvalidCredentials);
    
        return result;
    }
    

    and my original code to create a user in the ValiderUser method in my class inheriting from ActiveDirectoryMembershipProvider was (some of this contains calls to my own methods but hopefully you get the idea):

    public override bool ValidateUser(string username, string password)
    {
        var retval = base.ValidateUser(username, password);
    
        // Find the Active Directory member
        int userCount = 0;
        string[] usernameArray = { username };
        var adSearchResult = DirectorySearcher.FindUsersByAccountNames(usernameArray, out userCount);
        if (!adSearchResult.Any())
        {
            return false;
        }
        var adMemberAsUserModel = adSearchResult.First();
    
        //var adMember = GetUser(username, false);
        if (adMemberAsUserModel == null)
            return false;
    
        // Login has to be valid and we need to have Umbraco 
        // member's membership provider present
        if (retval && Membership.Providers[Constants.Conventions.Member.UmbracoMemberProviderName] != null)
        {
            var staffModel = StaffProfile.FromUserModel(adMemberAsUserModel);
    
            try
            {
                UmbracoHelpers.UpdateUmbracoMemberFromStaffProfile(staffModel);
    
                return true;
            }
            catch (Exception ex)
            {
                LogHelper.Error<ActiveDirectoryUmbracoMembersMembershipProvider>("Error while creating AD User as an Umbraco Member.", ex);
            }
        }
        // No matter if retval is true or false, we have to return
        // false because we can't create or make sure the ad user 
        // exists as an Umbraco member
        return false;
    }
    
  • Rajiv Mehara 1 post 71 karma points
    Jun 27, 2016 @ 14:23
    Rajiv Mehara
    0

    Hi Ben,

    Can you please provide more details to create user in Umbraco.

    I have tried your approach but i am not able to create new user in umbraco.

    If user is already added in Umbraco database then it works.

  • Brad Wickett 43 posts 124 karma points
    Sep 19, 2016 @ 17:40
    Brad Wickett
    0

    I'm using this same type of code in our environment, but I have noticed that the Preview feature is not working. If I switch back to the default appStartup of UmbracoDefaultOwinStartup the Preview feature works again. It seems that something about the current user environment is not getting set when using this form of authentication. Is anyone else experiencing this, or found a workaround for this?

  • Brad Wickett 43 posts 124 karma points
    Sep 19, 2016 @ 19:49
    Brad Wickett
    0

    After a day of troubleshooting I think I've answered my own question. In the public void Configuration(IAppBuilder app) function where I specify my IBackOfficeUserPasswordChecker password checker function, I needed to first set the following app options:

    app .UseUmbracoBackOfficeCookieAuthentication(ApplicationContext.Current) .UseUmbracoBackOfficeExternalCookieAuthentication(ApplicationContext.Current) .UseUmbracoPreviewAuthentication(ApplicationContext.Current);

    All the examples I had seen omitted the last option, which prevented the preview from working.

  • Ankur parekh 5 posts 74 karma points
    Nov 02, 2016 @ 14:51
    Ankur parekh
    0

    Hey Ben, Can you please share your code? I have tried your code but it's not working on my site. I want my site to be auto-login if the user doesn't exists in Umbraco and exists in Active Directory it should create the user automatically in Umbraco. Thanks in Advance!

  • Tom 119 posts 447 karma points
    Mar 13, 2017 @ 13:19
    Tom
    0

    Did anyone find a solution for this?

Please Sign in or register to post replies

Write your reply to:

Draft