Always set the "applicationName" property when configuring ASP.NET 2.0 Membership and other Providers
I helped out a few folks last night on the ASP.NET Forums with this problem, so I thought it might make sense to turn this into a blog post to help share information about this.
Scenario:
You develop an ASP.NET 2.0 application locally using the new ASP.NET 2.0 Membership, Roles or Profile features. You create several new users and everything works fine.
You then copy the application to a remote server (or even another directory on your local server) and run the application. For some reason it appears that you are able to connect to your membership database just fine – but when you try to login it doesn’t let you. It doesn’t throw a connection error, but rather when you attempt to login you get an error message that says something like: “Login attempt unsuccessful, please try again.”
Cause:
The reason this usually happens is because a membership (or roles or profile) provider has been added in the application’s web.config file – but without an applicationName attribute being specified (assume below that the applicationName in bold was missing):
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="LocalSqlServer"
enablePasswordRetrieval="false"
enablePasswordReset="true"
requiresQuestionAndAnswer="true"
requiresUniqueEmail="false"
passwordFormat="Hashed"
maxInvalidPasswordAttempts="5"
minRequiredPasswordLength="7"
minRequiredNonalphanumericCharacters="1"
passwordAttemptWindow="10"
passwordStrengthRegularExpression=""
applicationName="/"
/>
</providers>
</membership>
When no applicationName attribute is configured, ASP.NET uses the application vroot path within the web-server to automatically calculate the applicationName to use when adding data to an ASP.NET Application Service database. To see this in action, you can open up your ASPNETDB database, and look within the aspnet_Applications table:
This table stores a unique ApplicationID for each applicationName. Because I didn’t specify an “applicationName” attribute when I registered users within my application, it calculated the application name as /website8 (which happened to be the name my dev machine was using at the time).
Users created with the membership API will then be associated with this ApplicationID and in turn the applicationName. You can see this by opening up the aspnet_Users table:
This works fine when the application continues to run in the “/WebSite8” application virtual path. But if it is copied to another location or server with a different virtual path (for example: “/app1” or more commonly just "/"), then when the Membership APIs are used they will not “see” the users already in our database – since they will lookup membership data using a different application name and filter the users in the application_Users table accordingly. That is why you’ll get a “Login attempt unsuccessful, please try again.” message when you try to login.
How to Solve This
The easiest way to solve this is to open up the aspnet_Users and aspnet_Applications tables within the ASPNETDB database and figure out what application name was used when creating the users and other data during development (look in the aspnet_Application table to work this out).
You can then go back to your web.config file, and add an “applicationName” attribute to your provider declaration with that application name value. For example, note how the applicationName value below is now the same as the one in the aspnet_Application table:
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="LocalSqlServer"
enablePasswordRetrieval="false"
enablePasswordReset="true"
requiresQuestionAndAnswer="true"
requiresUniqueEmail="false"
passwordFormat="Hashed"
maxInvalidPasswordAttempts="5"
minRequiredPasswordLength="7"
minRequiredNonalphanumericCharacters="1"
passwordAttemptWindow="10"
passwordStrengthRegularExpression=""
applicationName="/website8"
/>
</providers>
</membership>
When the applicationName is set like above, the Membership API will always use that application name when connecting to the ASP.NET application service database. That means it will work regardless of where the application is deployed or what path is used.
You should also then make sure that you do this for any Roles, Profile, WebPartPersonalization or other providers you configure.
Your application will then work fine.
How to Prevent This in the
The best way to prevent this from ever happening is to always specify the “applicationName” attribute when declaring your providers. One good default value to use is “/” – which is the root application name. This is the value specified for the default provider that ships with ASP.NET 2.0 (which by default stores the application service data within the ASPNETDB.MDF file under /app_data), and is why if you don’t override the provider settings it will work if you copy an app to another machine.
For other ASP.NET Security Links and Resources please check out (and bookmark) this large post of content I did.
Hope this helps,
Scott
P.S. In case it isn’t obvious, the reason why the applicationName setting even exists in the first place is so that you can map multiple applications and sites to the same database.
P.P.S. In hindsight, we should have defaulted the provider collection to have a value of "/" for applicationName if it wasn't specified.