Configure autofac to use app.config in wcf client - wcf

Hi I'm trying to use autofac and use a wcf client.
But wondering how i can use the "client" configuration from my app.config ?
I would like to keep the settings in the config if possible?
public void ConfigureContainer()
{
var builder = new ContainerBuilder();
builder
.Register(c => new ChannelFactory<apiSoapType>(new BasicHttpsBinding("?????")) ?????.SingleInstance();
builder.Build();
}
//My app.config
<? xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name = "DisableServiceCertificateValidation" >
< clientCredentials >
< serviceCertificate >
< authentication certificateValidationMode="None" revocationMode="NoCheck" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<basicHttpsBinding>
<binding name = "apiSoapBinding" maxBufferPoolSize="20000000" maxBufferSize="20000000" maxReceivedMessageSize="20000000">
<readerQuotas maxDepth = "32" maxStringContentLength="200000000" maxArrayLength="200000000" />
<security mode = "Transport" >
< transport clientCredentialType="None" proxyCredentialType="None" realm="" />
</security>
</binding>
</basicHttpsBinding>
</bindings>
<client>
<endpoint
address = "https://somesite/api.wso"
binding="basicHttpsBinding"
bindingConfiguration="apiSoapBinding"
behaviorConfiguration="DisableServiceCertificateValidation"
contract="somename.apiSoapType"
name="**somename.apiSoapType**"
/>
</client>
</system.serviceModel>
</configuration>

Try registering the ChannelFactory<T> using the ChannelFactory<T>(string) constructor and passing in the endpoint name found in your config file.
builder
.Register(c => new ChannelFactory<apiSoapType>("**somename.apiSoapType**"))
.SingleInstance();

Related

WCF Service calls error out with message Could not establish secure channel for SSL/TLS with authority

Hello I am trying to call a web service:-
https://example.com/Dealio/DealioCapLinkSvc.svc (actual name hidden).
I am able to browse this service on the browser. However when I call the service through my client application I get the following error:-
Could not establish secure channel for SSL/TLS with authority 'example.com'.
Can any one tell me what is going on ?
Below is App Config:-
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="SOAPEndPoint1" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://example.com/dealio/DealioCapLinkSvc.svc/soap"
binding="basicHttpBinding" bindingConfiguration="SOAPEndPoint1"
behaviorConfiguration="authBehavior"
contract="DealioService.IDealioLib" name="SOAPEndPoint1" />
</client>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True" httpsGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
<!-- Security Behavior -->
<endpointBehaviors>
<behavior name="authBehavior">
<authBehavior />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="authBehavior" type="CanadaDealio.AuthBehavior, CanadaDealio , Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
</configuration>
This is the code that calls the service:-
static void Main(string[] args)
{
Deal deal = new Deal();
deal = PopulateDealDetails(deal);
DealReturnResults dealioReturnResults = null;
DealioLibClient dealioServiceProxy = new DealioLibClient();
try
{
ValidationErrorList validationErrorList =
dealioServiceProxy.ValidateDealDetails(deal);
}
catch(Exception e)
{
}
}
Can anyone tell me what is going on ? I don't think this should be very hard to call a WCF Service.
If you look at the Microsoft Documentation, they have the below example that shows how to connect basichttpbinding to SSL
<system.serviceModel>
<services>
<service
type="Microsoft.ServiceModel.Samples.CalculatorService"
behaviorConfiguration="CalculatorServiceBehavior">
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="Binding1"
contract="Microsoft.ServiceModel.Samples.ICalculator" />
</service>
</services>
<bindings>
<basicHttpBinding>
<!-- Configure basicHttpBinding with Transport security -- >
<!-- mode and clientCredentialType set to None.-->
<binding name="Binding1">
<security mode="Transport">
<transport clientCredentialType="None"
proxyCredentialType="None">
<extendedProtectionPolicy
policyEnforcement="WhenSupported"
protectionScenario="TransportSelected">
<customServiceNames></customServiceNames>
</extendedProtectionPolicy>
</security>
</binding>
</basicHttpBinding>
</bindings>
</system.serviceModel>
try to add the following code snippets when invoke the web service via adding the service reference.
Client-side.
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;
try
{
var result = client.SayHello();
Console.WriteLine(result);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Feel free to let me know if the problem still exists.

Custom UserNamePasswordValidator not called

I am trying to set up a WCF webservice with BasicHttpBinding and authentication using username/password. I made custom authentication class. However, it is never called (verified by debugging). This is my web.config:
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<behaviors>
<serviceBehaviors>
<behavior>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Compareware_WebApp.webservices.AuthenticationValidator, Compareware_WebApp" />
</serviceCredentials>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding">
<security mode="TransportCredentialOnly">
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="Compareware_WebApp.webservices.Accounts">
<endpoint address="/webservices/Accounts.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding" name="BasicEndpoint"
contract="Compareware_WebApp.webservices.IAccounts" />
</service>
</services>
<client />
This is my authentication class:
namespace Compareware_WebApp.webservices
{
public class AuthenticationValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (null == userName || null == password)
{
throw new ArgumentNullException();
}
if (!Membership.ValidateUser(userName, password))
{
// This throws an informative fault to the client.
throw new FaultException("Unknown Username or Incorrect Password");
}
}
}
}
What am I doing wrong?
I also stuck in this problem, And I got the solutions. If you want to call the Validate method of the UserNamePasswordValidator So, you must be have to use the TransportWithMessageCredential security.
NOTE: You must be have to host the WCF Services at the IIS server and have to enable the SSL for you website otherwise it will be not work. If we use the TransportWithMessageCredential than we have to enable the SSL for the our website.
WCF Server Web.config file
<?xml version="1.0"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsHttp">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="TransportWithMessageCredential.Service1" behaviorConfiguration="wsHttpBehavior">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttp" contract="TransportWithMessageCredential.IService1">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<host>
<baseAddresses>
<add baseAddress="https://localhost:8080/WCFDemo"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="wsHttpBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="TransportWithMessageCredential.ServiceAuthanticator, TransportWithMessageCredential"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.web>
<compilation debug="true"/>
</system.web>
</configuration>
Service Authentication class
public class ServiceAuthanticator : UserNamePasswordValidator
{
/// <summary>
///
/// </summary>
/// <param name="userName"></param>
/// <param name="password"></param>
public override void Validate(string userName, string password)
{
if (String.IsNullOrEmpty(userName) || String.IsNullOrEmpty(password))
{
throw new FaultException("Please, Provide the username and password!!!");
}
if (userName != "abc" || password != "abc")
{
throw new FaultException("Sorry, Invalid username or password!!!");
}
}
}
WCF Client app.config file
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IService1">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" />
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://kalpesh-pc/WCFAuth/Service1.svc" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IService1" contract="ServiceReference1.IService1"
name="WSHttpBinding_IService1">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
Client Program to call the WCF Services
try
{
Service1Client objClient = new Service1Client();
objClient.ClientCredentials.UserName.UserName = "abc";
objClient.ClientCredentials.UserName.Password = "abc";
objClient.Open();
string strData = objClient.GetData(10);
}
catch (FaultException ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
Happy Coding...
:)
Transport, as far as I understand, requires HTTPS to encrypt credentials and throws an exception if there is no SSL. TransportCredentialOnly will send the credentials in plain text and unencrypted and is recommended for testing ONLY.
Therefore, your credentials are send via transport. So <Transport> tag must be adjusted, not <Message>.
The following works for me:
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic"/>
</security>

WCF transport security: socket connection was aborted

I have a problem in getting transport security to work.
I have 2 services (A & B) running in the same server. service A will call service B to perform some task. Without any security, I can communicate just fine. But when I turn on transport security with the following settings:
security mode = transport
TransportClientCredentialType = Windows
ProtectionLevel = EncryptAndSign
I got error when service A calls service B:
System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:00:09.7810000'. ---> System.IO.IOException: The read operation failed, see inner exception. ---> System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:00:09.7810000'. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
I tried changing the receive and send timeout to 5 mins but I still get the same error with roughly the same timeout duration. The only difference is I need to wait out the 5 minutes instead of 1 minute.
Can anyone provide an insight to what's the cause and how to resolve this?
Attached is the configuration file for both service:
ServiceA
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.web>
<compilation targetFramework="4.5" debug="true" defaultLanguage="c#" />
</system.web>
<system.serviceModel>
<protocolMapping>
<remove scheme="net.tcp" />
<add scheme="net.tcp" binding="netTcpBinding" bindingConfiguration="ReliableTCP" />
</protocolMapping>
<client/>
<behaviors>
<serviceBehaviors>
<behavior name="mexTag">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="tryBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="mexTcp">
<tcpTransport portSharingEnabled="true" />
</binding>
</customBinding>
<netTcpBinding>
<binding name="ReliableTCP" portSharingEnabled="true" sendTimeout="00:05:00" receiveTimeout="00:05:00"
maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
<reliableSession enabled="true" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
</security>
</binding>
</netTcpBinding>
</bindings>
<services>
<service behaviorConfiguration="mexTag" name="Test.Service.ServiceAImpl">
<endpoint address="net.tcp://app-svr:10010/ServiceA/ServiceAImpl/" behaviorConfiguration="tryBehavior"
binding="netTcpBinding" bindingConfiguration="ReliableTCP" contract="Test.Service.IServiceA" />
<endpoint address="net.tcp://app-svr:10012/ServiceA/ServiceAImpl/mex"
binding="customBinding" bindingConfiguration="mexTcp" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true" />
</system.webServer>
</configuration>
ServiceB
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.web>
<compilation targetFramework="4.5" debug="true" defaultLanguage="c#" />
</system.web>
<system.serviceModel>
<client>
<endpoint address="net.tcp://app-svr:10010/ServiceA/ServiceAImpl/"
binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IServiceA"
behaviorConfiguration="tryBehavior"
contract="ServiceAReference.IServiceA" name="NetTcpBinding_IServiceA" />
</client>
<behaviors>
<serviceBehaviors>
<behavior name="MEXGET" >
<!-- Add the following element to your service behavior configuration. -->
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="tryBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="MexTcp">
<tcpTransport portSharingEnabled="true" />
</binding>
</customBinding>
<netTcpBinding>
<binding name="ReliableTCP" portSharingEnabled="true">
<reliableSession enabled="true" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
</security>
</binding>
<binding name="NetTcpBinding_IServiceA" receiveTimeout="00:05:00" sendTimeout="00:05:00" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
<reliableSession enabled="true" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
</security>
</binding>
</netTcpBinding>
<mexTcpBinding>
<binding name="MexTcp" />
</mexTcpBinding>
</bindings>
<services>
<service name="Test.Service.ServiceBImpl" behaviorConfiguration="MEXGET" >
<endpoint address="mex"
binding="customBinding"
bindingConfiguration="MexTcp"
contract="IMetadataExchange" />
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
<endpoint
address="net.tcp://app-svr:10010/ServiceB/ServiceBImpl"
binding="netTcpBinding" behaviorConfiguration="tryBehavior"
bindingConfiguration="ReliableTCP"
contract="Test.Service.ServiceB" />
<host>
<baseAddresses>
<add baseAddress="http://app-svr:10011/ServiceB/ServiceBImpl" />
<add baseAddress="net.tcp://app-svr:10010/ServiceB/ServiceBImpl" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true" />
</system.webServer>
</configuration>
There is a solution here ...you should try it...
Added these behaviors at both service and client config.
<behaviors>
<endpointBehaviors>
<behavior name="endpointBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
</behavior>
</endpointBehaviors>
</behaviors>
Update these values to maximum size in both Client and Server config.
<binding name="tcpBinding" receiveTimeout="00:15:00" sendTimeout="00:15:00" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
<security mode="None">
<transport clientCredentialType="None" protectionLevel="None" />
<message clientCredentialType="None" />
</security>
</binding>
Hope it helps.
I had that same error and it was caused by the service credentials being wrong or missing. Since you're using tcp binding first create the binding and set the security properly:
NetTcpBinding binding = new NetTcpBinding(SecurityMode.Transport)
{
CloseTimeout = TimeSpan.FromSeconds(timeoutInSeconds),
OpenTimeout = TimeSpan.FromSeconds(timeoutInSeconds),
SendTimeout = TimeSpan.FromSeconds(timeoutInSeconds),
ReceiveTimeout = TimeSpan.FromSeconds(timeoutInSeconds)
};
binding.Security.Transport.ClientCredentialType =
TcpClientCredentialType.Windows;
binding.MaxReceivedMessageSize = int.MaxValue;
binding.MaxBufferSize = int.MaxValue;
binding.MaxBufferPoolSize = int.MaxValue;
Make sure to set the username and password after creating the client:
var serviceClient = new MyServiceClient(binding, endpointYouDefine);
serviceClient.ClientCredentials.Windows.ClientCredential = new NetworkCredential("usernameInActiveDirectory", "passwordForTheADUser", "yourdomain.com");
Don't do anything else to the client after that. I've seen weird behavior like setting reader quotas programmatically after assigning the credentials, and the credentials get wiped out.
I managed to solve this problem by doing the following in the server where the net.tcp web service was hosted:
Restart NET TCP port sharing service from services.msc
Open command prompt as administrator and run the IIS Reset
I've created extension method for this.
One for NetTcpBinding and one for NetNamedPipeBinding. Only to be used on internal services of course. Timings too as when a default service is not used during the night it will fail on the first call in the morning.
public static void ActivateMaxValues(this NetTcpBinding b)
{
b.OpenTimeout = TimeSpan.FromHours(10);
b.CloseTimeout = TimeSpan.FromMinutes(10);
b.ReceiveTimeout = TimeSpan.FromHours(10);
b.SendTimeout = TimeSpan.FromHours(10);
b.MaxBufferSize = int.MaxValue;
b.MaxReceivedMessageSize = int.MaxValue;
b.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxArrayLength = int.MaxValue,
MaxBytesPerRead = int.MaxValue,
MaxDepth = int.MaxValue,
MaxNameTableCharCount = int.MaxValue,
MaxStringContentLength = int.MaxValue
};
}
public static void ActivateMaxValues(this NetNamedPipeBinding b)
{
b.TransactionFlow = true;
b.OpenTimeout = TimeSpan.FromHours(1);
b.CloseTimeout = TimeSpan.FromMinutes(10);
b.ReceiveTimeout = TimeSpan.FromHours(1);
b.SendTimeout = TimeSpan.FromHours(1);
b.MaxBufferSize = int.MaxValue;
b.MaxReceivedMessageSize = int.MaxValue;
b.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxArrayLength = int.MaxValue,
MaxBytesPerRead = int.MaxValue,
MaxDepth = int.MaxValue,
MaxNameTableCharCount = int.MaxValue,
MaxStringContentLength = int.MaxValue
};
}

RESTful GET on HTTPS returning bad request 400 error

I recently needed to change my REST WCF service to use SSL.
It is hosted in IIS 6 and was working fine prior to the SSL requirement.
I am unable to figure out why I'm getting the 400 bad request error.
I have diagnostic logging and it says the
<Message>The body of the message cannot be read because it is empty.</Message>
The web.config looks like:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<webServices>
<protocols>
<add name="HttpGet"/>
</protocols>
</webServices>
<identity impersonate="true"/>
<customErrors mode="Off"/>
</system.web>
<startup>
<supportedRuntime version="v4.0.30319"/>
</startup>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IMessageService">
<security mode="Transport">
<transport clientCredentialType="None"/>
</security>
</binding>
</wsHttpBinding>
<webHttpBinding>
<binding name ="webBinding">
</binding>
</webHttpBinding>
<basicHttpBinding>
<binding name="httpBinding">
<security mode="Transport">
<transport clientCredentialType = "None"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="metadataBehavior"
name="XXXDataService.XXXDataService">
<endpoint address=""
binding="wsHttpBinding"
bindingNamespace="http://blah.blah.blah.com/XXXDataService/"
bindingConfiguration="WSHttpBinding_IMessageService"
contract="ZZZDataService.IZZZDataServices">
</endpoint>
<endpoint address="mex"
binding="mexHttpsBinding" contract="IMetadataExchange" />
</service>
</services>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name=""
helpEnabled="true"
automaticFormatSelectionEnabled="true" />
</webHttpEndpoint>
</standardEndpoints>
<behaviors>
<serviceBehaviors>
<behavior name="metadataBehavior">
<serviceMetadata httpsGetEnabled="true" httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="True"
httpHelpPageEnabled="True"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
I only removed the diagnostics part and needed to protect the names by replacing with XXX and ZZZ.
Any help or suggestions would be greatly appreciated.
service implementation:
[ServiceBehavior(Namespace="blah.blah.blah.com", Name="XXXDataService")]
public class YYYDataService : IYYYDataService
{
public string GetUser(string id)
{
string result = string.Empty;
using (YYYAdmintTree tree = new YYYAdmintTree()) // used for accessing DB
{
result = tree.GetUserById(id, 1);
}
return result;
}
}
contract:
[ServiceContract(Namespace="https://blah.blah.blah.com", Name="XXXDataService")]
public interface IYYYDataService
{
[OperationContract]
[WebGet(UriTemplate="/GetUser/{id}", ResponseFormat=WebMessageFormat.Json)]
string GetUserById(string id);
}
Error 400 comes when you pass wrong parameter as Input. If you are passing XML or JSON as an input check once again. If server was unable to locate it it would have given you error 404. But 400 indicates surely there is something wrong with input provided.

InvalidSecurity when I try to soap call my WCF with Username and Password

Error [a:InvalidSecurity] An error occurred when verifying security for the message.
I try to call my service like this.
php
<?php
$soapURL = "https://localhost:8888/wcf/?wsdl" ;
$soapParameters = Array('userName' => "user23", 'password' => "pass123") ;
$soapClient = new SoapClient($soapURL, $soapParameters);
var_dump($soapClient->GetVersion());
?>
I got it to work before with no authentication so the fault lies here in the php code or in my app.config on the WCF i believe.
here is my app.config
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service
behaviorConfiguration="MYDLL.Behavior"
name="MYDLL.WCFService">
<endpoint
address=""
binding="basicHttpBinding"
bindingConfiguration="UsernameAndSSL"
name="basicHttpBinding"
contract="MYDLL.IService"/>
<endpoint
address="mex"
binding="mexHttpsBinding"
bindingConfiguration=""
name="MexBinding"
contract="IMetadataExchange"/>
<host>
<baseAddresses>
<add baseAddress="https://localhost:8734/wcf/"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MYDLL.Behavior">
<serviceMetadata httpsGetEnabled="true"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MYDLL.CustomUserNameValidator, MYDLL"/>
<serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"/>
</serviceCredentials>
<useRequestHeadersForMetadataAddress />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="UsernameAndSSL">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Basic"/>
<message clientCredentialType="UserName"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
</system.serviceModel>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/>
</startup>
</configuration>
and the customusernamevalidator
public class CustomUserNameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
Console.WriteLine("User validation succeeded (Username: {0}; Password: {1})", userName, password);
}
}
What I dont get is if the fault are in my php code or in the config. the consolewrite does not run, and I just get that invalidSecurity exception so it seems it knows I have an customusernamevalidation that it cant find?
I struggled with this same issue for several weeks and finally got PHP clients to authenticate with my WCF service. This configuration is what I currently have working in production.
First off, you have to enable Anonymous auth AND Basic auth. You have to leave Anonymous turned on so the client can read the WSDL before they authenticate. Even though anonymouse auth is enabled they will not be able to use your service without being authenticated first.
Put this in your web.config or turn on Anonymous AND Basic Authentication in IIS directly:
<security>
<authentication>
<anonymousAuthentication enabled="true" />
<basicAuthentication enabled="true" />
</authentication>
</security>
Then setup your WCF security to use Basic auth and Transport security:
<bindings>
<basicHttpBinding>
<binding name="SSLBinding">
<security mode="Transport">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
</bindings>
And here is my behavior:
<behaviors>
<serviceBehaviors>
<behavior name="SSLBehavior">
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceSecurityAudit auditLogLocation="Application"
serviceAuthorizationAuditLevel="Failure"
messageAuthenticationAuditLevel="Failure"
suppressAuditFailure="true" />
</behavior>
</serviceBehaviors>
</behaviors>
Use this to decorate your methods to control who can access what:
[PrincipalPermission(SecurityAction.Demand, Role = #"DOMAIN\ActiveDirectoryGroup")]
Using this setup the PHP client should be able to get the WSDL then call your method after authorization:
$url = 'https://domain.com/service.svc?wsdl';
$username = 'username';
$password = 'password';
$options = array(
'login' => $username,
'password' => $password,
'soap_version' => SOAP_1_1,
'exceptions' => true,
'trace' => 1,
'cache_wsdl' => WSDL_CACHE_NONE,
'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP,
'connection_timeout' => 60
);
$client = new SoapClient($url, $options);
$obj = new YourObject();
$obj->MyProperty = 'Test';
$obj->MyOtherProperty = 'Testing';
$result = $client->WCFMethodName(array('paramName' => $obj));
It's VERY important that the parameter name in your PHP code match the WCF parameter name EXACTLY (case sensitive).