locked
WCF Data Service with UserName/Password authentication via a SqlMembershipProvider RRS feed

  • Question

  • I have been trying (and failing) to configure a WCF Data Service with UserName/Password authentication via a SqlMembershipProvider. Depending on how I configure things, I either get no authentication whatsoever or I get Windows authentication. The latest version of the configuration file looks like this:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <configuration>
    
     <system.web>
    
     <compilation debug="true" targetFramework="4.0" />
    
     <membership defaultProvider="SqlProvider" userIsOnlineTimeWindow="15">
    
      <providers>
    
      <clear />
    
      <add name="SqlProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="PatchStore" applicationName="MembershipProvider" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" requiresUniqueEmail="true" passwordFormat="Hashed" />
    
      </providers>
    
     </membership> 
    
     </system.web>
    
     <system.serviceModel>
    
     <bindings>
    
      <webHttpBinding>
    
      <binding name="RoleBinding">
    
       <security mode="Transport">
    
       <transport clientCredentialType="Basic" />
    
       </security>
    
      </binding>
    
      </webHttpBinding>
    
     </bindings>
    
     <behaviors>
    
      <serviceBehaviors>
    
      <behavior name="RoleBehavior">
    
       <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
    
       <serviceMetadata httpGetEnabled="true" />
    
       <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
    
       <serviceDebug includeExceptionDetailInFaults="true" />
    
       <serviceCredentials>
    
       <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="SqlProvider" />
    
       </serviceCredentials>
    
      </behavior>
    
      </serviceBehaviors>
    
     </behaviors>
    
     <services>
    
      <service name="PatchService.PatchStore" behaviorConfiguration="RoleBehavior">
    
      <endpoint address="/PatchService/PatchStore.svc" binding="webHttpBinding" bindingConfiguration="RoleBinding" contract="System.Data.Services.IRequestHandler" />
    
      </service>
    
     </services>
    
     <serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
    
     </system.serviceModel>
    
     <system.webServer>
    
     <modules runAllManagedModulesForAllRequests="true" />
    
     </system.webServer>
    
     <connectionStrings>
    
     <add name="PatchStoreEntities" connectionString="metadata=res://*/PatchModel.csdl|res://*/PatchModel.ssdl|res://*/PatchModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=.;Initial Catalog=PatchStore;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />
    
     <add name="PatchStore" connectionString="Data Source=.;Initial Catalog=PatchStore;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
    
     </connectionStrings>
    
    </configuration>
    
    

     

     

    Presently Basic Authentication is installed and enabled on the application in IIS. In this configuration, authentication appears to be handled by Windows.

    Wednesday, April 21, 2010 2:03 PM

All replies

  • Please change basic authentication mode to FORM, then allow anonymous access:

     

    <authentication mode="Forms">
       <forms loginUrl="Login.aspx" name=".ASPXFORMSAUTH">
       </forms>
      </authentication>
      <authorization>
       <allow users="?" />
      </authorization>

    You can then control who can access what inside query interceptors:

    For example:

        [QueryInterceptor("BookSet")]
        public Expression<Func<Book, bool>> BookInterceptor()
        {
          Guid uid = (Guid)Membership.GetUser().ProviderUserKey;
          return b => b.Owner.ID == uid;
        }

    In the above case, I retrieve the ID from the membership provider, and then filter the result such that an authenticated user can only retrieve what he owns.

     

    Regards,

    PQ


    Peter Q. http://blogs.msdn.com/peter_qian
    Wednesday, April 21, 2010 7:09 PM
    Answerer
  • Thanks for the response, Peter. Unfortunately, I'm not quite there, yet. The good news is that I'm starting to get a better picture of what's going on, I think.

    For a 'webHttpBinding', it does not seem possible to configure a 'message' credential. The only option is to configure a 'transport' credential. None of the transport credential options seem appropriate. As you can see, I was using 'Basic'. Unless I configure the website for 'Basic Authentication', I get errors. And I can't simultaneously enable both 'Forms Authentication' and 'Basic Authentication' on the site.

    I have been assuming that because the data service is a REST API, I needed to use a 'webHttpBinding'. I tried to switch it to a 'wsHttpBinding', but I got an obscure error that I took as an indication that my initial assumption was correct.

    Perhaps there is no way in the current stack to validate 'Basic' credentials against something other than AD, at least without writing a custom authentication mechanism.

    Wednesday, April 21, 2010 8:39 PM
  • That's weird...As I remember you really don't need to config the channel or binding for this to work. Can you send a copy of your project to pqian at microsoft dot com? I'll take a look at it and see if I can figure something out.

    Regards,

    PQ


    Peter Q. http://blogs.msdn.com/peter_qian
    Wednesday, April 21, 2010 9:05 PM
    Answerer
  • Did this thread ever reach a resolution that wasn't posted? I'm having almost the exact same issue and I don't see the solution.


    My Goal: Use a custom binding with binary encoding over HTTPS.  Authenticate users with ASP.NET SQL membership provider using basic authentication.

    This is my custom binding:

    <customBinding>
          <binding name="BinaryHTTPS" sendTimeout="00:03:00">
            <binaryMessageEncoding maxSessionSize ="2048" maxReadPoolSize ="64" maxWritePoolSize ="16"/>
            <httpsTransport maxReceivedMessageSize="4194304"
                            maxBufferSize="4194304"
                            authenticationScheme="Basic"
                            proxyAuthenticationScheme ="Basic"                       
                            requireClientCertificate="False"
                            allowCookies="true"/>
           
          </binding>
        </customBinding>

    These are my behaviors:


    <behaviors>
        <serviceBehaviors>
          <behavior name="AnnotationBehaviour">
            <serviceMetadata httpsGetEnabled="true"/>
            <serviceDebug includeExceptionDetailInFaults="true" />
            <dataContractSerializer maxItemsInObjectGraph="2147483647" />
            <serviceCredentials>
              <userNameAuthentication userNamePasswordValidationMode ="MembershipProvider"                  
                                      membershipProviderName ="VikingMembershipProvider"/>
              <serviceCertificate findValue ="73bf4bf5e1e61e9b47c679b634cc8de9"
                                  storeLocation ="LocalMachine"
                                  x509FindType="FindBySerialNumber"/>
            </serviceCredentials>
           
            <serviceAuthorization principalPermissionMode ="UseAspNetRoles" roleProviderName ="VikingRoleProvider"/>
           
            <serviceSecurityAudit auditLogLocation ="Default"
                                    serviceAuthorizationAuditLevel ="SuccessOrFailure"
                                    messageAuthenticationAuditLevel ="SuccessOrFailure"/>
             
          </behavior>
        </serviceBehaviors>
      </behaviors>

    Problem: If I use "Basic" authentication I get an "Audit failure" error in the windows security event log, "An account failed to log on".  If I create a windows account for my username/password combo then I get an "Audit Sucess".  I'm expecting this authentication should occur against my SQL role provider.

    Once I hack my way past the windows authentication hurdle I still get this error in the WCF log: The HTTP request with client authentication scheme 'Basic' failed with 'Unauthorized' status.

    The error message I get in the client includes "System.ServiceModel.Security.SecurityAccessDeniedException: Access is denied".

    All users are in the root application '/', so that may be an issue, but that is how I have configured things until I fix it later.  Please help me figure out what is going wrong. Any tips for debugging SQL Membership providers? Thanks.

    Thursday, June 17, 2010 2:19 AM
  • Try the approach described here: http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2008/05/27/10447.aspx

    It's a bit of a hack IMO, but it works -- I've implemented it successfully in my project. In my case, I have a client application (can be a winform/WPF or a web app) that authenticates against the ASP.NET membership provider used by the WCF data service, then performs CRUD against the service.

    One observation, when you try this method, change clientCredentialType to "None" in web.config because you'll be performing authentication manually. You can also combine it with SSL.

    Wednesday, October 13, 2010 12:29 AM