ValidateUser() fails on IIS but not on Development Server with SqlMembershipProvider - asp.net

I have an MVC4 project which was upgraded from MVC2. While debugging with the VS development server the user validation works fine. But when I tried to debug with Local IIS the login fails, because of a wrong password.
On the production system the user validation also works fine.
The only difference between the development server / production system and local IIS is, that the website is running in an virtual directory on local IIS. So the URLs are
on development server / production something like:
http://localhost:12345/ OR http://the.production.server/
but on local IIS it is somthing like:
http://localhost/mymvcproject/
If I move the website to the root directory on local IIS the login works fine.
In the web.config membership is configured like:
<membership>
<providers>
<add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ApplicationServices" enablePasswordReset="true" passwordFormat="Encrypted" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="50" minRequiredPasswordLength="6" passwordAttemptWindow="10" applicationName="/" />
</providers>
</membership>
I think the problem is the applicationName parameter, but I haven't changed it.
Things I checked:
the database data were always the same
to create and validate an new user does work on local IIS when in virtual directory
I tested it also with the original MVC2 project, same problem
Am I on the right path?
Is there a way make validation/password encryption/decryption independend from the URL/application name?

Is there a way make validation/password encryption/decryption
independend from the URL/application name?
No. Instead, ASP.Net Membership provider uses Machine Key (in addition to salt) to encrypt and decrypt a password.
Normally, you want to include a machine key in application's web.config explicitly to avoid that kind of saturation.
If you know the machine key, save that in Local IIS's web.config explicitly.
If you do not know the machine key, use the following method to retrieve it.
http://aspnetresources.com/blog/how_to_read_auto_generated_machinekey
Update: 7/9/2013
If you do not assign machine key explicitly, the application will use the auto-generated key by default.

I solved it. Thanks to #Win, you pushed me to the machine key. Just to make it clear, I know the machine key.
I did not see the little IsolateApps parameter in the machine key setting, which is the "key".
With a machine key configured like this:
<machineKey validationKey="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,IsolateApps" decryptionKey="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,IsolateApps" validation="AES" />
I have the behavior like I described in my question. But without the IsolateApps like this:
<machineKey validationKey="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" decryptionKey="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" validation="AES" />
Everything works fine, no matter if the application is in root or a virtual directory. But there's a catch! I you change the machine like this (not the values, only remove the IsolateApps parameter), the user validation fails for existing users. Only new created users can be validated.
The according MSDN page is http://msdn.microsoft.com/en-us/library/vstudio/w8h3skw9(v=vs.100).aspx.

Related

Membership.ValidateUser works in 1 project fails in the other

I have a simple CLI app to create/update/delete users of an SqlMembershipProvider. It works, and I can validate just fine from the CLI app.
As soon as I try to do it from the related asp.net app, however, it fails. I've literally copy/pasted the relevant web.conf/app.config sections, so I have no clue why it's failing.
<machineKey validationKey="C94FA3782AAF21E932CAA92DC2A0641951E3A76E50DD25B19C627BA01E259C6CBC7839A7803A59EF3BF855152369A6AB10CC259513BE7ACA4E842B962FD1D8A4"
decryptionKey="2EA6D270AD94361ECFDCED5070D76FD67D9A147FEEBC8388FE9B73B450A04560"
validation="SHA1"
decryption="AES" />
<membership defaultProvider="MembershipProvider">
<providers>
<add name="MembershipProvider" type="System.Web.Security.SqlMembershipProvider"
connectionStringName="ApplicationServices"
enablePasswordRetrieval="false"
enablePasswordReset="true"
requiresQuestionAndAnswer="false"
requiresUniqueEmail="false"
maxInvalidPasswordAttempts="5"
minRequiredPasswordLength="1"
minRequiredNonalphanumericCharacters="0"
passwordAttemptWindow="10"
passwordStrengthRegularExpression=""/>
</providers>
</membership>
This is in both the web.config and app.config, can someone explain why this will validate in the CLI project and not the asp.net project?
And to be clear, here is the code that isn't validationg
Membership.ValidateUser("fake", "fake") // actual test un/pwd combo
I've verified that the un/pwd is in fact correct.
Without knowing anything else about you applications, it seems that the most likely issue is the applicationName attribute in the membership/providers/add node in the config files. See http://msdn.microsoft.com/en-us/library/system.web.security.membership.applicationname.aspx
These applications are probably defaulting to separate application names/identifiers. So when you attempt to login through the web app the provider only sees access granted to the CLI app. The CLI app may not have access to the web root or virtual path info (with no http context) to create a default name so it is probably defaulting to something different than your web app applicationName.
Look at this link for a good explanation: http://weblogs.asp.net/scottgu/archive/2006/04/22/Always-set-the-_2200_applicationName_2200_-property-when-configuring-ASP.NET-2.0-Membership-and-other-Providers.aspx
You can handle this in two ways:
Try setting the applicationName to the same string in both configurations.
OR
Ensure that there are two entries for the user "fake" in the aspnet_Membership table. One for the CLI app and one for the Web app. You should find the applicationID for each application listed in the aspnet_Application table.
Here is another reference where someone used the SqlMembershipProvider outside of an asp.net app: http://mdrasel.wordpress.com/2011/02/01/asp-net-membership-provider-outside-of-web-application/. Note the use of the applicationName attribute.

ASP.NET Memberhip with custom MachineKey for multiple applications and the Security Risks

This is sort of a two part question. So I'll start with the first which will lead into the second. I'm hosting a site at GoDaddy where at the root is the main web application. However in a sub-folder I have created a second web application. Both of which use Forms Authentication backed with ASP.NET Membership on a shared database. Meaning both are using the database aspnetdb. What I'm trying to accomplish is the ability to log into one and therefore be logged into both. A Single Sign On (SSO) if you will. What I have found so far is if the applications are on the same server (IIS instance) they therefore share the same MachineKey. From what I have found and from my understanding Forms Authentication uses the MachineKey to generate the Authentication Ticket. So therefore should be no modifications necessary as long as the application name for the Membership, Role and Profile providers match.
Am I accurate so far?
But I have not been able to get this to work. So I tried two other ideas with no prevail.
I added the following to both Web.config files (separately, not together of course).
<machineKey decryptionKey="AutoGenerate"
validation="SHA1" validationKey="AutoGenerate" />
or
<machineKey
validationKey="93A258D47F48AF07AB8BE3EF56C9D32897B9C458F2E14DB6F9AA47D77E40F4CA763D4BD56C2900B507073023F4C43C583A1F7086C2DD327C879368B0449EFB10"
decryptionKey="6BE371E3CDE768B71D0D261370127BAE094984D207EFD4B55FB24384FE1795D1"
validation="SHA1" decryption="AES" />
Generated from http://aspnetresources.com/tools/machineKey
And an example Membership configuration
<membership
defaultProvider="SqlProvider"
userIsOnlineTimeWindow="20">
<providers>
<remove name="AspNetSqlProvider" />
<add name="SqlProvider"
type="System.Web.Security.SqlMembershipProvider"
connectionStringName="aspnetdb"
enablePasswordRetrieval="false"
enablePasswordReset="true"
requiresQuestionAndAnswer="true"
passwordFormat="Hashed"
applicationName="/" />
</providers>
</membership>
So my first question, will this work and am I on the right track? Because I have not been able to get this to work.
The next question is, if this works and someone were to download an application and load it to their website. And, if in the application Web.config the MachineKey is specified, like the one generated above, and they leave the default application name, like '/', could this be a security risk? Meaning if person A loaded an app and person B loaded the same app could a user authenticate on one and therefore be authenticated on the second installation. Mind you person A and person B have no relationship other than they both loaded the same application and left it with the default settings.
References:
http://msdn.microsoft.com/en-us/library/ff649308.aspx
http://help.ablecommerce.com/faqs/ablecommerce_7/how_do_i_install_to_a_shared_hosting_environment_.htm
http://rtur.net/blog/post/2009/03/30/Using-machineKey-with-ASPNET-Membership.aspx
Using one Asp.net Membership database with multiple applications Single Sign On
[Update]
- I'm determined to find the solution to this issue soon. If you are also search for a solution stay tuned...

Asp.net membership logout automatically

I recently deploy an application that uses asp.net membership (SqlMembershipProvider) and I dont know why but it automatically log out after 1 minute of inactivity. This doesn´t happen on my development environment. I even set the userIsOnlineTimeWindow to 60 which is supposed to be in minutes. Any ideas why this is happening?
Im deploying to a virtual directory on a shared hosting environment.
Here is how I set up the membership provider
<membership defaultProvider="FaceMoviesMembership" userIsOnlineTimeWindow="60">
<providers>
<clear/>
<add name="FaceMoviesMembership" type="System.Web.Security.SqlMembershipProvider" connectionStringName="FaceMoviesAuthConnectionString"
enablePasswordRetrieval="true" enablePasswordReset="true" requiresQuestionAndAnswer="false" maxInvalidPasswordAttempts="10"
passwordAttemptWindow="60" requiresUniqueEmail="false" passwordFormat="Clear" applicationName="FaceMoviesWeb"
minRequiredPasswordLength="5" minRequiredNonalphanumericCharacters="0"/>
</providers>
Found the solution on this Blog. Here's an abstract:
It is well known that you need to specify the same machine key entry when running in a web farm. This is due to the fact that things such as FormsAuthTicket or the AntiForgeryToken in ASP.NET MVC by default use the machine key values for encrypting details. Otherwise the user will be redirected back to the login form or validation will fail, when they are load balanced to a different server. This occurs when the decrypted results do not match the cookie in the case for FormsAuth. Causing the user to keep wondering why he’s having to sign-in over and over.
Having encountered this problem with 2 different web host providers, it turns out hosting providers spin up several virtual instances of your app. This recreates the web farm scenario.
As stated, your problem could be an instancing issue.
The dynamically generated machineKey, which is used for encryption/decryption/hashing, is going to be different on every machine resulting in tickets that are not recognized by different instances of your application.
Explicitly specifying a machineKey section in your web.config will ensure that all instances of your application will honor a ticket regardless of source.
Generate a machineKey section here http://www.developmentnow.com/articles/machinekey_generator.aspx
and paste it into the <system.web> section of your app so that all instances of your app will use the same encryption keys.
This may solve your problem.

Getting "SQLExpress database file auto-creation error" for site that uses AspNetSqlMembershipProvider, but connection string is to SQL Server 2005

I have an ASP.NET v2.0 website (not web application) where the root directory is public, but the "Admin" subdirectory requires authentication. Everything about the setup is very generic - I used a connection string defined in the root web.config, and the standard AspNetSqlMembershipProvider provider, and I'm using the ASP.NET Login control.
The web.config in the Admin directory specifies that the user must have the "Admin" role.
I'm using SQL Server 2008, and my connection string is good; every root level page is data driven and they all work fine.
Provider configuration seems goo.
when I log in, the Login control's "OnLoggedIn" event fires.
The last line in that event code redirects me down to my Admin/Default.aspx page. My breakpoint in "OnLoggedIn" shows me that all's well until the redirect down into my Admin directory...and then...
...and then I wait...and wait...
And then I get an error telling me it's experienced a "SQLExpress database file auto-creation error."
Why in the world is it suddenly trying to create a SQL Server Express file? Why is it suddenly ignoring my connection string?
One odd clue: Just before the last line of the "OnLoggedIn" event I put in this:
bool blnTest = User.IsInRole("Admin");
I wanted to see if blnTest = true.
What happens is the process hits this line...and waits...and eventually tells me it can't access the SQL Server Express database. It seems that any reference (either in my code, or behind the scenes) to determine the User's Role, calls the wrong database.
EDIT: Argh, sometimes it waits when I test blnTest. Other times it immediately reports the value as "false."
According to your comment, it looks like you have haven't explicitly configured a role provider for you site.
If all that's in your web.config is:
<roleManager enabled="true" />
Then you are relying on the default providers declared further up the configuration hieracrchy (machine.config, global web.config, etc)
In machine.config you've probably got something like:
<roleManager>
<providers>
<add name="AspNetSqlRoleProvider"
connectionStringName="LocalSqlServer"
applicationName="/"
type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
<add name="AspNetWindowsTokenRoleProvider"
applicationName="/"
type="System.Web.Security.WindowsTokenRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</providers>
</roleManager>
As you can see, the first provider is configured to use a connectionString called LocalSqlServer - which is also usually declared in the machine.config:
<add name="LocalSqlServer"
connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
providerName="System.Data.SqlClient"/>
And this is designed to use a local file based database that will be created if it doesn't already exist.
So to get roles working on your site, you should ammend your root web.config to something like:
<roleManager enabled="true">
<providers>
<clear />
<add name="AspNetSqlRoleProvider"
connectionStringName="YourConnectionStringName"
applicationName="/"
type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</providers>
</roleManager>
Using the <clear /> element will remove all previously defined providers for that type.
When you specify the AttachDBFilename option in the connection string you are in fact asking for your-very-own-just-in-time-provisioned SQL Server instance, aka the 'user instance'. This instance is created by asking the 'master' instance (the .\SQLEXPRESS instance) to provision a child instance, which implies copying the master/model/msdb into your profile, starting a new SQL Server process under your account configured to use the freshly copied master/model/msdb, then asking this 'child' instance to attach the specified 'file' as a new database. The details are explained in SQL Server 2005 Express Edition User Instances.
The process of creating a child instance is extremly fragile and when it breaks the DB call results eventually in a time out error when opening the connection. In your case it seems that the process breaks in some cases (when you reach the protected part of the site). Why it breaks, is very hard to guess without proper information. Look at the Common Issues in the linked article and see if any applies to you. Also check the system event log for any message why the child instances cannot start or it cannot open the MDF file. Note that a common mistake is to ask for the same physical file with AttachDBFilename under different credentials: each credential will start its own 'child' instance and only the first one will succeed in attaching the desired database.
I had the same problem which was due to the services for sql server being disabled.
Check under services.msc to see if the sqlexpress service is running. If it is check to see if you have sql express installed on your machine

Site when published doesn't allow login

Ok, I've published a site using forms authentication. It works on my staging server. it does not work on the production server. The only thing different is sql is on a different machine for production.
The site does work, showing data from sql, but when I use the login link on the site, it brings up the login form, I login, it redirects to the main page, where it still says "login" instead of "logout".
I've tried aspnet_regsql to uninstall / install forms authentication for the server. I've restarted the www service. Here's a portion of my web.config
<membership>
<providers>
<remove name="AspNetSqlMembershipProvider"/>
<add name="AspNetSqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="LocalSqlServer"
enablePasswordRetrieval="true"
enablePasswordReset="true"
requiresQuestionAndAnswer="true"
applicationName="/TriState"
requiresUniqueEmail="true"
minRequiredPasswordLength="4"
minRequiredNonalphanumericCharacters="0"
passwordFormat="Clear"
maxInvalidPasswordAttempts="5"
passwordAttemptWindow="10"
passwordStrengthRegularExpression="" />
</providers>
</membership>
How are you creating the roles and users in the production environment? Do you use a back up of the development database? In that case pay attention to the aspnet_Applications table. Make sure that the name of the application in the development and the production environment are the same. If not, try to edit the table by hand.
Is your login page by any chance named login.aspx?
I had a problem with this in the past, since it creates the same class name as the login control and a conflict occurs. In my case I got the "Yellow Screen of Death" i.e. the stack trace since it threw an exception, but then I renamed the page and it was ok.
You should explicitly set your application name. Check your database for the application table. If you have two entries there, then the system is using a different application name for your dev box and the server. If you explicitly set the name, there will be no conflict.
Is it possible that the application is serving the wrong cookies? On our testing servers the hostname is different than the production servers and the cookies end up in a different domain. The testing and production servers need to know the domains they are serving to send the right cookies back (this is done by storing the names and domains in the database).
Have you tried backing up the database from the staging server and restoring it on the production server? (here are instructions to do that.)
This will confirm that the database you are using isn't the issue.
Ok have you tried doing a manual login to bypass the membership check? This will rule out forms authentication itself being the problem.
if (Request.QueryString["ReturnUrl"] != null)
{
FormsAuthentication.RedirectFromLoginPage("someuserid", false);
}
else
{
FormsAuthentication.SetAuthCookie("someuserid", false);
Response.Redirect("~/SomePage.aspx");
}
Check your users are set up with whatever roles you have allowed in authorization.
<authorization>
<deny users="?"/>
<allow roles="Public"/>
<allow roles="AdminUser"/>
</authorization>
Check if there any web.configs further down (inheriting from higher level folder web.configs) which allow or deny any user's roles by specifying authorization.
Make sure your application name is the same in the production database:
applicationName="/TriState"

Resources