I am attempting to secure a new .Net 4.5 WCF service using HTTPS / SSL, Basic client credentials and the WebHttpBinding. From reading up online I found a good series of Blog Posts from Allen Conway which I have used as a template.
WCF configuration
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webInteropSecureBinding" allowCookies="false" maxBufferPoolSize="2097152" maxBufferSize="2097152" maxReceivedMessageSize="2097152">
<security mode="Transport">
<transport clientCredentialType="Basic"></transport>
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service name="PsmDataProvider.PsmProvider" behaviorConfiguration="SecureRest">
<clear />
<endpoint address="" binding="webHttpBinding" bindingConfiguration="webInteropSecureBinding" name="PsmProvider" contract="PsmDataProvider.IPsmProvider" behaviorConfiguration="webHttpBehavior" />
<endpoint address="mex" binding="mexHttpsBinding" name="mex" contract="IMetadataExchange" listenUriMode="Explicit" />
<host>
<baseAddresses>
<add baseAddress="https://localhost:44300/PsmProvider/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="SecureRest">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="PsmDataProvider.Security.CustomerUserNamePasswordValidator, PsmDataProvider"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="webHttpBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
CustomerUserNamePasswordValidator
I have stubbed out the CustomerUserNamePasswordValidator implementation and have confirmed that the constructor is called before the exception is raised.
using System;
using System.IdentityModel.Selectors;
namespace PsmDataProvider.Security
{
internal class CustomerUserNamePasswordValidator : UserNamePasswordValidator, ICustomerUserNamePasswordValidator
{
public CustomerUserNamePasswordValidator()
{
}
public override void Validate(string userName, string password)
{
if (userName == null) throw new ArgumentNullException("userName","The username must be provided in the request to access this service");
if (password == null) throw new ArgumentNullException("password", "The password must be provided in the request to access this service");
}
}
}
When I try to run the code in VS2012 through IIS Express the service fails to start with the below error.
If I remove the clientCredentialType from the configuration then it works but I require the additional security of using the username / password validation on the service and possibly at a method level in the future.
Is this something I have configured incorrectly in the WCF config or a problem with the configuration in IISExpress?
Please help...
The issue appears to be when using Basic Authentication when hosting the service in IIS as IIS wants to handle the authentication.
This is discussed in this MSDN blog post
In the version of WCF that shipped with .Net Framework 3.0 we didn't
support custom validators with transport level HTTP security. We
received much feedback from the community that this was a highly
desired feature, so I'm happy to say we added support for this
scenario in the 3.5 release of the .Net Framework. Note that this is
only supported under self hosted services.
There is a resolution as discussed in Allen Conway's Blog Post by implementing a custom authorisation manager derived from ServiceAuthorizationManager
CustomAuthorizationManager
public class CustomAuthorizationManager : ServiceAuthorizationManager
{
private const string UserName = "username";
private const string Password = "password";
protected override bool CheckAccessCore(OperationContext operationContext)
{
string authHeader = WebOperationContext.Current.IncomingRequest.Headers["Authorization"];
if ((authHeader != null) && (authHeader != string.Empty))
{
string[] svcCredentials = System.Text.ASCIIEncoding.ASCII
.GetString(Convert.FromBase64String(authHeader.Substring(6)))
.Split(':');
var user = new { Name = svcCredentials[0], Password = svcCredentials[1] };
if ((user.Name.Equals(UserName) && user.Password.Equals(Password)))
return true;
else
return false;
}
else
{
WebOperationContext.Current.OutgoingResponse.Headers.Add("WWW-Authenticate: Basic realm=\"PsmProvider\"");
throw new WebFaultException(HttpStatusCode.Unauthorized);
}
}
}
Config
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webInteropSecureBinding" allowCookies="false" maxBufferPoolSize="51200" maxBufferSize="51200" maxReceivedMessageSize="51200">
<security mode="Transport"/>
</binding>
</webHttpBinding>
</bindings>
<services>
<service name="PsmDataProvider.PsmProvider" behaviorConfiguration="SecureRest">
<clear />
<endpoint binding="webHttpBinding" bindingConfiguration="webInteropSecureBinding"
name="PsmProvider" contract="PsmDataProvider.IPsmProvider" behaviorConfiguration="webHttpBehavior" />
<endpoint address="mex" binding="mexHttpsBinding" name="mex" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="https://localhost:44300/PsmProvider/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="SecureRest">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceAuthorization serviceAuthorizationManagerType="PsmDataProvider.Security.CustomAuthorizationManager, PsmDataProvider"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="webHttpBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
Note
Also note a comment from Travich regarding the IIS / IIS Express configuration
Travich said... One thing to help other users. It was briefly stated,
but something I overlooked... Turn off Basic Auth in IIS and remove
tag from your webHttpBinding!
Works for me.
Related
I've been trying to figure this out for a couple of days. I know that I'm close but can't quite figure out what I'm missing.
The service is in a website hosted on IIS
Basic authentication is enabled for the site, and all others including anonymous authentication is disabled.
I have created a self signed certificate and set up a https binding
I have overridden the Validate method of the UsernamePasswordValidator but when I attach a break point it isn't reached. So my problem is likely something to do with this.
When I attempt to access my service method at https://localhost/Services/JobSiteService.svc/rest/get I am continually prompted for username and password as i receive a 401 unauthorized error.
Can anyone see what I'm doing wrong?
Appropriate code below:
Web.Config
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webHttpTransportSecurity">
<security mode="Transport">
<transport clientCredentialType="Basic" />
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service name="Validity.WebService.JobSiteService" behaviorConfiguration="SecureRestBehavior">
<endpoint address="rest" binding="webHttpBinding" behaviorConfiguration="RESTBehavior" bindingConfiguration="webHttpTransportSecurity" contract="Validity.WebService.Contracts.IJobSiteService" />
<endpoint address="meta" binding="mexHttpsBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="SecureRestBehavior">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Validity.WebService.IdentityValidator, Validity.WebService" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="RESTBehavior" >
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<protocolMapping>
<add binding="webHttpBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
Service Contract:
[ServiceContract]
public interface IJobSiteService
{
[OperationContract]
[WebInvoke(Method="GET", ResponseFormat=WebMessageFormat.Json)]
List<JobSite> Get();
}
Custom Validator (it's in the namespace "Validity.WebService" but the code formatting broke when I included it.):
public override void Validate(string userName, string password)
{
using (var context = new ValidityContext())
{
using (var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context)))
{
var user = userManager.Find(userName, password);
if (user == null)
{
var msg = String.Format("Unknown Username {0} or incorrect password {1}", userName, password);
throw new FaultException(msg);//the client actually will receive MessageSecurityException. But if I throw MessageSecurityException, the runtime will give FaultException to client without clear message.
}
else
{
SessionOperationContext.Current.Items["CurrentUser"] = user;
}
}
}
}
Any help with this would be greatly appreciated.
Finally figured this out.
The solution was to disable basic authentication on the IIS server and override the CheckAccessCore method of the ServiceAuthorizationManager to validate the user.
I have a WCFservice that is a HTTPS REST API which exposes a few WebGet methods. I am using transport level security over HTTPS. This is all working fine. I implemented a custom UserNamePasswordValidator, overriding the Validate method as per MSDN documentation.
public override void Validate(string userName, string password)
{
WebFaultException rejectEx = new WebFaultException(HttpStatusCode.Unauthorized);
rejectEx.Data.Add("HttpStatusCode", rejectEx.StatusCode);
if (null == userName || null == password)
throw rejectEx;
if (userName == "someusername" && password == "somepassword") return;
throw rejectEx;
}
Accessing the service in a browser via HTTPS only (http is disabled), username and password are prompted for, leaving one out or entering one or both incorrectly results in the prompt coming back as I am throwing a 401.
The issue I have is that Validate is never called if both the username and password are left out when accessing the service. I need to be able to handle this and return an informative error, or even just a 401, to the browser/client.
Debugging shows that Validate is never fired when leaving out both the username and password. This means that in a browser you end up with a blank page and no errors.
How can I handle this situation and return a response to the client prompting for credentials again?
Edit to include config info:
<behaviors>
<serviceBehaviors>
<behavior name="RESTEndpointBehaviour">
<serviceMetadata httpsGetEnabled="true" httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="MyAPI.CustomAuthenticator, MyAPI"
/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="RESTEndpointBehaviour" name="MyAPI.RestService">
<endpoint binding="webHttpBinding" bindingConfiguration="BasicHttpSSLBinding"
contract="MyAPI.IRestService" />
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="https://localhost:443" />
</baseAddresses>
</host>
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="BasicHttpSSLBinding">
<security mode="Transport">
<transport clientCredentialType="Basic"/>
</security>
</binding>
</webHttpBinding>
</bindings>
I ended up solving this puzzle by implementing a serviceAuthorization instead. As per the below blog article, with a few tweaks to match my application.
http://www.allenconway.net/2012/07/using-basic-authentication-in-rest.html
I am struggeling with a problem with WCF Service configuration.
I have following config:
<behaviors>
<serviceBehaviors>
<behavior name="SampleWebBehavior">
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
<behavior name="MyServiceTypeBehaviors">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https"/>
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="false"/>
It works perfectly, I can try the methods with WcfTestClient.exe and get correct responses. But I need the binding to be webHttpBinding, so I can see the results in browser and create JSON requests and responses.
But when I change the binding to webHttpBinding, it throws an error:
The message with Action '' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).
Thaks for any help.
To pull this off you need to do a few things in your config
1) add another endpoint to your service, you can see below I have both basicHttp and webHttp
<system.serviceModel>
<services>
<service name="<serivceclass>" behaviorConfiguration="DefaultBehavior">
<endpoint binding="basicHttpBinding" contract="<serviceinterface>" bindingConfiguration="soapBinding"/>
<endpoint address="json" binding="webHttpBinding" behaviorConfiguration="jsonBehavior" contract="<serviceinterface>" bindingConfiguration="jsonBinding"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</system.serviceModel>
2) add your binding config (again you'll see both web and basichttp)
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="soapBinding" maxBufferPoolSize="9000000" maxBufferSize="9000000" maxReceivedMessageSize="9000000">
<readerQuotas maxArrayLength="9000000" maxBytesPerRead="9000000" maxDepth="9000000" maxNameTableCharCount="9000000" maxStringContentLength="9000000"/>
<security mode="None">
<transport clientCredentialType="None"/>
</security>
</binding>
</basicHttpBinding>
<webHttpBinding>
<binding name="jsonBinding">
<security mode="None">
<transport clientCredentialType="None"/>
</security>
</binding>
</webHttpBinding>
</bindings>
</system.serviceModel>
3) Setup your behaviors - notice the names and how they correlate tot the endpoints listed in step 1
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="jsonBehavior">
<webHttp defaultOutgoingResponseFormat="Json" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="DefaultBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<dataContractSerializer maxItemsInObjectGraph="9000000" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
While some of the stuff I enable and config are optional this allows you to access the service both ways, one way using web and json stuff and another using the tooling built into visual studio when you are not working in javascript,etc.
Note1: the endpoint when you are using the json part will be for example http://myhost.com/myservice.svc/json/MyMethodName, You can change this by modifying the "address" attribute on the appropriate endpoint line for your service (see how basic address is empty and the webHttp is 'json')
one possibility of the source of this error is: When you changed the configuration you made the change on the server or the client but not on both. The configuration on the server and client must match.
Hello and thank you for reading.
I'm trying to get a service hosted in IIS 7.5, that has multiple endpoints exposed.
I have a feeling the problem lies within my web.config, but I'll post my service code in here. There's no interface file, as I'm using the newer features of WCF 4, there's also no .svc file.
All the routing, from my understanding is handled in Global.asax.cs using the RouteTable feature.
Regardless, onto the code / config -
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
// NOTE: If the service is renamed, remember to update the global.asax.cs file
public class Service1
{
// TODO: Implement the collection resource that will contain the SampleItem instances
[WebGet(UriTemplate = "HelloWorld")]
public string HelloWorld()
{
// TODO: Replace the current implementation to return a collection of SampleItem instances
return "Hello World!";
}
}
And now, the config with the changes I thought would need to be made (I'm not sure if I needed to keep the standardEndpoints block, but with or without it I'm still getting error messages. -
<services>
<service name="AiSynthDocSvc.Service1" behaviorConfiguration="HttpGetMetadata">
<endpoint name="rest"
address=""
binding="webHttpBinding"
contract="AiSynthDocSvc.Service1"
behaviorConfiguration="REST" />
<endpoint name="soap"
address="soap"
binding="basicHttpBinding"
contract="AiSynthDocSvc.Service1" />
<endpoint name="mex"
address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="REST">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="HttpGetMetadata">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<standardEndpoints>
<webHttpEndpoint>
<!--
Configure the WCF REST service base address via the global.asax.cs file and the default endpoint
via the attributes on the <standardEndpoint> element below
-->
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
</webHttpEndpoint>
</standardEndpoints>
The Global.asax.cs file was left alone.
Again I'm pretty sure it has something to do with my config. The error I'm getting when I try to access any of the endpoints defined is -
The endpoint at '' does not have a Binding with the None MessageVersion. 'System.ServiceModel.Description.WebHttpBehavior' is only intended for use with WebHttpBinding or similar bindings.
Anyone have any ideas on this one?
Thanks,
Zachary Carter
OK, I tried to replicate your stuff - works like a charm for me :-)
I used your service class - no changes
I used your RegisterRoutes call in global.asax.cs
When I launch the web app from within Visual Studio, I get Cassini (the built-in web server) come up on http://localhost:3131/ - this might wary in your case.
Now, I can easily navigate there with a second browser window, and I do get a simple response on this URL:
http://localhost:3131/Service1/HelloWorld
+--------------------+
from Cassini
+--------+
name (first param) in ServiceRoute registration
+-----------+
from your URI template on the WebGet attribute
Does the same URL work for you??
Update: here's my config - I can connect to http://localhost:3131/Service1/HelloWorld in the browser using REST, and I can connect to http://localhost:3131/Service1/soap with the WCF Test Client to make a SOAP call (my Service1 lives in the RestWebApp namespace - thus my service and contract names are a tad different than yours - but other than that, I believe it's identical to your own config):
<system.serviceModel>
<serviceHostingEnvironment
aspNetCompatibilityEnabled="true" />
<services>
<service name="RestWebApp.Service1" behaviorConfiguration="Meta">
<endpoint name="rest"
address=""
binding="webHttpBinding"
contract="RestWebApp.Service1"
behaviorConfiguration="REST" />
<endpoint name="SOAP"
address="soap"
binding="basicHttpBinding"
contract="RestWebApp.Service1" />
<endpoint name="mex"
address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="REST">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="Meta">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<standardEndpoints>
<webHttpEndpoint>
<!--
Configure the WCF REST service base address via the global.asax.cs file and the default endpoint
via the attributes on the <standardEndpoint> element below
-->
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
Thanks for this it helped me a lot.
The issue in my case was that I had a default behaviour configured that contains webHttp. After giving it the name="REST" and setting my webHttpBinding endpoint behaviourConfiguration ="REST" I had no further errors.
<system.serviceModel>
<bindings>
<customBinding>
<binding name="CustomBinding_IMobileService">
<binaryMessageEncoding />
<httpTransport />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="http://localhost:6862/silverlight/services/MobileService.svc"
binding="customBinding" bindingConfiguration="CustomBinding_IMobileService"
contract="AlchemyMobileService.IMobileService" name="CustomBinding_IMobileService" />
</client>
<services>
<service name="MobileService.Alchemy">
<endpoint address="http://localhost:8732/mobileservice" binding="webHttpBinding" contract="MobileService.IAlchemy" behaviorConfiguration="REST">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceDebug includeExceptionDetailInFaults="True" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="REST">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
I am getting the following error when trying to use the WCF Test Client to hit my new web service. What is weird is every once in awhile it will execute once then start popping this error.
Failed to invoke the service. Possible causes: The service is offline or inaccessible; the client-side configuration does not match the proxy; the existing proxy is invalid. Refer to the stack trace for more detail. You can try to recover by starting a new proxy, restoring to default configuration, or refreshing the service.
My code (interface):
[ServiceContract(Namespace = "http://rivworks.com/Services/2010/04/19")]
public interface ISync
{
[OperationContract]
bool Execute(long ClientID);
}
My code (class):
public class Sync : ISync
{
#region ISync Members
bool ISync.Execute(long ClientID)
{
return model.Product(ClientID);
}
#endregion
}
My config (EDIT - posted entire serviceModel section):
<system.serviceModel>
<diagnostics performanceCounters="Default">
<messageLogging logMalformedMessages="true" logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="true" />
</diagnostics>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" />
<behaviors>
<endpointBehaviors>
<behavior name="JsonpServiceBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="SimpleServiceBehavior">
<serviceMetadata httpGetEnabled="True" policyVersion="Policy15"/>
</behavior>
<behavior name="RivWorks.Web.Service.ServiceBehavior">
<!-- 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="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="RivWorks.Web.Service.NegotiateService" behaviorConfiguration="SimpleServiceBehavior">
<endpoint address=""
binding="customBinding"
bindingConfiguration="jsonpBinding"
behaviorConfiguration="JsonpServiceBehavior"
contract="RivWorks.Web.Service.NegotiateService" />
<!--<host>
<baseAddresses>
<add baseAddress="http://kab.rivworks.com/services"/>
</baseAddresses>
</host>
<endpoint address=""
binding="wsHttpBinding"
contract="RivWorks.Web.Service.NegotiateService" />-->
<endpoint address="mex"
binding="mexHttpBinding"
contract="RivWorks.Web.Service.NegotiateService" />
</service>
<service name="RivWorks.Web.Service.Sync" behaviorConfiguration="RivWorks.Web.Service.ServiceBehavior">
<endpoint address="" binding="wsHttpBinding" contract="RivWorks.Web.Service.ISync" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<extensions>
<bindingElementExtensions>
<add name="jsonpMessageEncoding" type="RivWorks.Web.Service.JSONPBindingExtension, RivWorks.Web.Service, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bindingElementExtensions>
</extensions>
<bindings>
<customBinding>
<binding name="jsonpBinding" >
<jsonpMessageEncoding />
<httpTransport manualAddressing="true"/>
</binding>
</customBinding>
</bindings>
</system.serviceModel>
2 questions:
What am I missing that causes this error?
How can I increase the time out for the service?
TIA!
Found the problem(s)...
Had an error inside the web service that was not handled.
The test app does not do an ABORT when it sees an error. Instead, it is left unhandled (and unreported) AND the channel is now locked because of the error.
Putting a try/catch around the inside method makes it so I can log the error and the test app does not lock up.