Problem consuming IIS hosted service - locally and remotely - c#

I build 2 services for the project I'm currently working on.
1) a simple client-server request service using basicHttpBinding
2) a callback dataservice using WSDualHttpBinding
with some twiddling I was able to host both in IIS and I can visit both :/Service.svc in my browser locally as well as on another machine that shall interact with both services as well.
here is the app-config section for the callback dataservice
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="myBehavior">
<clientVia />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsDualHttpBinding>
<binding name="WSDualHttpBinding_IDataService">
<security mode="None">
<message negotiateServiceCredential="false" clientCredentialType="None" />
</security>
</binding>
</wsDualHttpBinding>
</bindings>
<client>
<endpoint address="http://<address>:50111/Service.svc/service"
binding="wsDualHttpBinding" bindingConfiguration="WSDualHttpBinding_IDataService" behaviorConfiguration="myBehavior"
contract="CallbackService.IDataService" name="WSDualHttpBinding_IDataService">
</endpoint>
</client>
</system.serviceModel>
the basicHttpBinding looks quite similar - only to another port
I set IIS to anonymous authentication .. User IUSR, no password ..
Originally it worked locally - but from the remote computer I received authentication errors .. so I tweaked some more - and now I get a timeout even locally .. though Visual Studio interacts with the service reference to both services just fine.
How do I get the service "to cooperate" again
And I'm at my wits end - any help greatly appreciated!?
Edit: first problem solved - local communication restored
public ClientCallbacks myCallbacks=new ClientCallbacks();
public DuplexChannelFactory<IDataService> channelFactory { get; set; }
public IDataService channel;
public int connect() {
WSDualHttpBinding binding = new WSDualHttpBinding()
{ Name = "WSDualHttpBinding_IDataService" };
this.channelFactory = new DuplexChannelFactory<IDataService>(
mainForm.dbCommunication.myCallbacks,
binding,
new EndpointAddress("http://<address>:50111/Service.svc/service"));
this.channel = this.channelFactory.CreateChannel();
...
...
channel.AddListener(int-parameter); // announce new callback-partner
...
...
this.newListe = this.myCallbacks.receivedTaskList; //get Tasklist over callback
This works locally - but calling it remotely I get a "new" error
System.ServiceModel.Security.SecurityNegotiationException:
The caller was not authenticated by the service. --->
System.ServiceModel.FaultException: The request for security token could not be satisfied
because authentication failed.
at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault
(Message message, EndpointAddress target)
at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody
(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)
--- End of inner exception stack trace ---
And again I struggle to find a clue / solution .. I set IIS authentication to anonymous ... but it seems the communication is blocked before that point is reached .. What do I have to do to reach the service from a remote machine?

It seems that we should provide the credentials to have the client authenticated by the server-side. which sort of credentials should be supplied relies on the binding configuration of the server-side.
By default. The security mode of the WsDualhttpbinding is configured with Message security mode, and the client credential type is Windows.
These below code snippets are equivalent.
WSDualHttpBinding binding = new WSDualHttpBinding();
WSDualHttpBinding binding = new WSDualHttpBinding();
binding.Security.Mode = WSDualHttpSecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
From your code snippet, I didn’t see your configuration of the server-side. If Windows credential is required, please consider the below code segments.
//ChannelFactory<IService> channelFactory = new ChannelFactory(...);
channelFactory.Credentials.Windows.ClientCredential.UserName = "administrator";
channelFactory.Credentials.Windows.ClientCredential.Password = "123456";
IService svc = channelFactory.CreateChannel();
Feel free to let me know if the problem still exists.

Related

WCF Visual Studio 2017 local development

March 27, 2020 UPDATE
It has been 4 days and I have padded my office walls now. :)
BIG QUESTION
This will only work locally if I change the following settings
Anonymous Authentication = Enabled
Does anybody know how to get around this issue with
- IIS Express
- Visual Studio 2017
I stepped out of the work code and created a test locally and I am only stuck on one issue now.
Authentication is now my BLOCKER
I will need to use the settings for deployment as I do not have any control on the DEV | SIT | UAT | PROD --- IAAS or PAAS - I can only code CI and CD.
I removed all the ---- Configuration
This was not in the original source code
I do have to comment out the code in the
MultipleBindingServiceHost.cs file
The localhost is complaining about the 2 URLs (I will revisit once I get all the security holes in the code fixed.
string rawUrl = ConfigurationManager.AppSettings["tsRawUrl"];
ServiceEndpoint endpoint = AddServiceEndpoint(typeof(ITicketService), httpBinding, baseAddress, new Uri(rawUrl));
endpoint.Behaviors.Add(new WebHttpBehavior());
#if (!DEBUG)
string vanityUrl = ConfigurationManager.AppSettings["tsVanityUrl"];
ServiceEndpoint endpoint2 = AddServiceEndpoint(typeof(ITicketService), httpBinding, baseAddress, new Uri(vanityUrl));
endpoint2.Behaviors.Add(new WebHttpBehavior());
#endif
March 23, 2020
I have been trying to figure out WCF and Local Machine development for the past week and now I am coming to stackoverflow for the communities assistance.
I have been tasked with supporting an application that has two WCF Services
Web.config appSettings Settings as follows:
<appSettings>
<add key="tsVanityUrl" value="http://localhost:1574/TicketService.svc" />
<add key="tsRawUrl" value="http://localhost:1574/TicketService.svc" />
<add key="fsVanityUrl" value="http://localhost:1574/FileService.svc" />
<add key="fsRawUrl" value="http://localhost:1574/FileService.svc" />
</appSettings>
Web.config system.serviceModel
<system.serviceModel>
<!-- START RBD Additions for Local Development -->
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<behaviors>
<serviceBehaviors>
<behavior name="TicketBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
<behavior name="FileBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="TicketSystem.TicketService" behaviorConfiguration="TicketBehavior">
<endpoint address="/TicketService.svc"
binding="basicHttpBinding"
contract="TicketSystem.ITicketService"
/>
</service>
<service name="TicketSystem.FileService" behaviorConfiguration="FileBehavior">
<endpoint address="/FileService.svc"
binding="basicHttpBinding"
contract="TicketSystem.IFileService"
/>
</service>
</services>
<!-- END RBD Additions for Local Development -->
</system.serviceModel>
I keep getting the following error:
The value could not be added to the collection, as the collection already contains an item of the same type: 'System.ServiceModel.Description.ServiceMetadataBehavior'. This collection only supports one instance of each type.
Parameter name: item
This points me to the MultipleBindingServiceHost.cs file
protected override void ApplyConfiguration()
{
base.ApplyConfiguration();
ServiceMetadataBehavior mexBehavior = new ServiceMetadataBehavior();
Description.Behaviors.Add(mexBehavior);
WebHttpBinding httpBinding = new WebHttpBinding();
foreach (Uri baseAddress in BaseAddresses)
{
if (baseAddress.Scheme == Uri.UriSchemeHttp)
{
httpBinding.Security.Mode = WebHttpSecurityMode.None;
mexBehavior.HttpGetEnabled = true;
}
else if (baseAddress.Scheme == Uri.UriSchemeHttps)
{
httpBinding.Security.Mode = WebHttpSecurityMode.Transport;
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
mexBehavior.HttpsGetEnabled = true;
}
//ServiceEndpoint endpoint = AddServiceEndpoint(typeof(TicketSystem.ITicketService),
// httpBinding,
// baseAddress);
//endpoint.Behaviors.Add(new WebHttpBehavior());
//Fix for 404 Vanity URL Issue
string rawUrl = ConfigurationManager.AppSettings["tsRawUrl"];
ServiceEndpoint endpoint = AddServiceEndpoint(typeof(ITicketService), httpBinding, baseAddress, new Uri(rawUrl));
endpoint.Behaviors.Add(new WebHttpBehavior());
string vanityUrl = ConfigurationManager.AppSettings["tsVanityUrl"];
ServiceEndpoint endpoint2 = AddServiceEndpoint(typeof(ITicketService), httpBinding, baseAddress, new Uri(vanityUrl));
endpoint2.Behaviors.Add(new WebHttpBehavior());
break;
}
}
}
I know I am very close and probably missing something very simple, but after spending multiple days on this I have to post on stackoverflow to get the services running on my local maching.
WCF service application project doesn’t support multiple service contracts in one service host. One service project has one service host, we need to specify the service implemented class during starting the host. Consequently, it is impossible to host multiple service contracts in one host. We could create multiple hosts in a console/Windows NT service application so as to support multiple service contracts.
using (ServiceHost sh = new ServiceHost(typeof(TestService)), sh2 = new ServiceHost(typeof(MyService1)))
{
sh.Opened += delegate
{
Console.WriteLine("service is ready");
};
sh.Closed += delegate
{
Console.WriteLine("service is closed");
};
sh2.Opened += delegate
{
Console.WriteLine("service2 is ready...");
};
sh2.Closed += delegate
{
Console.WriteLine("Service is closed...");
};
sh.Open();
sh2.Open();
Console.ReadLine();
sh.Close();
sh2.Close();
}
Feel free to let me know if there is anything I can help with.

Consuming Web Services - Confusion on Setup

I recently added a remote WSDL file for a web service to my app_webreferences folder.
I can see all the objects in that WSDL file - however, the problem is that the company that i'm integrating with sent me two urls.
a wsdl: https://website.com/SOAP/Queue?wsdl
an endpoint: https://website.com/SOAP/Queue?api_key=xxxxxx
So - when I added the app_webreference I used the first url - it worked, added it to project.
I then proceeded to setup my bindings as such:
<wsHttpBinding>
<binding name="QueueWebServiceBinding">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
My client endpoint looks like this:
<endpoint address="https://website.com/SOAP/Queue?wsdl"
binding="wsHttpBinding" bindingConfiguration="QueueWebServiceBinding"
contract="MyWebService" name="QueueWebServicePort" />
</client>
The thing is, I cannot for the life of me get this to work. I'm not sure where I should use the endpoint url (#2) in any of this. It seems that when I switch it out in the endpoint address (#2) above I receive a response of:
...<env:Fault..... does not contain operation meta data for ... </env:Fault>...
I'm really confused on how to set this all up.
Another thing to note, they do require authentication... so my actual code looks something like this...
MyWebServiceClient client = new MyWebServiceClient();
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
var t = client.doSomething();
I've been googling/searching stackoverflow for the past few hours but have had little success figuring out the right way to set this all up.
Appreciate any help.
The first endpoint is for the WSDL. The second endpoint is for the actual service itself. So you need to change your endpoint in your config file as follows:
<client>
<endpoint address="https://website.com/SOAP/Queue?api_key=xxxxxx"
binding="wsHttpBinding"
bindingConfiguration="QueueWebServiceBinding"
contract="MyWebService" name="QueueWebServicePort" />
</client>
Since you've already tried switching out the endpoint, try using the second endpoint to add the service reference and see if that works (if you haven't already).
If neither of those work, then I'd suggest talking to the vendor's support staff to see if they can help, since they should know everything you need to do to access the service.

How to supply both UserName and Client Certificate in WCF client (why does this example work)?

Consider a WCF service in which the intent is to have Client Certificates required at the Transport layer (Client Certificates set to "Required" in IIS). As well, there will be username authentication at the message layer.
Now I've seen this question already:
WCF Client Certificate AND UserName Credentials forbidden
and I can somewhat understand what's going on there and realize that inherently WCF does not allow both. I went through the same steps in code as the poster in the link referenced above and found the same result...the message-level UserName credentials were being passed (in the SOAP header in my case), but the Client Cert (despite being attached when the request client is viewed in VS debug) was not actually being processed by the endpoint.
So now comes the part that has me confused. I decided to hack it somewhat. I'm wondering why this works exactly like I'm wanting...it gets past IIS Client Cert requirement, the UserName gets passed to the WCF Service and all just works. Yet WCF does not allow me to do it just using WCF config files or code (that I can find). Why?
// sets up a proxy client based on endpoint config
// basically just here to get the URL.
this.InitializeSubmitClient();
// these get used to create the HttpWebRequest
string url = this.submitClient.Endpoint.Address.ToString();
string action = "SubmitCDA";
// this deserializes an XML file which is the "shell" of SOAP document and inject username/password into SOAP Security node
XmlDocument soapEnvelopeXml = XMLHelper.CreateSoapDocument(this.txtSubmitCdaXmlFile.Text, this.txtAdAccount.Text, this.txtPassword.Text);
HttpWebRequest webRequest = XMLHelper.CreateWebRequest(url, action);
// saves the SOAP XML into the webRequest stream.
XMLHelper.InsertSoapEnvelopeIntoWebRequest(soapEnvelopeXml, webRequest);
// attach the cert
if (this.chkSendClientCert.Checked)
{
X509Certificate myCert = X509Certificate.CreateFromCertFile(#"C:\temp\CDX-IHAT_DevClientCert.cer");
webRequest.ClientCertificates.Add(myCert);
}
else
{
webRequest.ClientCertificates.Clear();
}
// begin async call to web request.
IAsyncResult asyncResult = webRequest.BeginGetResponse(null, null);
To further complicate matters, the WCF Service that this applies to is a BizTalk service.
Here's how I ended up doing it.
The Server Config:
<customBinding>
<binding name="CustomCDARequestEndpointBinding">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="UserNameOverTransport" />
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
The Client Config:
<system.ServiceModel>
<bindings>
<customBindings>
<binding name="CustomBinding_ITwoWayAsync">
<security defaultAlgorithmSuite="Default"
authenticationMode="UserNameOverTransport"
requireDerivedKeys="true"
includeTimestamp="true"
messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
>
<localClientSettings detectReplays="false" />
<localServiceSettings detectReplays="false" />
</security>
<textMessageEncoding messageVersion="Soap11" />
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="ohBehave">
<clientCredentials useIdentityConfiguration="false">
<clientCertificate findValue="6D0DBF387484B25A16D0E3E53DBB178A366DA954" storeLocation="CurrentUser"
x509FindType="FindByThumbprint" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="https://myservice/CDASubmitService/CDASubmit.svc"
binding="customBinding" bindingConfiguration="SubmitDev" behaviorConfiguration="ohBehave"
contract="CDASubmitService.CDASubmit" name="SubmitDev" />
</client>
</system.serviceModel>
The key to getting it working was the <httpsTransport requireClientCertificate="true" /> element and the <security authenticationMode="UserNameOverTransport" element/attribute.
This configuration allowed me to submit a message to a WCF (BizTalk) service completely through configuration files, with no changes to actual code. It still allows me to submit to it VIA WebRequest as well, as shown above.
I have to give credit to this post:
WCF Client Certificate AND UserName Credentials forbidden
as well as this one:
Translate non-BizTalk WCF config into BizTalk WCF-Custom endpoint
for finally getting me on the right track. I always shied away from Custom Bindings in WCF because I assumed it was overkill, but they are really nothing crazy, just a way to supply more detailed config than is available out of the box.

Castle Windsor WCF Facility HTTPS

I successfully integrated the Caste WCF Facility with my services. Now I try to configure an HTTPS communication based on BasicHttpBinding.
According the following blog post, this should not be a big deal: http://blog.adnanmasood.com/2008/07/16/https-with-basichttpbinding-note-to-self/
Here's my setup. On client-side, I configure the Windsor container using the following code:
BasicHttpBinding clientBinding = new BasicHttpBinding();
// These two lines are the only thing I changed here to allow HTTPS
clientBinding.Security.Mode = BasicHttpSecurityMode.Transport;
clientBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
// Everything else worked well with HTTP
clientBinding.MaxReceivedMessageSize = 163840;
clientBinding.MaxBufferSize = (int)clientBinding.MaxReceivedMessageSize;
container = new WindsorContainer();
container.AddFacility<WcfFacility>();
container.Register(
Component.For<IClientService>()
.AsWcfClient(new DefaultClientModel {
Endpoint = WcfEndpoint.BoundTo(clientBinding)
.At(configuration.Get(CFGKEY_SERVICE_CLIENT))
})
);
Besides that, I don't have any configuration on client-side. This worked well using HTTP communication.
The server side got the following configuration within Web.config:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
When I'm trying to connect through https://, I get the following exception:
System.ServiceModel.EndpointNotFoundException: There was no endpoint listening at https://myuri.com/Services/Client.svc that could accept the message.
Any ideas what's missing?
Thank you in advance.
Fixed it by myself, the above code is correct, the problem has been located inside my Windsor service installer on server-side. The following snippet for each service point will do the job.
As you can see, I've put the absolute service URI as well as transport mode (either http or https) into the app settings section of the Web.config file. Of course it would be nice to use the default WCF configuration model but this did not work.
.Register(
Component
.For<MyNamespace.ContractInterface>()
.ImplementedBy<MyNamespace.ImplementationClass>()
.Named("ServiceName").LifestylePerWcfOperation()
.AsWcfService(
new DefaultServiceModel().Hosted().AddEndpoints(
WcfEndpoint.BoundTo(new BasicHttpBinding {
Security = new BasicHttpSecurity {
Mode = (WebConfigurationManager.AppSettings["Service.WCFFacility.TransportMode"] == "http") ? BasicHttpSecurityMode.None : BasicHttpSecurityMode.Transport,
Transport = new HttpTransportSecurity {
ClientCredentialType = HttpClientCredentialType.None
}
}
}).At(WebConfigurationManager.AppSettings["Service.WCFFacility.Endpoint"])
)
)
);
The server configuration remains as shown above, except the app setting keys.
Hope this might help someone experiencing similar problems.

WCF Error : Manual addressing is enabled on this factory, so all messages sent must be pre-addressed

I've got a hosted WCF service that I created a custom factory for, so that this would work with multiple host headers:
/// <summary>
/// Required for hosting where multiple host headers are present
/// </summary>
public class MultipleHostServiceFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
List<Uri> addresses = new List<Uri>();
addresses.Add(baseAddresses[0]);
return base.CreateServiceHost(serviceType, addresses.ToArray());
}
}
I'm pretty sure that my config files are now right, on both client and server (can be seen here).
The error I'm getting appears to be related to the factory:
Manual addressing is enabled on this factory, so all messages sent must be pre-addressed.
public string GetData(int value) {
return base.Channel.GetData(value);
}
The error occurs at line return base.Channel.GetData(value);.
I experienced this error and the problem was resolved by adding the WebHttpBehavior (line 2 below):
var factory = new ChannelFactory<IService>(new WebHttpBinding(), uri);
factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
var proxy = factory.CreateChannel();
I added a service reference as usual and got this error. Turns out all I had to do was to amend the client config to use an endpoint config with a behaviour specifing webhttp
<client>
<endpoint address="http://localhost:9000/GeoConverterService/GeoConverterService.svc"
binding="webHttpBinding"
contract="GeoConverter.IGeoConverterService"
behaviorConfiguration="webhttp"/>
</client>
<behaviors>
<endpointBehaviors>
<behavior name="webhttp">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
I don't think this necessarily has anything to do with your factory.
See
http://msdn.microsoft.com/en-us/library/system.servicemodel.channels.transportbindingelement.manualaddressing.aspx
or others among the first few Bing hits for "manualaddressing". It sounds like the binding being used is incompatible with some other portion of the stack/messaging logic.
So this has finally come to an end!
Brian - thanks for your guidance on this. The bindings were mis-aligned b/t the client and server, and I finally ended up going with the following in both:
<basicHttpBinding>
<binding name="TransportSecurity">
<security mode="Transport">
<transport clientCredentialType="None"/>
</security>
</binding>
</basicHttpBinding>
... and setting their endpoint binding and bindingConfiguration attributes accordingly:
<endpoint binding="basicHttpBinding"
bindingConfiguration="TransportSecurity"
contract="ServiceReference1.IService"
name="WebHttpBinding_IService"
address="https://mysslserver.com/Service.svc" />
Since this is relatively new turf for me, just the explanation of why those errors were popping up lead me in the right direction :).

Categories

Resources