WCF/WF - Consuming WebServices and HTTP Basic Authentication

Asked By Pascal Delprat on 25-Sep-08 05:38 PM

Hi,

After reading this article :

http://www.eggheadcafe.com/articles/20051104.asp

I believed that I found a solution. But when I'm using VS2008, it seems that Authorization header is never send :(

ProxyService.WebService proxy = new ProxyService.WebService();

NetworkCredential netCredential = new NetworkCredential("Admin", "Password");

Uri uri = new Uri("http://www.xxx.com/webservice.asmx");

ICredentials credentials = netCredential.GetCredential(uri, "Basic");

proxy.Credentials = credentials;

// Be sure to set PreAuthenticate to true or else authentication will not be sent.

proxy.PreAuthenticate = true;

string result = proxy.Echo("Hello");

textBox1.Text = result;

Any idea ?

Thanks,

Pascal 

RE

Web Star replied to Pascal Delprat on 25-Sep-08 11:57 PM

Now here is a final block of sample code that combines both the HTTP BASIC with PreAuthenticate along with the SSL "Accept all certificates" code that (at least for me) got me onto first base:

MyWebServiceProxy myWebServiceProxy = new MyWebServiceProxy();
// set a default CertificatePolicy that accepts ALL Server certificates
System.Net.ServicePointManager.CertificatePolicy = 
                    new  TrustAllCertificatePolicy(); 
myWebServiceProxy.Url =this.endpointUrl;  //items from your appConfig
NetworkCredential netCredential = new NetworkCredential(this.endpointUserName,this.endpointPassword );
Uri uri = new Uri(this.endpointUrl);
ICredentials credentials = netCredential.GetCredential(uri, "Basic");
myWebServiceProxy.Credentials = credentials;
// Be sure to set PreAuthenticate to true or else authentication will not be sent.
myWebServiceProxy.PreAuthenticate = true;
myWebServiceProxy.MyMethod(myparam1,myparam2) ; 
http://www.eggheadcafe.com/articles/20051104.asp

RE

Web Star replied to Pascal Delprat on 25-Sep-08 11:59 PM

I recently made a web services call into WebMethods using basic authentication.  This authentication meant that we needed to modify the WSDL generated classes to handle the authentication. 

Here’s how it works.  I add a reference to the Web Service (Visual Studio generates the client code for calling the web service).  To this generated class I need to add the following method:

protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
    HttpWebRequest request;
    request = (HttpWebRequest)base.GetWebRequest(uri);

    if (PreAuthenticate)
    {
        NetworkCredential networkCredentials =
            Credentials.GetCredential(uri, "Basic");

        if (networkCredentials != null)
        {
            byte[] credentialBuffer = new UTF8Encoding().GetBytes(
                networkCredentials.UserName + ":" +
                networkCredentials.Password);
            request.Headers["Authorization"] =
                "Basic" + Convert.ToBase64String(credentialBuffer);
        }
        else
        {
            throw new ApplicationException("No network credentials");
        }
    }
    return request;
}

 This overrides the GetWebRequest() method of the System.Web.Services.Protocols.SoapHttpClientProtocol class that the web service client code derived from.

With Visual Studio 2005 the generated code code is a C# 2.0 partial classes.  As a result, regenerating the web services client code does not over-write the additional method.  To enable this, add a class file to your project and give it the same namespace and name as the generated System.Web.Services.Protocols.SoapHttpClientProtocol derived class.  The key is to use the partial modifier on the class header so that the GetWebRequest() method is added to the generated class.  (partial class Michaelis.MockService{…})

Regardless of using Visual Studio.NET 2005 or earlier, the client code requires that the network credentials are set and the PreAuthenticate property is assigned true.  Here is a sample client call:

Michaelis.MockService service = new Michaelis.MockService();

// Create the network credentials and assign
// them to the service credentials
NetworkCredential netCredential = new NetworkCredential("Inigo.Montoya", "Ykmfptd");
Uri uri = new Uri(service.Url);
ICredentials credentials = netCredential.GetCredential(uri, "Basic");
service.Credentials = credentials;

// Be sure to set PreAuthenticate to true or else
// authentication will not be sent.
service.PreAuthenticate = true;

// Make the web service call.
service.Method();

UPDATE - 4/14/2005

Comments on the post raised the question, "Why cant you just say request.Credentials = new NetworkCredential(username,password)."

The reason relates to interoperating with WebMethods specifically.  When just setting Credentials, the HTTP header looks like this:

POST /soap/rpc HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50113.0)
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
Host: <servername>:<port>
Content-Length: 779
Expect: 100-continue
Accept-Encoding: gzip

Notice, there is no Authentication item even though PreAuthenticate is set to true.

RE

Web Star replied to Pascal Delprat on 25-Sep-08 11:59 PM

To set up:

1. Build BasicAuthMod.dll, and copy it to your web application’s bin directory on your server.

2. Make the following changes to your web.config file (in the <system.web> section):

  • Change authentication line to: <authentication mode=”None” />.  We need to disable the built-in ASP.NET authentication.
  • Add an authorization section if you wish, such as

       <authorization>
    <deny users=”?” />
    </authorization>

    If you use BasicAuthMod to authenticate, you can still leverage the built-in ASP.NET authorization capabilities.

  • Add the following lines to wire the BasicAuthMod.dll into the ASP.NET pipeline.

       <httpModules>
    <add name=”BasicAuthenticationModule”
    type=”Rassoc.Samples.BasicAuthenticationModule,BasicAuthMod” />
    </httpModules>

3. Make the following changes to your web.config file (in the <configuration> section), and edit appropriately:

<appSettings>
<add key=”Rassoc.Samples.BasicAuthenticationModule_Realm”
value=”RassocBasicSample” />
<add key=”Rassoc.Samples.BasicAuthenticationModule_UserFileVpath”
value=”~/users.xml” />
</appSettings>

4. Copy the sample users.xml file into your virtual directory

http://www.rassoc.com/gregr/weblog/2002/06/26/web-services-security-http-basic-authentication-without-active-directory/

solutions
Perry replied to Pascal Delprat on 26-Sep-08 12:34 AM

I hope that there must be a Active Directory present to verify the credentials. The previous replies assumes there is no active directory. I have done one of my web service to track the user's timing where I have used HTTP authentication. You will have three options for this:

   1. use NTLM authentication

   2. use Plink utility which uses SSH to authenticate user and most secure

   3. Kerberose Authentication

I have used Kerberos Authentication as it prooved to be most secure and the attacks like snooping, spoofing and interception will be avoided. For this you just need to setup KDC - key distribution center on one of your server which will identifies the user via unique key called kerberos realm. For more details please go over how to install kerberose authentication from scratch.

-Paresh

Thanks
Pascal Delprat replied to Web Star on 26-Sep-08 09:53 AM
Many thanks, it works now. It's just strange that it not supported directly on generated class. Thanks again, Pascal