none
WCF How to authenticate a Client using Membership Services RRS feed

  • Question

  • Can someone help me take the next step please?
    1. I have created a website with a simple "hello" service using Microsoft Visual Web Developer 2010 Express.  My intent is to authenticate client users
        by having them provide their website membership username and password in the Client. 
        (The applicable website files are listed below.)
    2. I run the website in localhost as I am in development at this time.  The website and service runs without error.
    3. I created a simple WIndows Forms client to consume the service using Microsoft Visual Studio Express 2012 for Windows Desktop.  
       I add a service reference to the client which creates an entry in the app.config file  (See the client code below)
    4. The service replies to the service request from the client without setting a username and password - which is not what I expected. 
       At least the service and client "work" but not the way I want.
    5. I tried for days and seached the web to find an example of how to make this work.  I want the service to respond ONLY if I send the username and password
       corresponding to a user in the membership on the website.  I have had no luck.

    Obviously I am missing something here.  I can't even find an example that will work for my scenario - including the code in the WCF examples in MSDN WF_WCF_Samples
    I would eventually like to enable the authorization using the membership roles. I have tried examples, tried adding certificates ETC. with no luck

    For now I would just like to get the authorization to work.

    Any help at all would be most appreciated.







    ----------------  THE WEB SERVICE   -------------------------

    WEB SERVICE   WEB.CONFIG
    <?xml version="1.0"?>
    <configuration>
      <connectionStrings>
        <add name="ApplicationServices" 
             connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnetdb.mdf;User Instance=true"
             providerName="System.Data.SqlClient"/>
      </connectionStrings>
      <system.web>
        <compilation debug="true" targetFramework="4.0"/>
        <authentication mode="Forms">
          <forms loginUrl="~/Account/Login.aspx" timeout="2880"/>
        </authentication>
        <membership>
          <providers>
            <clear/>
            <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" 
                 connectionStringName="ApplicationServices" enablePasswordRetrieval="false" enablePasswordReset="true" 
                 requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" 
                 minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/"/>
          </providers>
        </membership>
        <profile>
          <providers>
            <clear/>
            <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/"/>
          </providers>
        </profile>
        <roleManager enabled="true">
          <providers>
            <clear/>
            <add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="ApplicationServices" applicationName="/"/>
            <add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/"/>
          </providers>
        </roleManager>
      </system.web>
      <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
      </system.webServer>
      <system.serviceModel>
     
        <bindings>
          <wsHttpBinding>
            <binding name="Binding1">
              <security mode="Message">
                <message clientCredentialType="UserName"  />
              </security>
            </binding>
          </wsHttpBinding>
        </bindings>
        
        <services>
          <service
              name="Service"
              behaviorConfiguration="MyServiceBehavior">
            <endpoint address=""
                   binding="wsHttpBinding"
                   contract="IService" />
          </service>
        </services>

        <behaviors>
          <serviceBehaviors>
            <behavior name="MyServiceBehavior">
              <serviceMetadata httpGetEnabled="true" />
              <serviceDebug includeExceptionDetailInFaults="false" />
              <!-- Configure user name authentication to use the Membership Provider -->
              <serviceCredentials>
                <userNameAuthentication
                  userNamePasswordValidationMode="MembershipProvider"
                  membershipProviderName="AspNetSqlMembershipProvider" />
              </serviceCredentials>
            </behavior>
          </serviceBehaviors>
        </behaviors>
     
       <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
            
      </system.serviceModel>
    </configuration>

    WEB SERVICE .svc file

    <%@ ServiceHost Language="C#" Debug="true" Service="Service" CodeBehind="~/App_Code/Service.cs" %>

    WEB SERVICE App_Code/Service.cs  file

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;

    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service" in code, svc and config file together.
    public class Service : IService
    {
    public string SayHello()
    {
            return "Hello from MyWebService";
    }
    }


    WEB SERVICE App_Code/IService.cs  file

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;

    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService" in both code and config file together.
    [ServiceContract]
    public interface IService
    {
    [OperationContract]
    string SayHello();
    }


     -----     CLIENT  Windows Form -----------------------------------------------

    CLIENT  app.config file

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <startup>
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
        </startup>
        <system.serviceModel>
     
          <!--  This is the configuration genereated when I add the Service Reference 
                The Client works using this configuration but does not use the memebership
                data on the website  (username, password not required) -->
            <bindings>
                <wsHttpBinding>
                    <binding name="WSHttpBinding_IService" />
                </wsHttpBinding>
            </bindings>
            <client>
                <endpoint address="http://localhost:58139/MyWebService/Service.svc"
                    binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService"
                    contract="MyServiceReference.IService" name="WSHttpBinding_IService">
                    <identity>
                        <userPrincipalName value="Dennis-Win7\Dennis" />
                    </identity>
                </endpoint>
            </client>
          <!-- The configuration below was tried (commented out above first) - while also setting username and password
               In the Btn_CallService_Click sub code but the call fails even with username and password set  -->
     <!--        <bindings>
                <wsHttpBinding>
                  <binding name="WSHttpBinding_IService">
                    <security mode="Message">
                      <message clientCredentialType="UserName"/>
                    </security>
                  </binding>
                </wsHttpBinding>
            </bindings>
          
            <client>
                <endpoint address="http://localhost:58139/MyWebService/Service.svc"
                    binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService"
                    contract="MyServiceReference.IService" name="WSHttpBinding_IService">

                  <identity>
                      <userPrincipalName value="Dennis-Win7\Dennis" />
                  </identity>
                  
                </endpoint>
            </client>
     -->

        </system.serviceModel>
    </configuration>

    CLIENT  code to call service

       Private Sub Btn_CallService_Click(sender As Object, e As EventArgs) Handles Btn_CallService.Click
            TB_Serviceresponse.Text = ""
            Dim gethello As MyServiceReference.ServiceClient

            gethello = New MyServiceReference.ServiceClient
            '  the Web Site Membership has the following user in the SQL Membership
            '  I can login to the membership on the website useing this user
            '  The code below can be used or commented out - makes no difference
            '    as the service reponds with the default app.config genereated
            '    by adding the service reference 
            '  gethello.ClientCredentials.UserName.UserName = "dan"
            '  gethello.ClientCredentials.UserName.Password = "dan123"

            gethello.Open()
            ' TB_Serviderespone is a textbox for the service response
            TB_Serviceresponse.Text = gethello.SayHello()
            gethello.Close()

        End Sub
    Friday, October 3, 2014 10:22 PM

Answers

  • I made progress.  I had to use certificates and get them installed correctly, an important point left out of many examples.

    Here is the new service web.config system.serviceModel section.

      <system.serviceModel>
         <services>
          <service name="AuthenticationService.Service" behaviorConfiguration="MyServiceBehavior">
            <endpoint address="" binding="wsHttpBinding" bindingConfiguration ="MembershipBinding"
                      contract="AuthenticationService.IService"/>
          </service>
        </services>
        <bindings>
          <wsHttpBinding>
            <binding name="MembershipBinding">
              <security mode="Message">
                <message clientCredentialType="UserName"/>
              </security>
            </binding>
          </wsHttpBinding>
        </bindings>
         <behaviors>
          <serviceBehaviors>
            <behavior name="MyServiceBehavior">
              <serviceMetadata httpGetEnabled="true"/>   
              <serviceDebug includeExceptionDetailInFaults="false"/>
              <serviceCredentials>
                <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" 
                                        membershipProviderName="AspNetSqlMembershipProvider"/>
                <serviceCertificate  findValue="Dev Certification Authority"  x509FindType="FindByIssuerName"
                                     storeLocation="LocalMachine" storeName="My"  />
              </serviceCredentials>
           </behavior>
         </serviceBehaviors>
        </behaviors>
      </system.serviceModel>

    To get the service to work several things needed to happen.

    I needed to generate a self signed certificate (The one in the example). It is installed in my development PC in the local machine "personal" store.  I also had to create Trusted Root certificate and put it in the local machine Trusted Root Certifications Authorities Store. I did all this using  the following sites for help:

    http://www.codewrecks.com/blog/index.php/2009/09/08/use-aspnet-membership-provider-with-a-wcf-svc-service/    

    That site had almost everything except details on how to create and save the root authority certificate. To get more help on that I went to this site

    http://www.codeproject.com/Articles/24027/SSL-with-Self-hosted-WCF-Service

    And then to 

    http://webservices20.blogspot.com/2011/02/wcf-keyset-does-not-exist.html

    to help with some errors.  All the issues are with security.

    There were probably some other steps and tricks along the way I have  forgotten. But if anyone is trying to do this the above example and links may help.  This only works in my development station and has not been deployed to a real website server. - I am sure the certificates will create much confusion in that step.  Once the server and service were running the client worked with not much change.

    I just deleted the service reference and added it back using the website service.svc link.

    here is the app.config created when I added the service reference. I did no modifications to it.

       <bindings>
          <wsHttpBinding>
            <binding name="WSHttpBinding_IService">
              <security>
                <message clientCredentialType="UserName" />
              </security>
            </binding>
          </wsHttpBinding>
        </bindings>
        <client>
          <endpoint address="http://localhost:58139/MyWebService/Service.svc"
            binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService"
            contract="MyServiceReference.IService" name="WSHttpBinding_IService">
            <identity>
              <certificate encodedValue="AwAAA..detail omitted..+mzv7Pw==" />
            </identity>
          </endpoint>
        </client>

    The code section of the client now looks like this.

           TB_Serviceresponse.Text = ""
            Dim gethello As MyServiceReference.ServiceClient
            gethello = New MyServiceReference.ServiceClient
            gethello.ClientCredentials.UserName.UserName = "dan"
            gethello.ClientCredentials.UserName.Password = "dan123"
            ' Next line is required if not using a real certificate authority.  It requires a  reference  System.IdenityModel  to be added.
            ' gethello.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None
             gethello.Open()
            ' TB_Serviderespone is a textbox for the service response
            TB_Serviceresponse.Text = gethello.SayHello()
            gethello.Close()

    If you have a good certificate (with private key) and have the Trusted Root certificate  for that installed you do not need the line:

     gethello.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None
            

    I will consider this my answer and mark this as closed.

    I still have questions about WCF, Certificates, but will do that in another post.

    Sunday, October 12, 2014 11:11 PM

All replies

  • I made progress.  I had to use certificates and get them installed correctly, an important point left out of many examples.

    Here is the new service web.config system.serviceModel section.

      <system.serviceModel>
         <services>
          <service name="AuthenticationService.Service" behaviorConfiguration="MyServiceBehavior">
            <endpoint address="" binding="wsHttpBinding" bindingConfiguration ="MembershipBinding"
                      contract="AuthenticationService.IService"/>
          </service>
        </services>
        <bindings>
          <wsHttpBinding>
            <binding name="MembershipBinding">
              <security mode="Message">
                <message clientCredentialType="UserName"/>
              </security>
            </binding>
          </wsHttpBinding>
        </bindings>
         <behaviors>
          <serviceBehaviors>
            <behavior name="MyServiceBehavior">
              <serviceMetadata httpGetEnabled="true"/>   
              <serviceDebug includeExceptionDetailInFaults="false"/>
              <serviceCredentials>
                <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" 
                                        membershipProviderName="AspNetSqlMembershipProvider"/>
                <serviceCertificate  findValue="Dev Certification Authority"  x509FindType="FindByIssuerName"
                                     storeLocation="LocalMachine" storeName="My"  />
              </serviceCredentials>
           </behavior>
         </serviceBehaviors>
        </behaviors>
      </system.serviceModel>

    To get the service to work several things needed to happen.

    I needed to generate a self signed certificate (The one in the example). It is installed in my development PC in the local machine "personal" store.  I also had to create Trusted Root certificate and put it in the local machine Trusted Root Certifications Authorities Store. I did all this using  the following sites for help:

    http://www.codewrecks.com/blog/index.php/2009/09/08/use-aspnet-membership-provider-with-a-wcf-svc-service/    

    That site had almost everything except details on how to create and save the root authority certificate. To get more help on that I went to this site

    http://www.codeproject.com/Articles/24027/SSL-with-Self-hosted-WCF-Service

    And then to 

    http://webservices20.blogspot.com/2011/02/wcf-keyset-does-not-exist.html

    to help with some errors.  All the issues are with security.

    There were probably some other steps and tricks along the way I have  forgotten. But if anyone is trying to do this the above example and links may help.  This only works in my development station and has not been deployed to a real website server. - I am sure the certificates will create much confusion in that step.  Once the server and service were running the client worked with not much change.

    I just deleted the service reference and added it back using the website service.svc link.

    here is the app.config created when I added the service reference. I did no modifications to it.

       <bindings>
          <wsHttpBinding>
            <binding name="WSHttpBinding_IService">
              <security>
                <message clientCredentialType="UserName" />
              </security>
            </binding>
          </wsHttpBinding>
        </bindings>
        <client>
          <endpoint address="http://localhost:58139/MyWebService/Service.svc"
            binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService"
            contract="MyServiceReference.IService" name="WSHttpBinding_IService">
            <identity>
              <certificate encodedValue="AwAAA..detail omitted..+mzv7Pw==" />
            </identity>
          </endpoint>
        </client>

    The code section of the client now looks like this.

           TB_Serviceresponse.Text = ""
            Dim gethello As MyServiceReference.ServiceClient
            gethello = New MyServiceReference.ServiceClient
            gethello.ClientCredentials.UserName.UserName = "dan"
            gethello.ClientCredentials.UserName.Password = "dan123"
            ' Next line is required if not using a real certificate authority.  It requires a  reference  System.IdenityModel  to be added.
            ' gethello.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None
             gethello.Open()
            ' TB_Serviderespone is a textbox for the service response
            TB_Serviceresponse.Text = gethello.SayHello()
            gethello.Close()

    If you have a good certificate (with private key) and have the Trusted Root certificate  for that installed you do not need the line:

     gethello.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None
            

    I will consider this my answer and mark this as closed.

    I still have questions about WCF, Certificates, but will do that in another post.

    Sunday, October 12, 2014 11:11 PM