none
how to force webservice clients to provide user name and password

    Question

  • Hi,

     

    I have no experience in WCF. I have a basic WCF service application in .net 4 which writes to a database. I want the clients to provide a valid username and password to call the web methods, otherwise they would be able to write database rows which they don't have rights to access.

    To solve the problem, of course I could add two string arguments "username" and "pwd" to each webmethod, but then the password would travel unencrypted (I presume), defeating the purpose. My question is how to approach this problem.

     

    my web.config is the default one, created by visual studio 2010:

     

    <?xml version="1.0"?>
    
    
    <configuration>
    
     <system.web>
      <compilation debug="true" targetFramework="4.0" />
     </system.web>
     <system.serviceModel >
      <bindings>
      </bindings>
      <behaviors>
       <serviceBehaviors>
        <behavior >
         <!-- 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="false"/>
        </behavior>
       </serviceBehaviors>
      </behaviors>
      
      <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
     </system.serviceModel>
     <system.webServer>
      <modules runAllManagedModulesForAllRequests="true"/>
     </system.webServer>
     
    </configuration>
    
    <br/>
    

     

    and I am connecting to the web server as follows (F# language):

     

    let binding = ServiceModel.BasicHttpBinding()
            
    let endpointAddr = new ServiceModel.EndpointAddress("http://localhost:4472/Service1.svc")
    
            
    let cl = new WpfControlLibrary1.AppServiceRef.Service1Client(binding , endpointAddr )

    Thanks for any help.

    Wednesday, July 27, 2011 10:44 AM

Answers

  • The error that you are getting is because you are using message security.

    Basically, when you pass username and password, they shouls be send in a secured channel, so people won't be able to see them.

    To secure a service you can do one of the following:

    1. let windows secure the channel by using https (transport security) - this requires a certificate to be installed and configured to be used for a specific https (ssl) port (OS settings)

    2. securing the channel using wcf (message security) - this requires a certificate to be installed and configured to be used by the service (WCF settings)

    Which type of security do you want to use?


    Please mark posts as answers/helpful if it answers your question. Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper. Visit my blog: http://blogs.microsoft.co.il/blogs/idof
    • Marked as answer by Yi-Lun Luo Tuesday, August 02, 2011 8:59 AM
    Friday, July 29, 2011 2:02 PM
  • I have no experience in WCF. I have a basic WCF service application in .net 4 which writes to a database. I want the clients to provide a valid username and password to call the web methods, otherwise they would be able to write database rows which they don't have rights to access.


    I would suggest you first setup a wcf service without any access to database or any username/password authentications, and set the security mode to None. Make this service works first, then you can dig into it and tweak all those settings  to see the effect, then you can add more operations to the service.
    Tony Zeng qzcbs@163.com
    • Marked as answer by Yi-Lun Luo Tuesday, August 02, 2011 8:59 AM
    Sunday, July 31, 2011 5:21 PM

All replies

  • Is this is a special username and password, you can use WCF security with username/password authentication, and provide your own custom authenticator.

    Check this article:

    http://msdn.microsoft.com/en-us/library/aa702565.aspx


    Please mark posts as answers/helpful if it answers your question. Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper. Visit my blog: http://blogs.microsoft.co.il/blogs/idof
    Wednesday, July 27, 2011 11:09 AM
  • Thanks, I tried following the tutorial but it does not say how to specify the credentials when creating the client.

    More in detail, here is what I did:

    1) I wrote the code to validate the credentials:


    namespace AppServer
    {

        public class UserValidator : System.IdentityModel.Selectors.UserNamePasswordValidator
        {

            public override void Validate(string userName, string password)
            {

                if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password))

                    throw new System.IdentityModel.Tokens.SecurityTokenException("Username and password required");


                var db = new DB.dbDataContext();

                var x = (from u in db.users
                         where u.name == userName && u.password == password
                         select u).FirstOrDefault();
                if (x == null)
                    throw new FaultException(string.Format("Wrong username ({0}) or password ", userName));

            }

        }

       
        ...
       
       
    2) I modified the web.config as follows (the comments identify the added code):

    <?xml version="1.0"?>

    <!-- I followed this tutorial: http://msdn.microsoft.com/en-us/library/aa702565.aspx -->
    <configuration>

      <system.web>
        <compilation debug="true" targetFramework="4.0" />
      </system.web>
      <system.serviceModel >
        <bindings>

          <!--first block added-->
          <wsHttpBinding>
            <binding name="Binding1">
              <security mode="Message">
                <message clientCredentialType="UserName" />
              </security>
            </binding>
          </wsHttpBinding>
          <!-- end first block added-->
        </bindings>
        <behaviors>
          <serviceBehaviors>
            <behavior >

              <!--second block added-->
              <serviceCredentials>
                <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="AppServer.UserValidator, AppServer"></userNameAuthentication>
              </serviceCredentials>
              <!-- end second block added-->
             
              <!-- 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="false"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>

        <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
      </system.serviceModel>
      <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
      </system.webServer>

    </configuration>



    3) then I got stuck when creating the client (new code is in bold):

          
        let binding = ServiceModel.BasicHttpBinding()
        binding.Security.Mode <- ServiceModel.SecurityMode.Message
        binding.Security.Message.ClientCredentialType <- ServiceModel.MessageCredentialType.UserName
       
        let endpointAddr = new ServiceModel.EndpointAddress("http://localhost:4472/Service1.svc")

        let cred = ServiceModel.Description.ClientCredentials()
        cred.UserName.UserName <- userName
        cred.UserName.Password <- pwd
        // where do I put cred? currently it is unused...
       
        let cl = new WpfControlLibrary1.AppServiceRef.Service1Client(binding , endpointAddr )
    Wednesday, July 27, 2011 1:58 PM
  • Notice that your service uses wsHttpBinding and your client uses basicHttpBinding - change the client to use wsHttpBinding
    Please mark posts as answers/helpful if it answers your question. Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper. Visit my blog: http://blogs.microsoft.co.il/blogs/idof
    Wednesday, July 27, 2011 2:18 PM
  • Thanks. I did, and we are getting closer: now when I run the code I get this exception: "The username is not provided. Specify username in ClientCredentials."

    Which was my initial question: how do I supply the user name and password to the client? In the code above, I don't know where to put "cred"...

     

     

    Wednesday, July 27, 2011 2:36 PM
  • The cl object you created should have a property named clientCredentials. So instead of creating new clientCredentials objects, just go to your Service1Client object (the cl object) and set its credentials, like so:

     

    cl.ClientCredentials.Username.Username = ...

    cl.ClientCredentials.Username.Password = ...


    Please mark posts as answers/helpful if it answers your question. Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper. Visit my blog: http://blogs.microsoft.co.il/blogs/idof
    Wednesday, July 27, 2011 2:49 PM
  • Thank you! It worked, but unfortunately I have another exception now:

    System.ServiceModel.ProtocolException: "Content Type application/soap+xml; charset=utf-8 was not supported by service http://localhost:4472/Service1.svc.  The client and service bindings may be mismatched."

    Wednesday, July 27, 2011 3:04 PM
  • Instead of constructing the proxy using binding and endpoint address, try pointing it to an endpoint configuration element in the app.config - the add service reference should have generated one for you. I assume you have some mismatch between how the service's endpoint is configured and how you configured your client proxy.
    Please mark posts as answers/helpful if it answers your question. Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper. Visit my blog: http://blogs.microsoft.co.il/blogs/idof
    Wednesday, July 27, 2011 3:25 PM
  • Instead of constructing the proxy using binding and endpoint address, try pointing it to an endpoint configuration element in the app.config - the add service reference should have generated one for you. I assume you have some mismatch between how the service's endpoint is configured and how you configured your client proxy.
    You are right. I find every time after add service reference or update existing service reference, there will always be some mismatch between the service cofig file and the client config file. fortunately I can open both config files with Service Editor at the same time, and compare each settings, especially those binding settings.
    Tony Zeng qzcbs@163.com
    Wednesday, July 27, 2011 4:00 PM
  • Instead of constructing the proxy using binding and endpoint address, try pointing it to an endpoint configuration element in the app.config - the add service reference should have generated one for you. 
    Sorry I don't understand this. Could you give a code sample? What should I look for in the Reference.cs file generated by the "add service reference" wizard?
    (I don't see any "endpoint" tag in the web.config. I have copied my entire web.config above.)
    Thursday, July 28, 2011 6:17 AM
  • You are right. I find every time after add service reference or update existing service reference, there will always be some mismatch between the service cofig file and the client config file. fortunately I can open both config files with Service Editor at the same time

    what is "service editor"? is it in visual studio?
    Thursday, July 28, 2011 6:32 AM
  • To  get the Service1Client class, you've probably used Visual Studio's "add service reference" option - this option creates  the reference.cs file and a configuration file (app.config/web.config) with the endpoints configuration (under the <system.serviceModel>|<client> section). Instead of initializing the proxy using binding object and endpoint address, you can simply use the endpoint configuration name and pass it to the constructor like so: new Service1Client("myEndpointName"). This constructor will look in the configuration, see the endpoint configuration + binding settings and will create matching endpoint settings.


    Please mark posts as answers/helpful if it answers your question. Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper. Visit my blog: http://blogs.microsoft.co.il/blogs/idof
    Thursday, July 28, 2011 6:42 AM
  • I guess he meant the WCF configuration editor - you can access it from the Tools menu.

     

    By the way, the configuration in the client side is supposed to be a bit different from the service side configuration. In services you place the service's configuration in the <services> element, and in the client you place the list of service endpoints in the <client> element.

    You can find more information in the following article:

    http://msdn.microsoft.com/en-us/library/ms735103.aspx

     


    Please mark posts as answers/helpful if it answers your question. Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper. Visit my blog: http://blogs.microsoft.co.il/blogs/idof
    Thursday, July 28, 2011 6:45 AM
  • Sorry, I hadn't noticed the app.config generated by "add service reference", just the web.config for the wcf service. :P

    Now I think I am doing what you say, but I am getting another exception (see below).

    Here is my app.config, as generated by "add service reference" (strange that it contains "basicHttpBinding" when the web.config specifies wsHttpBinding...):
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <bindings>
          <basicHttpBinding>
            <binding name="BasicHttpBinding_Service1" closeTimeout="00:01:00"
              openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
              allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
              maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
              messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
              useDefaultWebProxy="true">
              <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                maxBytesPerRead="4096" maxNameTableCharCount="16384" />
              <security mode="None">
                <transport clientCredentialType="None" proxyCredentialType="None"
                  realm="" />
                <message clientCredentialType="UserName" algorithmSuite="Default" />
              </security>
            </binding>
          </basicHttpBinding>
        </bindings>
        <client>
          <endpoint address="http://localhost:4472/Service1.svc" binding="basicHttpBinding"
            bindingConfiguration="BasicHttpBinding_Service1" contract="AppServerRef.Service1"
            name="BasicHttpBinding_Service1" />
        </client>
      </system.serviceModel>
    </configuration>
    
    



    Here is how I am creating the client (passing the endpoint name as you suggested, which is "BasicHttpBinding_Service1"):

    namespace WpfControlLibrary1
    {
      public class WcfClient
      {
        public static AppServerRef.Service1Client createClient(string userName, string pwd)
        {
          var cl = new AppServerRef.Service1Client("BasicHttpBinding_Service1");
          cl.ClientCredentials.UserName.UserName = userName;
          cl.ClientCredentials.UserName.Password = pwd;
          return cl;
        }
      }
    }
    
    



    and here is the exception:

    System.InvalidOperationException: "Could not find endpoint element with name 'BasicHttpBinding_Service1' and contract 'AppServerRef.Service1' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this name could be found in the client element."}   

    I deleted the service reference and the app.config, and regenerated them, but the error remains...
    Thursday, July 28, 2011 7:19 AM
  • I'm sorry, but it seems you are doing something wrong in your code. Will it be possible for you to upload it so I can see? please compress all the solution and upload it to a public place
    Please mark posts as answers/helpful if it answers your question. Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper. Visit my blog: http://blogs.microsoft.co.il/blogs/idof
    Thursday, July 28, 2011 8:14 AM
  • what is "service editor"? is it in visual studio?


    Refer to this link,

    http://msdn.microsoft.com/en-us/library/ms732009.aspx

    When you trying to open your config file, right click on that file and choose "Open with...", and use the editor above. If you could not find it, then choose "Add" and browse to the location and choose the editor executalbe.


    Tony Zeng qzcbs@163.com
    Thursday, July 28, 2011 8:51 AM
  • Thank you very much. :)

    I've put a stripped-down code here. Just a few lines, demonstrating the problem.

    http://www.mediafire.com/?460kni75bx4sxk7

    search for the keywords "createClient1" and "createClient2"

    Basically if I use the createClient1 method, I get the exception "Could not find endpoint element with name 'BasicHttpBinding_Service1' and contract 'AppServerRef.Service1' in the ServiceModel client configuration section. ..."

    If OTOH I use the createClient2 method, it works but as soon as I invoke a web method I get the exception  "Content Type application/soap+xml; charset=utf-8 was not supported by service http://localhost:4472/Service1.svc. The client and service bindings may be mismatched."

    Thursday, July 28, 2011 10:30 AM
  • Thank you very much. :)

    I've put a stripped-down code here. Just a few lines, demonstrating the problem.

    http://www.mediafire.com/?460kni75bx4sxk7


    I'm in China, seems can not reach this link. If your project is not classified, can you send a copy to qzcbs@163.com?
    Tony Zeng qzcbs@163.com
    Thursday, July 28, 2011 5:09 PM
  • The problem is that your client configuration is in the app.config which is placed in a class library. When you develop client applications, only the app.config in the application project itself (wpf, winforms, console) is used, all other app.config files in the class libraries are ignored. So you need to copy the content of the app.config from the class library to the wpf application - this will make it work.

     

    In addition, not that your service doesn't actually use the wsHttpBinding, but rather basicHttpBinding, this is because you haven't declared the endpoints for the service in the web.config file. If you don't declare endpoints then you automatically get basic http bindings when hosting using web servers. You need to create a <services> section in the web.config, and add your service configuration+endpoints to it, and connect the endpoint that uses wsHttpBinding to the binding configuration you've created.

    Please read the following article to understand the concept of web hosting:

    http://msdn.microsoft.com/en-us/library/ms733766.aspx


    Please mark posts as answers/helpful if it answers your question. Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper. Visit my blog: http://blogs.microsoft.co.il/blogs/idof
    Friday, July 29, 2011 7:46 AM
  • ...You need to create a <services> section in the web.config, and add your service configuration+endpoints to it, and connect the endpoint that uses wsHttpBinding to the binding configuration you've created...

    I did my best (see below) but I get the utf-8 error when I run the "add service reference" functionality.

    Screenshot of the error: http://gyazo.com/bb178a39d55c010122c5b96fdec86417.png

    This is my attempt at writing a web.config:
    <?xml version="1.0"?>
    
    
    <configuration>
    
     <system.web>
     <compilation debug="true" targetFramework="4.0" />
     </system.web>
     <system.serviceModel >
     <services>
      <service name="myApplicationServer" behaviorConfiguration="myBehavior">
      <endpoint bindingConfiguration="myBinding" name="myEndpoint" binding="wsHttpBinding"
         address="http://localhost:46106/Service1.svc"
         contract="AppServer.Service1"></endpoint>
      <endpoint address="http://localhost:46106/Service1.svc/mex/" binding="mexHttpBinding"
    
         contract="IMetadataExchange" />
    
      </service>
    
     </services>
     <bindings>
    
      <!--first block added-->
      <wsHttpBinding >
      <binding name="myBinding" >
       <security mode="Message">
       <message clientCredentialType="UserName" />
       </security>
      </binding>
      </wsHttpBinding>
      <!-- end first block added-->
     </bindings>
     <behaviors>
      <serviceBehaviors>
      <behavior name="myBehavior">
    
       <!--second block added-->
       <serviceCredentials>
       <userNameAuthentication userNamePasswordValidationMode="Custom"
             customUserNamePasswordValidatorType="AppServer.UserValidator, AppServer"/>
       </serviceCredentials>
       <!-- end second block added-->
    
       <!-- 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="false"/>
      </behavior>
      </serviceBehaviors>
     </behaviors>
    
     <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
     </system.serviceModel>
     <system.webServer>
     <modules runAllManagedModulesForAllRequests="true"/>
     </system.webServer>
    
    </configuration>
    
    

    Any idea what is wrong with this web.config and why the "add service reference" wizard fails? Thank you
    Friday, July 29, 2011 12:55 PM
  • The name attribute in the service element should point to the service's class name, including namespace, not the myApplicationServer string you wrote in the config.

     

    Please read the article in the link I gave you before - it will teach you how to write configuration files.


    Please mark posts as answers/helpful if it answers your question. Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper. Visit my blog: http://blogs.microsoft.co.il/blogs/idof
    Friday, July 29, 2011 1:06 PM
  • The name attribute in the service element should point to the service's class name, including namespace, not the myApplicationServer string you wrote in the config.

    Please read the article in the link I gave you before - it will teach you how to write configuration files.



    Sorry. I read the document you pointed to but unfortunately there is nothing there that helps with the new error that I am getting now (after fixing the error in the name attribute in the service element as you said).

    When I run "add service reference", the error has now changed to: "The service certificate is not provided. Specify a service certificate in ServiceCredentials."

    This is strange since the tutorial did not mention anything like "service certificate" being necessary. What am I doing wrong?

    My current web config in case it is useful:

     

    <?xml version="1.0"?>
    
    <!-- I followed this tutorial: http://msdn.microsoft.com/en-us/library/aa702565.aspx -->
    <configuration>
    
     <system.web>
      <compilation debug="true" targetFramework="4.0" />
     </system.web>
     <system.serviceModel >
      <services>
       <service name="AppServer.Service1" behaviorConfiguration="myBehavior">
        <endpoint bindingConfiguration="myBinding" name="myEndpoint" binding="wsHttpBinding"
             address=""
             contract="AppServer.Service1"></endpoint>
        <endpoint address="mex" binding="mexHttpBinding"
    
             contract="IMetadataExchange" />
    
       </service>
    
      </services>
      <bindings>
    
       <!--first block added-->
       <wsHttpBinding >
        <binding name="myBinding" >
         <security mode="Message">
          <message clientCredentialType="UserName" />
         </security>
        </binding>
       </wsHttpBinding>
       <!-- end first block added-->
      </bindings>
      <behaviors>
       <serviceBehaviors>
        <behavior name="myBehavior">
    
         <!--second block added-->
         <serviceCredentials>
          
          <userNameAuthentication userNamePasswordValidationMode="Custom"
                      customUserNamePasswordValidatorType="AppServer.UserValidator, AppServer"/>
         </serviceCredentials>
         <!-- end second block added-->
    
         <!-- 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="false"/>
        </behavior>
       </serviceBehaviors>
      </behaviors>
    
      <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
     </system.serviceModel>
     <system.webServer>
      <modules runAllManagedModulesForAllRequests="true"/>
     </system.webServer>
    
    </configuration>
    
    

    Friday, July 29, 2011 1:21 PM
  • The error that you are getting is because you are using message security.

    Basically, when you pass username and password, they shouls be send in a secured channel, so people won't be able to see them.

    To secure a service you can do one of the following:

    1. let windows secure the channel by using https (transport security) - this requires a certificate to be installed and configured to be used for a specific https (ssl) port (OS settings)

    2. securing the channel using wcf (message security) - this requires a certificate to be installed and configured to be used by the service (WCF settings)

    Which type of security do you want to use?


    Please mark posts as answers/helpful if it answers your question. Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper. Visit my blog: http://blogs.microsoft.co.il/blogs/idof
    • Marked as answer by Yi-Lun Luo Tuesday, August 02, 2011 8:59 AM
    Friday, July 29, 2011 2:02 PM
  • I have no experience in WCF. I have a basic WCF service application in .net 4 which writes to a database. I want the clients to provide a valid username and password to call the web methods, otherwise they would be able to write database rows which they don't have rights to access.


    I would suggest you first setup a wcf service without any access to database or any username/password authentications, and set the security mode to None. Make this service works first, then you can dig into it and tweak all those settings  to see the effect, then you can add more operations to the service.
    Tony Zeng qzcbs@163.com
    • Marked as answer by Yi-Lun Luo Tuesday, August 02, 2011 8:59 AM
    Sunday, July 31, 2011 5:21 PM