I'm working on an WCF-based client/server application (WCF is self-hosted, not in IIS).
The WCF service has an operation to upload a chunk of data to the server. The contract roughly looks like this:
void UploadChunk(int clientId, byte[] chunk);
We are using Windows Authentication (Kerberos/NTLM) so we cannot use streaming here.
The binding looks like this (client- and server-side):
new BasicHttpBinding
{
Security = new BasicHttpSecurity
{
Mode = BasicHttpSecurityMode.TransportCredentialOnly,
Transport = { ClientCredentialType = HttpClientCredentialType.Windows },
},
MaxReceivedMessageSize = 0x7fffffff,
ReaderQuotas = { MaxArrayLength = 0x800000 },
};
The client talks to the service via proxy objects derived from System.ServiceModel.ClientBase<TChannel>
.
All of this works perfectly fine, but we observed that the WCF client sends each HTTP request twice, once without auth header and once again with the correct auth header. This is problematic because the requests will be pretty big and this behavior causes the request size to be two times the actual chunk size.
I already found out (https://weblog.west-wind.com/posts/2010/Feb/18/NET-WebRequestPreAuthenticate-not-quite-what-it-sounds-like) that setting WebRequest.PreAuthenticate
to true
remembers the auth header and reuses it for subsequent requests.
However from what I've seen up to now WCF does not expose a mechanism to modify the WebRequest instance.
Is there any solution for this problem`?
For Windows Authentication there will always be a challenge response (401
) for your first request .
If you're in control of all clients I think the most practical solution is to implement an operation with a minimal payload.
Operation void IsAuthenticated()
should do. For each client proxy instance you would then call IsAuthenticated
before UploadChunk
.
The IsAuthenticated
request would get you over the 401
challenge response without sending the large payload but will authenticate the connection. Subsequent requests for that connection will not be challenged.
Edit :
The behaviour I described seems to only be applicable with IIS 8. So I took a closer look with two http.sys traces, one for an IIS hosted service and one for a self hosted service.
The IIS hosted service seems to utilize some sort of optimization with regards to authentication. The first request for the connection is authenticated using the Authenticator Sspi Authenticator
. Subsequent requests are authenticated using the Fast Authenticator
.
None of these events are present in the self host trace which leads me to the conclusion that self hosting is not optimized for Windows Authentication.
http.sys - trace IIS
http.sys - trace self host
Then I found this blog entry that proposes a solution using NTLM, a custom binding and the unsafeConnectionNtlmAuthentication
setting for the HTTP transport. If you're willing to only use NTLM and the security concerns highlighted in the documentation are not a concern this seems to provide the behaviour you're looking for as per the http.sys trace.
http.sys trace - self host with custom binding
For the server use binding
<customBinding>
<binding name="myBinding">
<textMessageEncoding messageVersion="Soap11" />
<httpTransport authenticationScheme="Ntlm" unsafeConnectionNtlmAuthentication="true"/>
</binding>
</customBinding>
For your client you can use a regular basicHttpBinding with Ntlm security:
<basicHttpBinding>
<binding name="BasicHttpBinding_ITest">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" />
</security>
</binding>
</basicHttpBinding>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With