title: WCF Service with wsHttpBinding - Manipulating HTTP request headers
body: <p>I have been following <a href="http://msdn.microsoft.com/en-us/library/ff649647.aspx" rel="nofollow noreferrer">this</a> tutorial in order to get username authentication with transport security working in my WCF service. The tutorial however refers to using <code>basicHttpBinding</code> which is unacceptable - I require <code>wsHttpBinding</code>.</p>
  
  <p>The idea is to have a custom <code>BasicAuthenticationModule</code> on WCF service which would read the "Authorization" header from the HTTP request and perform the auth process according to "Authorization" header contents. The problem is that "Authorization" header is missing!</p>
  
  <p>I have implemented <code>IClientMessageInspector</code> via custom behavior in order to manipulate outgoing messages and add custom SOAP headers. I have added the following code in <code>BeforeSendRequest</code> function:</p>
  
  <pre><code>    HttpRequestMessageProperty httpRequest = request.Properties.Where(x =&gt; x.Key == "httpRequest").Single().Value;
      httpRequest.Headers.Add("CustomHeader", "CustomValue");
  </code></pre>
  
  <p>This should work and according to many web resources, it works for <code>basicHttpBinding</code> but not <code>wsHttpBinding</code>. When I say "works", I mean that the header is successfully received by WCF service.</p>
  
  <p>This is the simplified function which inspects the received HTTP message on WCF service side:</p>
  
  <pre><code>    public void OnAuthenticateRequest(object source, EventArgs eventArgs)
      {
          HttpApplication app = (HttpApplication)source;
  
          //the Authorization header is checked if present
          string authHeader = app.Request.Headers["Authorization"];
          if (string.IsNullOrEmpty(authHeader))
          {
              app.Response.StatusCode = 401;
              app.Response.End();
          }
      }
  </code></pre>
  
  <p>Bottom posts of <a href="http://social.msdn.microsoft.com/Forums/en-NZ/wcf/thread/69ebfa7c-8a73-4fd1-adf3-d9deb863efad" rel="nofollow noreferrer">this</a> thread dated september 2011 say that this is not possible with <code>wsHttpBinding</code>. I don't want to accept that response.</p>
  
  <p>As a side note, if I use the Basic Authentication Module built in IIS and not the custom one, I get </p>
  
  <blockquote>
    <p>The parameter 'username' must not contain commas.** error message when trying <code>Roles.IsInRole("RoleName")</code> or `[PrincipalPermission(SecurityAction.Demand, Role = "RoleName")]</p>
  </blockquote>
  
  <p>probably because my <code>PrimaryIdentity.Name</code> property contains the certificate subject name as I am using <code>TransportWithMessageCredential</code> security with certificate-based message security.</p>
  
  <p>I am open for suggestions as well as alternate approaches to the problem. Thanks.</p>
  
  <p><strong><em>UPDATE</em></strong> </p>
  
  <p>As it seems, the HTTP header gets read correctly later throughout the WCF service code.
  <code>(HttpRequestMessageProperty)OperationContext.Current.IncomingMessageProperties["httpRequest"]</code> contains my custom header. However, this is already message-level.. How to pass the header to transport authentication routine?</p>
  
  <p><strong><em>UPDATE 2</em></strong><br>
  After a bit of a research, I came to a conclusion that, when a web browser receives HTTP status code 401, it presents me with the login dialog where I can specify my credentials. However a WCF client simply throws an exception and doesn't want to send credentials. I was able to verify this behavior when visiting <code>https://myserver/myservice/service.svc</code> in Internet Explorer. Tried to fix using information from <a href="https://stackoverflow.com/questions/1544830/wcf-transportcredentialonly-not-sending-username-and-password">this</a> link but to no avail. Is this a bug in WCF or am I missing something?</p>
  
  <p><strong><em>EDIT</em></strong></p>
  
  <p>Here are relevant sections from my <code>system.servicemodel</code> (from <code>web.config</code>) - I am pretty sure I got that configured right though.</p>
  
  <pre><code>  &lt;serviceBehaviors&gt;
      &lt;behavior name="ServiceBehavior"&gt;
        &lt;serviceMetadata httpsGetEnabled="true" httpGetEnabled="false" /&gt;
        &lt;serviceDebug includeExceptionDetailInFaults="true" /&gt;
        &lt;serviceCredentials&gt;
          &lt;clientCertificate&gt;
            &lt;authentication certificateValidationMode="ChainTrust" revocationMode="NoCheck" /&gt;
          &lt;/clientCertificate&gt;
          &lt;serviceCertificate findValue="server.uprava.djurkovic-co.me" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My" /&gt;
        &lt;/serviceCredentials&gt;
        &lt;serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="AspNetSqlRoleProvider" /&gt;
      &lt;/behavior&gt;
    &lt;/serviceBehaviors&gt;
      ................
    &lt;wsHttpBinding&gt;
      &lt;binding name="EndPointWSHTTP" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="20480000" maxReceivedMessageSize="20480000" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false"&gt;
        &lt;readerQuotas maxDepth="20480000" maxStringContentLength="20480000" maxArrayLength="20480000" maxBytesPerRead="20480000" maxNameTableCharCount="20480000" /&gt;
        &lt;reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" /&gt;
        &lt;security mode="TransportWithMessageCredential"&gt;
          &lt;transport clientCredentialType="Basic" /&gt;
          &lt;message clientCredentialType="Certificate" negotiateServiceCredential="true" algorithmSuite="Default" /&gt;
        &lt;/security&gt;
      &lt;/binding&gt;
    &lt;/wsHttpBinding&gt;
      ............
    &lt;service behaviorConfiguration="ServiceBehavior" name="DjurkovicService.Djurkovic"&gt;
      &lt;endpoint address="" binding="wsHttpBinding" bindingConfiguration="EndPointWSHTTP" name="EndPointWSHTTP" contract="DjurkovicService.IDjurkovic" /&gt;
    &lt;/service&gt;
  </code></pre>
  
  <p>The exception returned by the service is: </p>
  
  <blockquote>
    <p>The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Basic Realm,Negotiate,NTLM'. (The remote server returned an error: (401) Unauthorized.)</p>
  </blockquote>