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'. "
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>
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.
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
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?
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
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.
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
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?
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;
}
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?
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:
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!
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'. "
Here is my membership provider section in web.config:
And "ADConnectionString" is as below:
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?
same issue for me, Any fix for this?
Please let me know
Not yet Kumar.
hopefully someone will come up with a solution :)
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
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
Thanks Kevin,
I used your and Brad's sample and it works now for me :)
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:
2- Restore the DefaultBackofficeProvider in "umbracoSettings.config" to it's default
3- Create an Owinstartup class if you dont have any or add followings to your startup class:
4- Create a custom Usermanager class as below:
Note: Add System.DirectoryServices to your references and also to web.config under assemblies
Thanks Kevin and Amir..
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?
This is my configuration: The owinstartup.cs
Then the usermanager.cs
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
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
@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.
Not sure AD can provide the above very easily (if at all).
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
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
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?
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:
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):
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.
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?
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.
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!
Did anyone find a solution for this?
is working on a reply...