Sitefinity 4.3 broke custom MembershipProvider
Hi,
We have a custom membership provider that used to work fine so far (up to Sitefinity 4.2 SP1), but which doesn't work anymore since we updated to Sitefinity 4.3. When trying to log into the backend, we now get the following error message: "Incorrect Username/Password Combination".
I don't know what's wrong. The GetUser method still works and returns the right result, but the ValidateUser method is never called.
Is this a known issue?
What can I do to solve or debug this?
Thanks.
Hello Thomas,
Is your membership provider inheriting from OpenAccessMembershipProvider? It's possible that the project didn't properly upgrade, so can you try upgrading it again, if you have a backup?
Also, can you try restarting the server, maybe this could help.
Hi Svetoslav,
I've encounter same problem on my custom membership provider but I solve it.
It's not a migration problem my custom membership inherit of MembershipDataProvider.
Previously when we are log-in for access to backend it's ValidateUser(string username, string password) method who was called. And now in SF4.3 it's ValidateUser(User user, string password) method.
If you just override ValidateUser(String username, String password), the problem will appear. It's probably due to an evolution on log-in system process.
Regards,
Nicolas
So, basically, the MembershipDataProvider API changed between 4.2 SP1 and 4.3.
Did I miss a footnote mentioning this API breakage?
Hello Thomas,
This was an omission in our release notes and we apologize for that, I myself wasn't aware of it until a day ago.
Again, accept our apologies for the discomfort, I'm glad that your issue got resolved.
Let me know if I can help you with something else.
Hi Svetoslav,
This new feature have a bigger impact than I think at first time.
My custom membership can create users when they tried to login with check-in an other reference table.
But with this new feature only users already created can login...
Others are blocked on login page with no hope to enter.
Regards,
Nicolas
Hi,
Indeed, as Nicolas said, Sitefinity 4.3 also broke our code to create users.
This is a showstopper for us. We were supposed to release our sites early next month, but if we can't fix this issue by then, we'll have to cancel these releases.
We'd appreciate it if you could help us ASAP.
Thanks.
Hi,
I created a bypassing but it's really a hack....
When you logged in on site the SecurityManager, now in 4.3, call ValidateUser(User user, string password) during AuthenticateUser method.
But for call this method it use GetUser(String userName) and obtains object User.
So in my override method GetUser(String userName) I created a hack to recover user password (with HttpContext.Instance.Request.Params).
With couple userName/password, I called my override method ValidateUser(String userName, String password) and I can create dynamically users.
This solution is not long-lasting. Could administrators help us?
Regards,
Nicolas
Hello guys,
Indeed, the method was changed for performance optimizations (not a change in the API). However, I cannot exactly understand what issues this could cause to your custom membership providers. Can you open support tickets and send us the implementations of your providers, so we can get a better picture of the problem?
Thank you for the understanding.
Hi Svetoslav,
The problem are I have a custom membership provider who inherit MembershipDataProvider, this provider can create user dynamically when somebody try to logon onto the site.
My provider checked user's existance and roles reading from another database.
If this user doesn't exist in Sitefinity and has roles in the other database, then the provider creates it and assigns it roles.
If user exist, provider just synchronises roles (ie: Sitefinity roles are updated using the other DB data).
Now in Sitefinity 4.3 if user doesn't exist none of ValidateUser methods are called, making it so the user will never be created automatically..... And That's the problem.
Before 4.3 :
SecurityManager methods called ValidateUser(String userName, String password) of membership provider. The user was created in the ValidateUser method if it didn't exist.
public
static
UserLoggingReason AuthenticateUser(
string
membershipProviderName,
string
userName,
string
password,
bool
persistent,
out
User user)
UserLoggingReason result;
var manager = UserManager.GetManager(membershipProviderName, SecurityManager.authTranName);
manager.Provider.SuppressSecurityChecks =
true
;
if
(manager.ValidateUser(userName, password))
user = manager.GetUser(userName);
// This exception should be impossible, but just in case.
if
(user ==
null
)
throw
new
ArgumentException(
"Invalid user name."
);
result = AuthenticateUser(user, manager, persistent);
else
result = UserLoggingReason.Unknown;
user =
null
;
manager.Provider.SuppressSecurityChecks =
false
;
return
result;
public
static
UserLoggingReason AuthenticateUser(
string
membershipProviderName,
string
userName,
string
password,
bool
persistent,
out
User user)
UserLoggingReason result;
var manager = UserManager.GetManager(membershipProviderName, SecurityManager.authTranName);
using
(
new
ElevatedModeRegion(manager))
user = manager.GetUser(userName);
if
(user ==
null
)
return
UserLoggingReason.Unknown;
if
(manager.ValidateUser(user, password))
result = AuthenticateUser(user, manager, persistent);
else
result = UserLoggingReason.Unknown;
user =
null
;
TransactionManager.CommitTransaction(SecurityManager.authTranName);
return
result;
Hi,
So, to sum things up:
We used to be able to create users in the ValidateUser method (if the user doesn't exist yet).
We can't do this anymore since Sitefinity 4.3, because ValidateUser is no longer called when the user is null. This is another breaking change in the API (I'm surprised you do not have unit tests to prevent things like this from happening...).
It was convenient to do this in the ValidateUser method, as it has the password as parameter, and we need the password to create the user. Now that we no longer enter the ValidateUser method when the user hasn't been created yet, we came up with a hack to retrieve the password ourselves (from HttpContext), then manually call ValidateUser, which is dirty.
Please suggest a better way to do this, which won't break in the future.
Thanks.
Hello Thomas,
I am not aware of your whole case, which means that I cannot suggest something which should be case-specific - there is no "general" best method for that. However, creating users on ValidateUser is not a good practice.
Regards,Hi Svetoslav,
You do not need to know the whole case, just that: we have some kind of user database for all of the apps of our company (let's call it UserDB). All applications must use UserDB to identify users (through API or membership provider). This is mandatory.
For Sitefinity sites, our strategy was that when a user logs in, check if the login / password couple exists in UserDB. If it does, check if the user exists in Sitefinity. If it doesn't exist in Sitefinity, automatically create it. Then synchronize user permissions (update Sitefinity user info from UserDB info, so that if the user rights were changed in UserDB, this is reflected on Sitefinity). Then finally log the user.
We cannot manually create all users in Sitefinity beforehand, because there are thousands of them. That's why we wanted to have an automatic creation during login. Besides, not all users may want to use these sites, so we shouldn't needlessly create Sitefinity user accounts.
Can you think of a better way to do something like this?
Thanks.
Hi Thomas,
Ok, I understand the scenario right now. Indeed - the method is not called if a user is not found in the database (when null is returned by GetUser, you enter the if check and never get to the ValidateUser method). We will inspect the code and see if we can find another suitable place where you can create the users.
However, in 4.4 SP1, which will be released soon after 4.4, all of these methods (AuthenticateUser, ValidateUser and so on) will be obsolete and Single Sign On will be available out-of-the-box, so you won't need to implement your own providers in order to use a single user database for all your websites.
We will need some time to investigate the code and see if there is a way to put the create user code in a good place. I will get back to you with the results, when we are ready.