Custom Message Encoder in WCF with support for ReaderQuotas - wcf

Found the answer here (last post): http://social.msdn.microsoft.com/Forums/eu/wcf/thread/f5c0ea22-1d45-484e-b2c0-e3bc9de20915
I'm having one last issue with the implementation of my custom (TextOrMtomEncoder), which is the implementation of ReaderQuotas.
I've searched the web a lot, but I can't figure out the final piece of the puzzle.
I've got a class, which contains my implementations of the 'BindingElementExtensionElement' and 'MessageEncodingBindingElement'.
The MessageEncodingBindingElement implementation contains an override for:
T GetProperty<T>(BindingContext context)
which I 'borrowed' from the default .NET MessageEncoding implementations, like the TextMessageEncoding.
This has to be the right implementation, because MSDN says so.
The configuration is loaded fine from the web.config, I can see the ReaderQuotas properties in both my classes are set correctly, but it looks like .NET isn't reading the ReaderQuotas config from my MessageEncodingBindingElement implementation.
My guess is .NET uses the GetProperty method to load the config, because MessageVersion is requested via this method. But the problem is, T is never equal to XmlDictionaryReaderQuotas, so the ReaderQuotas is never begin requested.
The root of my question is weird btw, I'm developing on a Windows 7 x64 machine with IIS7.5. Posting 'large' files (like 100 KB) works on my machine. But when I deploy the service to Windows Server 2008 R2 (tried 2 different servers), I get the following error:
The formatter threw an exception while trying to deserialize the
message: There was an error while trying to deserialize parameter
http://socialproxy.infocaster.net:argument. The InnerException message
was 'There was an error deserializing the object of type
System.Object. The maximum array length quota (16384) has been
exceeded while reading XML data. This quota may be increased by
changing the MaxArrayLength property on the XmlDictionaryReaderQuotas
object used when creating the XML reader. Line 1, position 1584.'.
Please see InnerException for more details.
And like I said, it works on my machine :-/
Could anybody tell me how I can resolve this?
Many thanks in advance!
The WCF service config:
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="wsdlExtensions" type="WCFExtras.Wsdl.WsdlExtensionsConfig, WCFExtras, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<add name="textOrMtomMessageBehavior" type="InfoCaster.SocialProxy.lib.TextOrMtom.TextOrMtomMessageBehavior, InfoCaster.SocialProxy" />
</behaviorExtensions>
<bindingElementExtensions>
<add name="textOrMtomEncoding" type="InfoCaster.SocialProxy.lib.TextOrMtom.TextOrMtomEncodingElement, InfoCaster.SocialProxy" />
</bindingElementExtensions>
</extensions>
<bindings>
<customBinding>
<binding name="TextOrMtomBinding">
<textOrMtomEncoding messageVersion="Soap11">
<readerQuotas maxDepth="32" maxStringContentLength="5242880" maxArrayLength="204800000" maxBytesPerRead="5242880" maxNameTableCharCount="5242880" />
</textOrMtomEncoding>
<httpTransport maxBufferSize="5242880" maxReceivedMessageSize="5242880" transferMode="Buffered" authenticationScheme="Anonymous" />
</binding>
</customBinding>
</bindings>
<services>
<clear />
<service name="InfoCaster.SocialProxy.SocialProxy" behaviorConfiguration="InfoCaster.SocialProxy.ISocialProxyServiceBehavior">
<endpoint name="SocialProxyServiceEndpoint" address="" binding="customBinding" bindingConfiguration="TextOrMtomBinding" contract="InfoCaster.SocialProxy.ISocialProxy" behaviorConfiguration="InfoCaster.SocialProxy.ISocialProxyEndpointBehavior" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<clear />
<behavior name="InfoCaster.SocialProxy.ISocialProxyServiceBehavior">
<!-- 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>
<endpointBehaviors>
<clear />
<behavior name="InfoCaster.SocialProxy.ISocialProxyEndpointBehavior">
<textOrMtomMessageBehavior />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>

Found the answer here (last post): http://social.msdn.microsoft.com/Forums/eu/wcf/thread/f5c0ea22-1d45-484e-b2c0-e3bc9de20915
Ok, I found it. You have to pass the readerquotes to the standard encoders. Unfortunantly there's no constructor for this so you have to set the property.
class TextOrMtomEncoder : MessageEncoder {
MessageEncoder _textEncoder;
MessageEncoder _mtomEncoder;
public TextOrMtomEncoder(MessageVersion messageVersion, XmlDictionaryReaderQuotas readerQuotas)
{
TextMessageEncodingBindingElement textEncoderBindingElement = new TextMessageEncodingBindingElement(messageVersion, Encoding.UTF8);
MtomMessageEncodingBindingElement mtomEncoderBindingElement = new MtomMessageEncodingBindingElement(messageVersion, Encoding.UTF8);
readerQuotas.CopyTo(mtomEncoderBindingElement.ReaderQuotas);
readerQuotas.CopyTo(textEncoderBindingElement.ReaderQuotas);
_mtomEncoder = mtomEncoderBindingElement.CreateMessageEncoderFactory().Encoder;
_textEncoder = textEncoderBindingElement.CreateMessageEncoderFactory().Encoder;
}

Related

XAMARIN, WCF with Custom User Name/Password and Principal

I am developing a WCF service that will provide data to a Xamarin client. I am trying to utilize custom user name/password and custom principle so I can attach usable information for the service to the identity. After many tries I have not been able to get anything but different error messages on the client. I believe the problem is in the WCF configuration, but I can not figure out what the problem is. My web.config code is below. Any help or tips on where to go would be greatly appreciated!
<system.serviceModel>
<diagnostics wmiProviderEnabled="true">
<messageLogging
logEntireMessage="true"
logMalformedMessages="true"
logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="true"
maxSizeOfMessageToLog="65535000"
maxMessagesToLog="3000"
/>
</diagnostics>
<behaviors>
<serviceBehaviors>
<behavior name="Behavior">
<!-- To avoid disclosing metadata information, set the values below
to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="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"/>
<serviceCredentials>
<serviceCertificate findValue="Cert" storeLocation="LocalMachine"
storeName="My" x509FindType="FindBySubjectName"/>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="Service.ServiceAuthenticator,
Service"/>
</serviceCredentials>
<serviceAuthorization
serviceAuthorizationManagerType="Service.CustomAuthorizationManager,
Service" principalPermissionMode="Custom">
<authorizationPolicies>
<add policyType="Service.AuthorizationPolicy, Service"/>
</authorizationPolicies>
</serviceAuthorization>
<serviceSecurityAudit
auditLogLocation="Application"
serviceAuthorizationAuditLevel="Failure"
messageAuthenticationAuditLevel="Failure"
suppressAuditFailure="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="Service" behaviorConfiguration="Behavior">
<endpoint address="" binding="basicHttpBinding"
contract="Service.IService" bindingConfiguration="Secure"/>
<endpoint address="mex" binding="mexHttpBinding"
contract="IMetadataExchange"/>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="Secure">
<security mode="Transport">
<transport clientCredentialType="Basic"
proxyCredentialType="None" realm=""/>
<message clientCredentialType="UserName"
algorithmSuite="Default"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
</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"/>
<httpErrors errorMode="Detailed" />
</system.webServer>
I'm using the WFC service as a connected service in the main project of the Xamarin solution. The service is being called with the code below:
Service.ServiceClient _client;
_client = new Service.ServiceClient();
_client.ClientCredentials.UserName.UserName =
"username";
_client.ClientCredentials.UserName.Password = "password";
//To allow service to connect even though certificate was not
validated.
ServicePointManager.ServerCertificateValidationCallback =
MyRemoteCertificateValidationCallback;
lci = await _client.WCFTestMethod();
To update the post on the comments below. wsHttpBinding is not supported by Xamarin, so I do have to use basicHttpBinding. I have gotten the checkAccessCore procedure to execute when I set the authentication to Anonymous on the IIS site, but it throws this error "No Identity Found" when the AuthorizationPolicy is executing GetClientIdentity. Is there a way to assign an identity in checkAccessCore?
I was able to get the service to work the way I wanted it to, by making sure the authentication method on IIS was set to Anonymous and in the Evaluate method of the AuthorizationPolicy I created a GenericIdentity and used it (example code below) instead of calling the GetClientIdentity method. The service seems to be working now and is authenticating the user in the checkAccessCore method.
Dim client As IIdentity = New GenericIdentity("User")
Dim cp As CustomPrincipal = New CustomPrincipal(client)
evaluationContext.Properties("Identities") = l
evaluationContext.Properties("Principal") = cp

TransactionFlowAttribute attribute set to Mandatory but the channel's binding is not configured with a TransactionFlowBindingElement

I have a big problem. I am trying to create a web service that will work with a distributed transaction.
All the code below is on the server side of the web service(the web service that is called from a client).
I wrote this in my interface:
[ServiceContract]
public interface IClientOperations
{
[OperationContract]
[ServiceKnownType(typeof(TriggerExecInput))]
[ServiceKnownType(typeof(TriggerExecOutput))]
[TransactionFlow(TransactionFlowOption.Mandatory)]
TriggerExecOutput TriggeredProfileDataUpdate(TriggerExecInput triggerInputData, bool isST3StatusActive);
And this in the web.config file:
<services>
<service name="ClientOperationsService" behaviorConfiguration="ServiceBehavior">
<endpoint address="" binding="wsHttpBinding"
bindingConfiguration="wsHttpBinding_Common" contract="SL.STAdmin.Applications.WebAPI.IClientOperations"/>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="wsHttpBinding_Common" transactionFlow="true">
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="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="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
If I right click the .svc file and click on "View in browser" I get the following error
Exception Details: System.InvalidOperationException: At least one operation on the 'ClientOperations' contract is configured with the TransactionFlowAttribute attribute set to Mandatory but the channel's binding 'BasicHttpBinding' is not configured with a TransactionFlowBindingElement. The TransactionFlowAttribute attribute set to Mandatory cannot be used without a TransactionFlowBindingElement.
I have other .svc files that don't use transactions.
They all work well.
I don't understand why it still tries to use the BasicHttpTransaction when I instruct it to use the other binding type.
DOes anyone have any idea what I am doing wrong?
Thank you in advance.
Add this inside your <system.serviceModel> element of your web.config:
<protocolMapping>
<add scheme="http" binding="wsHttpBinding" bindingConfiguration="wsHttpBinding_Common"/>
</protocolMapping>
You need to do a few things to get the transaction working.
Add the transactionflow to your operation
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
void TransactionSupported(int id, string name);
After that you add an operationbehavior to your implementation
[OperationBehavior(TransactionScopeRequired = true)]
public void TransactionSupported(int id, string name)
{
...
}
In your config file you need to add the transaction flow to your host binding
<system.serviceModel>
...
<bindings>
<netNamedPipeBinding> --> Your binding (don't use basicHttpBinding)
<binding transactionFlow="true"/>
</netNamedPipeBinding>
</bindings>
</system.serviceModel>
And last but not least you need to set the transactionflow of your client to get it working. In my example I do this in my code in my unit test, I think you can also do this in your configuration of your client, in your config file.
var factory = new ChannelFactory<IService>(callback,
new NetNamedPipeBinding() { TransactionFlow = true },
new EndpointAddress("net.pipe://localhost/ping"));

Dreaded WCF BAD request 400 due to maxReceivedMessageSize still being 65536

God don't you just love WCF.
I read all possible threads but I am really stuck now.
Here is WCF configuration:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BaseHttp"
maxBufferSize="4194304"
maxBufferPoolSize="4194304"
maxReceivedMessageSize="4194304" />
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="TaskServiceBehavior">
<serviceMetadata httpGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="True" />
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="TaskServiceBehavior" name="TaskService">
<endpoint
address="http://www.mysite.com/TableTaskService/TableTaskService.svc"
binding="basicHttpBinding" bindingConfiguration="BaseHttp"
contract="TableTaskService.ITableTaskService" />
<endpoint
address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://www.mysite.com/TableTaskService/" />
</baseAddresses>
</host>
</service>
</services>
<serviceHostingEnvironment>
<baseAddressPrefixFilters>
<add prefix="http://www.mysite.com/" />
</baseAddressPrefixFilters>
</serviceHostingEnvironment>
</system.serviceModel>
Now I send a message and get thrown this default exception ( saw in svclog file):
The maximum message size quota for incoming messages (65536) has been
exceeded. To increase the quota, use the MaxReceivedMessageSize
property on the appropriate binding element.
Now I clearly state that it should be 4mb.
My client code :
TableTaskServiceClient client =
new TableTaskServiceClient(
new BasicHttpBinding { MaxBufferSize = 4194304,
MaxReceivedMessageSize = 4194304 },
new EndpointAddress(GetEndpointAddressString())
);
Yet it throws this 65536 size error at me. Where is it coming from??..
Also does it REALLY matter that client sets up MaxReceivedMessageSize to same value as server? I think it would be logical that server was responsible for determining length, not client.
There is also this warning (notice how useful it is in what its saying, that is: no information on element that gets overridden or am I missing something), maybe basichttpbinding is exactly what gets overridden ? but why would that be? anyway:
[TraceRecord] Severity Warning
TraceIdentifier http://msdn.microsoft.com/en-US/library/System.ServiceModel.OverridingDuplicateConfigurationKey.aspx
Description The configuration system has detected a duplicate key in a
different configuration scope and is overriding with the more recent
value.
AppDomain /LM/W3SVC/11/ROOT/TableTaskService-33-1296567
Source System.ServiceModel.Configuration.ServiceBehaviorElementCollection/-851144413
ElementName behavior OldElementLineNumber 0 NewElementLineNumber 0
UPDTE:
reconfigured both service and client :
TableTaskServiceClient client = new TableTaskServiceClient(
new BasicHttpBinding {
MaxBufferSize = 4194304,
MaxReceivedMessageSize = 4194304,
MaxBufferPoolSize=4194304,
ReaderQuotas = new XmlDictionaryReaderQuotas
{
MaxArrayLength = 4194304,
MaxBytesPerRead = 4194304,
MaxDepth = 4194304,
MaxNameTableCharCount = 4194304,
MaxStringContentLength = 4194304
}
}, new EndpointAddress(GetEndpointAddressString()));
Same error still.
If that is of some interest I am sending a byte[] array of 467000~ length
http://msdn.microsoft.com/en-us/library/ms731361.aspx
http://msdn.microsoft.com/en-us/library/ms731325.aspx
http://www.codeproject.com/Articles/151753/BlogFeedList.aspx?amid=Andrii-Vasylevskyi&display=Mobile
Try this:
<basicHttpBinding>
<binding name="BaseHttp"
maxBufferSize="4194304"
maxBufferPoolSize="4194304"
maxReceivedMessageSize="4194304">
<readerQuotas maxArrayLength="4194304"
maxBytesPerRead="4194304"
maxDepth="4194304"
maxNameTableCharCount="4194304"
maxStringContentLength="4194304"
/>
</binding>
</basicHttpBinding>
Though some of those settings don't make sense, I simply set them to a large value.
Also, once you get this working I highly suggest you trim down the values, since they're present to prevent DoS attacks on your site.

invalid mscorlib exception in custom security attribute c'tor

I'm trying to implement my custom security attribute. It's very simple for now
[Serializable]
[ComVisible(true)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class SecPermissionAttribute : CodeAccessSecurityAttribute
{
public SecPermissionAttribute(SecurityAction action) : base(action) { }
public override System.Security.IPermission CreatePermission()
{
IPermission perm = new PrincipalPermission(PermissionState.Unrestricted);
return perm;
}
}
For some reason I've got an exception in the attribute c'tor
System.IO.FileLoadException occurred
Message=The given assembly name or codebase, 'C:\WINDOWS\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll', was invalid.
Source=WcfRoleProviderTestService
StackTrace:
at SecLib.SecPermissionAttribute..ctor(SecurityAction action)
at WcfRoleProviderTestService.Service1.GetData(Int32 value) in D:\TestProjects\WcfRoleProviderTestService\WcfRoleProviderTestService\Service1.svc.cs:line 19
InnerException:
The dll is signed. It seems to me like a security issue but I'm not sure. By the way I tried to use PrincipalPermissionAttribute and it works fine.
Forgot to say, I'm using VS 2010, FW 4.0, the attribute is concumed in the WCF service
I'll be very glad to get some help.
My service configuration is the following
<system.web>
<compilation debug="true" defaultLanguage="c#" targetFramework="4.0" />
<roleManager enabled="true" cacheRolesInCookie="true" cookieName=".ASPROLES"
defaultProvider="MyRoleProvider">
<providers>
<clear />
<add connectionStringName="Service1" applicationName="InfraTest"
writeExceptionsToEventLog="false" name="MyRoleProvider" type="SecLib.MyRoleProvider, SecLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=798c04e15cff851a" />
</providers>
</roleManager>
</system.web>
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBindingConfiguration" closeTimeout="00:01:00"
sendTimeout="00:10:00" maxBufferSize="524288" maxReceivedMessageSize="524288">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="WcfRoleProviderTestService.Service1"
behaviorConfiguration="BasicHttpServiceBehavior" >
<endpoint name="BasicHttpEndpoint"
contract="WcfRoleProviderTestService.IService1"
address="WcfAuthenticationTest"
binding="basicHttpBinding"
bindingConfiguration="BasicHttpBindingConfiguration" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost/WcfRoleProviderTestService/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="BasicHttpServiceBehavior">
<serviceAuthorization principalPermissionMode="UseAspNetRoles"
roleProviderName="MyRoleProvider" impersonateCallerForAllOperations="true" />
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
I've got the error both on Windows XP, IIS v5.1 and on Windows Server 2008 R2 IISV7.5 only if the WCF service is configured to use Windows Authentication (see the configuration above). On more interesting fact is that the error occured only if the attribute is used with the System.Security.Permissions.SecurityAction.Demand security action.
[OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
[SecPermission(System.Security.Permissions.SecurityAction.Demand)]
public string GetData(int value)
{
string userName = ServiceSecurityContext.Current.WindowsIdentity.Name;
return string.Format("You entered: {0}, User {1}", value, userName);
}
Other options work fine.
Thanks.
With a help of one of my colleagues, the problem has been soleved. I'm not sure what the exact reason of the exception was but it seems to be a compilation issue. When I changed the project type from web application to web site wich is compiled at run time according it's pool definition (64 or 32 bit) it started to work fine.

WCF: Cannot find my custom validator specified in web.config - customUserNamePasswordValidatorType - - Could not load file or assembly ... - help?

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");