Vb.net – wrong with the Membership ValidateUser request? (using ClientFormsAuthenticationMembershipProvider)

asp.netasp.net-membershipauthenticationmembershipvb.net

Are there any simple diagnostics I can run to determine why authentication is not working with the ClientFormsAuthenticationMembershipProvider provider? My problem:

I have a web site (we shall call it the "Authenticator" web site) hosted on Server A that is configured to use the AspNetSqlMembershipProvider provider for both Membership and Role management. The (what I think is) relevant web.config keys for that web site are these:

    <membership>
        <providers>
            <clear />
            <add name="AspNetSqlMembershipProvider"
                 type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
                 connectionStringName="MMMS35.API"
                 enablePasswordRetrieval="false"
                 enablePasswordReset="true"
                 requiresQuestionAndAnswer="false"
                 applicationName="Moose"
                 requiresUniqueEmail="false"
                 passwordFormat="Hashed"
                 maxInvalidPasswordAttempts="5"
                 minRequiredPasswordLength="7"
                 minRequiredNonalphanumericCharacters="0"
                 passwordAttemptWindow="1"
                 passwordStrengthRegularExpression="" />
        </providers>
    </membership>
    <profile>
        <providers>
            <clear />
            <add name="AspNetSqlProfileProvider"
                 connectionStringName="MMMS35.API"
                 applicationName="Moose"
                 type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
        </providers>
    </profile>
    <roleManager enabled="true">
        <providers>
            <clear />
            <add name="AspNetSqlRoleProvider"
                 connectionStringName="MMMS35.API"
                 applicationName="Moose"
                 type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
        </providers>
    </roleManager>

I have another web site (called the "Test Client" web site) hosted on Server B on the same network as Server A, but I do have any idea what the network paths, router configuration, etc. are between Servers A & B. The Test Client has a mock login setup, which uses the ClientFormsAuthenticationMembershipProvider provider to ask the "Authenticator" web site to authenticate the credentials given on the Test Client. The relevant web.config sections of the Test Client are these:

    <membership defaultProvider="ClientAuthenticationMembershipProvider">
        <providers>
            <clear/>
            <add name="ClientAuthenticationMembershipProvider"
                 type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                 serviceUri="http://slmooseapp1/mia/Authentication_JSON_AppService.axd"
                 credentialsProvider=""
                 savePasswordHashLocally="False"/>
        </providers>
    </membership>
    <roleManager defaultProvider="ClientRoleProvider" enabled="true">
        <providers>
            <clear/>
            <add name="ClientRoleProvider"
                 type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                 serviceUri="http://slmooseapp1/mia/Role_JSON_AppService.axd"
                 cacheTimeout="1"/>
        </providers>
    </roleManager>

When the Test Client executes this line:

DirectCast(Membership.Provider, ClientServices.Providers.ClientFormsAuthenticationMembershipProvider).ValidateUser(UserName.Text, Password.Text)

I get an exception:

System.UnauthorizedAccessException: Access to the path 'C:\Documents and Settings\Default User\Application Data\Microsoft Corporation\Internet Information Services\6.0.3790.3959' is denied.

However, when I run the Test Client on my development box (not on Server B) with the exact same web.config serviceUri paths (i.e., my client membership provider is pointing to the same Authentication web site on Server A), validating a user and getting the roles for that user works perfectly.

If I open a browser on Server B and request a page from the Authentication web site, the Authentication web app runs fine, so I know that I can make http requests from Server B to Server A successfully. What is going on with the ClientFormsAuthenticationMembershipProvider that fails when it is trying to make (what I think is) a JSON request from Server B to Server A?

EDIT (9/21/2009):

I've tried this scenario now with the Authentication web site and the Test Client on the same box (which is not my development machine). It still fails. I wrote a raw Http request programmatically and DID get a successful response…

Request:

Content-Type: application/json
Host: slappdev
Content-Length: 70
Expect: 100-continue
Connection: Keep-Alive

{"userName":"mdh","password":"test123","createPersistentCookie":false}

Response:

MicrosoftOfficeWebServer: 5.0_Pub
Content-Length: 10
Cache-Control: private, max-age=0
Content-Type: application/json; charset=utf-8
Date: Mon, 21 Sep 2009 20:10:13 GMT
Set-Cookie: AppName=1756811D52C4619AC78...; path=/; HttpOnly
Server: Microsoft-IIS/6.0
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET

{"d":true}

So, my guess is that something is wrong while working through the .Net framework calls to get from ValidateUser to the actual HTTP request. Any ideas?

Edit (9/22/2009):

The exception thrown is the exact same exception thrown as when my test client web page is attempting to access System.Windows.Forms.Application.UserAppDataPath. Adding a connectionStringName attribute to the membership/providers/add key that points to a valid connection string name in the web.config file on the client web app changes the exception!

System.Data.SqlClient.SqlException: Invalid object name 'ApplicationProperties'. at System.Data.SqlClient.SqlConnection.OnError

So, if all the membership functions are using the serviceUri, why is anything being attempted locally on the client? And, what utility do I need to run to add these new tables / objects into my database to support the client membership provider?

Best Answer

The ClientAuthenticationMembershipProvider has an offline cache feature. In case the actual membership provider cannot be reached, the client membership provider can cache credentials on the client machine and use those offline credentials. This offline cache is represented by a SQL Server Compact Edition database file automatically generated and stored in the System.Windows.Forms.Application.UserAppDataPath location.

According to this MSDN article, you can configure the local offline cache to use a database. You must manually create the database tables as specified in the article (in my 9/22 edit, I had not yet created those tables, and there is no utility to do so). I did this, but the client still wanted to create the folder structure on the local machine for the local SQL CE file, even though it would never create such a file. For every machine in my testing EXCEPT my local workstation, the IIS user did not have access to build that folder structure for the local cache database, which generated the UnauthorizedAccessException.

I'm rather unhappy that A) there is no config key to turn off the offline cache for membership, and B) even when directing the offline cache to a database, the client membership provider still must build a folder path to the local cache file that it never creates.

Related Topic