Jeff Dege
Jeff Dege

Reputation: 11740

Calling third party secure webservice through WCF - authentication problems

I'm writing a client against a vendor's webservice, using WCF in Visual Studio 2010. I have no ability to change their implementation or configuration.

Running against an install on their test server, I had no problems. I added a service reference from their wsdl, set the url in code, and made the call:

var client = new TheirWebservicePortTypeClient();
client.Endpoint.Address = new System.ServiceModel.EndpointAddress(webServiceUrl);

if (webServiceUsername != "")
{
    client.ClientCredentials.UserName.UserName = webServiceUsername;
    client.ClientCredentials.UserName.Password = webServicePassword;
}

TheirWebserviceResponse response = client.TheirOperation(myRequest);

Simple and straightforward. Until they moved it to their production server and configured it to use https. Then I got this error:

The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Basic realm='.

So I went looking for help. I found this: Can not call web service with basic authentication using wcf.

The approved answer suggested this:

BasicHttpBinding binding = new BasicHttpBinding();

binding.SendTimeout = TimeSpan.FromSeconds(25);

binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = 
                              HttpClientCredentialType.Basic;

EndpointAddress address = new EndpointAddress(your-url-here);

ChannelFactory<MyService> factory = 
             new ChannelFactory<MyService>(binding, address);

MyService proxy = factory.CreateChannel();

proxy.ClientCredentials.UserName.UserName = "username";
proxy.ClientCredentials.UserName.Password = "password";

Which also seemed simple enough. Except for my trying to figure out which of the multitude of classes and interfaces that were generated from the wsdl to make the service reference I should use in place of the "MyService", above.

My first try was to use "TheirWebservicePortTypeClient" - the class I had instantiated in the previous version. That gave me a runtime error:

The type argument passed to the generic ChannelFactory class must be an interface type.

So I dug into the generated code, a bit more. I saw this:

public partial class TheirWebservicePortTypeClient
:
    System.ServiceModel.ClientBase<TheirWebservicePortType>, 
    TheirWebservicePortType
{
    ...
}

So I tried instantiating ChannelFactory<> with TheirWebservicePortType.

This gave me compile-time errors. The resulting proxy didn't have a ClientCredentials member, or a TheirOperation() method.

So I tried "System.ServiceModel.ClientBase".

Instantiation ChannelFactory<> with it still gave me compile-time errors. The resulting proxy did have a ClientCredentials member, but it still didn't have a TheirOperation() method.

So, what gives? How do I pass a username/password to an HTTPS webservice, from a WCF client?

==================== Edited to explain the solution ====================

First, as suggested, instantiation the factory with TheirWebservicePortType, adding the username and password to the factory.Credentials, instead of to proxy.ClientCredentials worked fine. Except for one bit of confusion.

Maybe it's something to do with the odd way the wsdl is written, but the client class, TheirWebservicePortTypeClient, defined TheirOperation as taking a Request argument and returning a Response result. The TheirWebservicePortType interface defined TheirOperation as taking a TheirOperation_Input argument and returning a TheirOperation_Output result, where TheirOperation_Input contained a Request member and TheirOperation_Output contained a Response member.

In any case, if I constructed a TheirOperation_Input object from the passed Request, the call to the proxy succeeded, and I could then extract the contained Response object from the returned TheirOperation_Output object:

TheirOperation_Output output = client.TheirOperation(new TheirOperation_Input(request));
TheirWebserviceResponse response = output.TheirWebserviceResponse;

Upvotes: 2

Views: 1903

Answers (1)

Richard Blewett
Richard Blewett

Reputation: 6119

You add the credentials to the ChannelFactory Credentials property

Upvotes: 1

Related Questions