I'm having a unclear CPU issue with WCF services using NetTcp binding (hosted on IIS 8 on Windows Server 2012 Datacenter)
I'm holding WCF clients (proxies) pool in one service and use it to connect the other..
Our problem is that when the client service that holds the pool is not properly disposed (which is possible scenario for us) then the server is spinning up to 100% CPU and stay there for while ( the duration is strongly depends on the number of the clients in the pool which haven’t been closed)
We are using InstanceContextMode.Single with multiple concurrency mode on the server and no reliable sessions..
server configuration:
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_IRecommenderService" closeTimeout="00:00:01.5000000" openTimeout="00:00:01.5000000" receiveTimeout="00:00:01.5000000" sendTimeout="00:00:01.5000000" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxConnections="2147483647" maxReceivedMessageSize="2147483647" listenBacklog="2147483647" transactionFlow="false">
<readerQuotas maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxDepth="2147483647" maxNameTableCharCount="2147483647" maxStringContentLength="2147483647"/>
<reliableSession enabled="false"/>
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceThrottling maxConcurrentCalls="4000" maxConcurrentInstances="1000" maxConcurrentSessions="1000" />
</behavior>
<behavior name="Unthrottled">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceThrottling maxConcurrentCalls="6000" maxConcurrentSessions="6000" maxConcurrentInstances="6000" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="UnthrottledServiceEndpoint">
<dispatcherSynchronization maxPendingReceives="10000" asynchronousSendEnabled="true" />
</behavior>
</endpointBehaviors>
</behaviors>
Client configuration:
<client>
<endpoint address="net.tcp://this-will-be-given-at-runtime:12345/an-invalid-service-too/"
behaviorConfiguration="UnthrottledClientEndpoint" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_IRecommenderService" contract="RecommenderClient.IRecommenderService"
name="IRecommenderServiceClient_nettcp" />
</client>
...
<behavior name="UnthrottledClientEndpoint">
<dispatcherSynchronization asynchronousSendEnabled="true" />
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
...
Any answer will be appreciated
Thanks,
Sagi.
//when the client service that holds the pool is not properly disposed (which is possible scenario for us) //
Are you saying you wrote your own "pool" for WCF connections?
I wouldn't do that. I would use the proxy and dispose of it after each use....to avoid the exact thing you're describing.
If you cannot "trust" your dotnet consumers to close/dispose of things, then I would suggest (as I do) to write a ClientWrapper assembly, and they must go through it...and YOU make sure you close the proxies.
I liken it to an IDataReader. If I returned IDataReaders to the client, I cannot guarantee the consumer disposes of them. Aka, it is outside of my control. No way. I consume the IDataReader, dispose of it, and give them back some (serialized?) object. The consumer would never get something back that they could forget to dispose of, and screw me me.
Below is my typical "client side" wrapper code.
private IMyService GetTheProxy()
{
string endPointName = "MyEndPointName";
ChannelFactory<IMyService> factory;
//Use default endpoint
Console.WriteLine("endPointName='{0}'", endPointName);
factory = new ChannelFactory<IMyService>(endPointName);
IMyService proxy1 = factory.CreateChannel();
return proxy1;
}
public string GetAString()
{
string returnValue = null;
IMyService proxy1 = this.GetTheProxy();
using (proxy1 as IDisposable)
{
returnValue = proxy1.GetAString();
return returnValue ;
}
}
public int GetAnInt()
{
int returnValue = null;
IMyService proxy1 = this.GetTheProxy();
using (proxy1 as IDisposable)
{
returnValue = proxy1.GetAnInt();
return returnValue ;
}
}
Related
I have an asp.net project that was hosting a WCF service (via the wcf file exposure technique for IIS) that was put together around 10 years ago. There is an operation on the service that retrieves content that is greater than the default 75K limit. The service configuration (in web.config) property on the binding was adjusted to be larger than the default and was working ok. The ASP.NET project was upgraded in VS2017/2019 - and (I assume) the project was adjusted (somehow). The service operation described above no longer works. I did a lot of investigation and all solutions were handled (which makes sense as the original version of the project was working). I even went through the process of building a new web.config with minimal configuration and still did not work. I also adjusted the service to include the Configure operation that could be used to configure the endpoint configuration in code. It also did not work. For this I have the following:
public static void Configure(ServiceConfiguration config)
{
BasicHttpBinding binding = new BasicHttpBinding();
binding.MaxReceivedMessageSize = int.MaxValue;
binding.MaxBufferSize = int.MaxValue;
binding.MaxBufferPoolSize = int.MaxValue;
binding.BypassProxyOnLocal = true;
binding.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxStringContentLength = int.MaxValue,
MaxDepth = int.MaxValue,
MaxArrayLength = int.MaxValue,
MaxBytesPerRead = int.MaxValue,
MaxNameTableCharCount = int.MaxValue
};
var endpoint = new ServiceEndpoint(
ContractDescription.GetContract(
typeof(IAuthenticateServicePack)),binding,new
EndpointAddress("http://localhost:49437/WCFSevices/AuthenticateServicePack.svc"));
//MyEndpointBehavior Behavior = new MyEndpointBehavior();
//endpoint.Behaviors.Add(Behavior);
config.AddServiceEndpoint(endpoint);
}
The web.config definitions is the following
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true"
/>
<behaviors>
<serviceBehaviors>
<behavior name="ServicePack.ServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceThrottling maxConcurrentCalls="500" maxConcurrentSessions="500"
/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<clear />
<binding name="myBindingForBigArrays" maxReceivedMessageSize="2147483647"
transferMode="Buffered">
<readerQuotas
maxDepth="32"
maxStringContentLength="2147483647"
maxArrayLength="2147483647"
maxBytesPerRead="4096"
maxNameTableCharCount="10000"
/>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="Lapp.WebAdmin.Mvc.WCFSevices.AuthenticateServicePack"
behaviorConfiguration="ServicePack.ServiceBehavior">
<endpoint address="" binding="basicHttpBinding"
bindingConfiguration="myBindingForBigArrays"
contract="Lapp.WebAdmin.Mvc.WCFSevices.IAuthenticateServicePack" />
<host>
<timeouts closeTimeout="00:01:00" />
</host>
</service>
</services>
</system.serviceModel>
For some reason - the updated asp.net project does not seem to be handling the WCF service endpoint configuration. Has anyone run into this issue?
Note: I can test the older version of the service and is working properly -- and reviewing the old config with the new -- cannot identify any modification that would appear to be causing the new not to use the size limit settings.
I setup a custom ClaimsAuthenticationManager for my wcf service. Now I found out that the method ClaimsAuthenticationManager.Authenticate is executed for each and every wcf call. Instead I want to have it executed once per session to avoid unneccessary overhead.
According to Microsoft:
The claims authentication manager is typically invoked once per session, with the following exceptions: For transport security, tokens present at the transport layer will invoke the claims authentication once per call, even if sessions are present.
Source: https://msdn.microsoft.com/en-us/library/ee748487.aspx
Since my custom binding does not use transport security I see no reason why ClaimsAuthenticationManager.Authenticate is executed per call.
Does anyone know if there are further requirements that need to be met to have this method called once per session instead? Thank you very much for any suggestions.
The wcf binding configuration:
<behaviors>
<serviceBehaviors>
<behavior name="defaultBehavior">
<serviceDebug includeExceptionDetailInFaults="True" />
<serviceThrottling maxConcurrentCalls="200" maxConcurrentSessions="200" maxConcurrentInstances="200" />
<serviceCredentials useIdentityConfiguration="true" />
<serviceAuthorization principalPermissionMode="Always" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netNamedPipeBinding>
<binding name="ServiceNamedPipeBinding" receiveTimeout="00:05:00" sendTimeout="00:05:00" maxReceivedMessageSize="134217728" maxBufferPoolSize="134217728" maxBufferSize="134217728" />
</netNamedPipeBinding>
<customBinding>
<binding name="TcpLoadBalanced" receiveTimeout="00:05:00" sendTimeout="00:05:00">
<security authenticationMode="SecureConversation" requireSecurityContextCancellation="true">
<secureConversationBootstrap authenticationMode="SspiNegotiated"/>
</security>
<binaryMessageEncoding>
<readerQuotas maxArrayLength="2147483647" />
</binaryMessageEncoding>
<tcpTransport listenBacklog="200" maxBufferPoolSize="134217728" maxReceivedMessageSize="134217728" maxBufferSize="134217728">
<connectionPoolSettings leaseTimeout="00:00:00" maxOutboundConnectionsPerEndpoint="0" />
</tcpTransport>
</binding>
</customBinding>
</bindings>
If you want to use per session call,then try like this-
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class MyService:IMyService
{
public int MyMethod()
{
int m_Counter = 0;
m_Counter++;
return m_Counter;
}
}
We have a WCF service hosted in a windows service which hangs after long periods of inactivity: ie after the weekend.
This behavior happens at different locations.
The service uses WSHttpBinding which is set to use transport security a Custom serviceAuthorization authorizationPolicy which uses windows authentication.
The Spring framework is used. Spring.ServiceModel.Activation.ServiceHostFactory creates the Service hosts.
Service throttle is set to 200 sessions, instances and calls. There are at most 15 users of the system.
Tracing is enabled and set to Warning which should let me know about throttle issues.
There are no messages in the service trace log.
There are no messages in the Event log which look relevant.
There are no relevant logs in HTTPPerf logs.
We log considerably in the server side application but there is no activity being recorded when the system hangs.
It is a total black box when the system hangs.
The client fails with the following message.
08:13:32.014 [1] ERROR App - System.TimeoutException: Client is unable to finish the security negotiation within the configured timeout (00:00:59.9941374). The current negotiation leg is 1 (00:00:59.9863206). ---> System.TimeoutException: The request channel timed out while waiting for a reply after 00:00:59.9628702. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. ---> System.TimeoutException: The HTTP request to 'http://localhost:8080/OrderManagementService.svc' has exceeded the allotted timeout of 00:00:59.9690000. The time allotted to this operation may have been a portion of a longer timeout. ---> System.Net.WebException: The operation has timed out
at System.Net.HttpWebRequest.GetResponse()
I have spent many hours searching for relevant information on this.
I do not think this is related to inactivity timeout as this should be recorded in the trace log.
Only thing that I can think of is related to Active Directory credential caching or something of that nature or that it is related to the use of the Spring framework.
Any help would be greatly, greatly appreciated.
Am thinking of moving away from WSHttpBinding or WCF altogether as this is an unacceptable situation.
Service side configuration is as follows.
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false" maxBufferPoolSize="2147483646" maxReceivedMessageSize="2147483646">
<readerQuotas maxDepth="2147483646" maxStringContentLength="2147483646" maxArrayLength="2147483646" maxBytesPerRead="2147483646" maxNameTableCharCount="2147483646" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security>
<transport></transport>
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceAuthorization principalPermissionMode="Custom">
<authorizationPolicies>
<add policyType="Kodi.Kodiak.Security.AuthorizationPolicy, Kodi.Kodiak.Security" />
</authorizationPolicies>
</serviceAuthorization>
<serviceCredentials>
<windowsAuthentication includeWindowsGroups="true" allowAnonymousLogons="false" />
</serviceCredentials>
<serviceThrottling maxConcurrentCalls="200" maxConcurrentSessions="200" maxConcurrentInstances="200" />
<dataContractSerializer maxItemsInObjectGraph="2147483646" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="rest">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="ServiceBehavior" name="OrderManagementService">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding" contract="Kodi.Kodiak.Services.ServiceContracts.IOrderManagementService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
SPRING CONFIG
<object id="OrderManagementService"
singleton="false"
type="Kodi.Kodiak.Services.OrderManagementService, Kodi.Kodiak.Services"
scope="session">
</object>
How the clients are calling the service? If they are calling the service through the SvcUtil generated proxy; are they closing the proxy connection properly? I think the clients are not closing the connections or simply the connections are not getting closed properly.
One important thing is, you should avoid using using statement while creating proxies.
A better approach would be something like this,
ServiceClient client = null;
try
{
client = new ServiceClient();
client.CallMethod();
}
finally
{
client.CloseConnection(); // extension method
}
public static void CloseConnection(this ICommunicationObject client)
{
if (client.State != CommunicationState.Opened)
{
return;
}
try
{
client.Close();
}
catch (CommunicationException)
{
client.Abort();
throw;
}
catch (TimeoutException)
{
client.Abort();
throw;
}
catch (Exception)
{
client.Abort();
throw;
}
}
I think this can be done by maintaining the client and server Binding End Points and Behavior same, the timeout values specified in the Client and Server Configs Should be Same.
I'm having some difficulty setting up a WCF service to run under Windows authentication. The service is only consumed via jQuery using ajax.
IIS (version 6 on server 2003) is set to only allow Windows Authentication.
web.config has the <authentication mode="Windows" /> tag.
Here's the service section of the web.config:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="AspNetAjaxBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<services>
<service name="SearchService" behaviorConfiguration="ServiceBehavior">
<endpoint address="http://localhost:9534/SearchService.svc" behaviorConfiguration="AspNetAjaxBehavior"
binding="webHttpBinding" bindingConfiguration="webWinBinding"
name="searchServiceEndpoint" contract="MyApp.Services.ISearchService">
</endpoint>
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="webWinBinding" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows"/>
</security>
<readerQuotas maxArrayLength="100000" maxStringContentLength="2147483647" />
</binding>
</webHttpBinding>
</bindings>
The interface looks like this:
[ServiceContract(Namespace = "http://MyService.ServiceContracts/2012/02", Name = "SearchService")]
public interface ISearchService
{
[WebGet(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "GetSomeData?filter={filter}")]
[OperationContractAttribute(Action = "GetSomeData")]
string GetSomeData(string filter);
}
And the implementation:
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class SearchService : ISearchService
{
public string GetSomeData(string filter)
{
// Call Database and get some results
// return the results
return "";
}
}
When I navigate to the service in Internet Explorer, it prompts me for my username and password, despite having Windows Authentication turned on.
As soon as I enable Anonymous Authentication, the service loads just fine and everything works. Problem is, I have other things going on in the web application that require anonymous to be turned off.
I've scoured the web and can't find anything on this problem.
So I've basically got everything up and running with wsHttpBindings and my WCF service using custom authentication over HTTPS.
The issue I'm having is with the customUserNamePasswordValidatorType:
<serviceCredentials>
<!-- Use our own custom validation -->
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="CustomValidator.CustomUserNameValidator, CustomValidator"/>
</serviceCredentials>
Following directions found here I've created my custom class as well:
namespace CustomValidator
{
public class CustomUserNameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (null == userName || null == password)
{
throw new ArgumentNullException();
}
if (!AuthenticateUser(userName, password))
throw new SecurityTokenValidationException("Invalid Credentials");
The error is "Could not load file or assembly 'CustomValidator' or one of its dependencies. The system cannot find the file specified.", and refers to the tail end of customUserNamePasswordValidatorType - "..., CustomValidator".
I didn't think it was a problem having my custom validator in its own namespace and class, but I can't see what else to do to make this work.
I've tried with/without the namespace at the beginning, swapping, etc - nothing.
Hoping another pair of eyes can pick this out.
Thanks.
EDIT
system.serviceModel
<system.serviceModel>
<bindings>
<!-- wsHttpBinding -->
<wsHttpBinding>
<binding name="wsHttpEndpointBinding">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" />
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
<!-- webHttpBinding -->
<webHttpBinding>
<binding name="wsHttps" >
<security mode="Transport"/>
</binding>
</webHttpBinding>
<!-- Basic binding -->
<basicHttpBinding>
<binding name="TransportSecurity">
<security mode="Transport">
<message clientCredentialType="UserName"/>
<!-- transport clientCredentialType="None"/-->
</security>
</binding>
</basicHttpBinding>
<!-- customBinding>
<binding name="WebHttpBinding_IService">
textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16"
messageVersion="Soap12" writeEncoding="utf-8">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
</textMessageEncoding>
<httpsTransport manualAddressing="false"/>
</binding>
</customBinding -->
<!-- Another custom binding -->
<customBinding>
<binding name="CustomMapper">
<webMessageEncoding webContentTypeMapperType=
"IndexingService.CustomContentTypeMapper, IndexingService" />
<httpTransport manualAddressing="true" />
</binding>
</customBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" />
<services>
<service behaviorConfiguration="ServiceBehavior" name="Service">
<!-- Service Endpoints -->
<!-- since we're hosting in IIS, baseAddress is not required
<host>
<baseAddresses>
<add baseAddress="https://mysslserver.com/Service.svc"/>
</baseAddresses>
</host>
-->
<endpoint address="https://mysslserver.com/Service.svc"
binding="wsHttpBinding"
bindingConfiguration="wsHttpEndpointBinding"
contract="IService"
name="wsHttpEndpoint">
<!--
Upon deployment, the following identity element should be removed or replaced to reflect the
identity under which the deployed service runs. If removed, WCF will infer an appropriate identity
automatically.
-->
<!--identity>
<dns value="https://mysslserver.com"/>
</identity-->
</endpoint>
<!-- endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/ -->
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="webBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<!-- Setup Security/Error Auditing -->
<serviceSecurityAudit auditLogLocation="Application"
suppressAuditFailure="false"
serviceAuthorizationAuditLevel="Failure"
messageAuthenticationAuditLevel="Failure" />
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true"
httpsGetUrl="https://mysslserver.com/Service.svc"/>
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceCredentials>
<!-- Use our own custom validation -->
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="CustomValidator.CustomUserNameValidator, CustomValidator"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
<!-- serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpsGetEnabled="true"
httpsGetUrl="https://mysslserver.com/Service.svc" />
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-->
</behaviors>
</system.serviceModel>
I decided to give it another stab, and didn't like having my custom validator in another lib.
So I created a new class in App_Code, and went at it...
The following is what actually fixed it,
="CustomValidator.CustomUserNameValidator, App_Code"
When you refer to the custom validator with the values
="CustomValidator.CustomUserNameValidator, CustomValidator"
The first value is the type name and the second is the name of the assembly
in which to find the type. So I would suggest that in your first instance
your service is actually in some other assembly such as MyService
In that case you really needed your config file to say
="CustomValidator.CustomUserNameValidator, MyService"
I suspect that when you have created your new class library for your
validator, you have called your project CustomValidator (which will
output an assembly called CustomValidator.dll), and hence now your
config will work (i.e. it has nothing to do with being in a separate
class library - it just happens that the naming of your assembly
reference in the web.config is now valid)
Seems a bit strange, but the solution was to create a separate class library and make reference to its DLL in my WCF service.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.ServiceModel;
/// <summary>
/// Summary description for CustomUsernamePasswordValidator
/// </summary>
namespace CustomValidator
{
public class CustomUserNameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (null == userName || null == password)
{
throw new ArgumentNullException();
}
if (!AuthenticateUser(userName, password))
throw new SecurityTokenValidationException("Invalid Credentials");
else
{
// do nothing - they're good
}
}
public bool AuthenticateUser(string userName, string password)
{
if (userName != "userbill" || password != "passwordbill")
{
return false;
}
else
{
return true;
}
}
}
}
I then made added a reference to System.IdentityModel and System.ServiceModel.
The serviceCredentials section for the WCF service is now changed to this:
<serviceCredentials>
<!-- Use our own custom validation -->
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="CustomValidator.CustomUserNameValidator, CustomValidator"/>
</serviceCredentials>
Hope that helps someone.
I tried this with invalid credentials, and was expecting to see my "Invalid Credentials" message. Instead I'm getting "At least one security token in the message could not be validated."
Other than that this thing is finally up and running!
Just reading this as it was helpful for a POC I had to get going quickly. In response to ELHaix above...this should work to ensure your descriptive custom error is returned back to the client:
using System.ServiceModel
...
throw new FaultException("Invalid Credentials - Custom Error");