title: WCF with transport authentication using certificates and message authentication using username/password
body: <p>I'm trying to configure a WCF service to implement two factor authentication.  The idea being that there would be message level authentication with a username/password (WS security) and transport level authentication using a client certificate.</p>
  
  <p><strong>Step 1: Message level authentication</strong></p>
  
  <p>I've built a WCF service that uses message level authentication with username/password.  All works great.  I see the credentials passed from the client in the soap header and the service authenticates and authorizes the user.</p>
  
  <p>The System.ServiceModel.ServiceSecurityContext.Current.PrimaryIdentity object is a System.Security.Principal.WindowsIdentity type and has the following property values:</p>
  
  <ul>
  <li>AuthenticationType = Basic</li>
  <li>ImpersonationLevel = Impersonation</li>
  <li>IsAnonymous = false</li>
  <li>IsAuthenticated = true</li>
  <li>Name = my ad account (DOMAIN\accountname) </li>
  </ul>
  
  <p>The System.ServiceModel.ServiceSecurityContext.Current.WindowsIdentity object is also a windowsIdentity and has the same property values as above.</p>
  
  <p><strong>Step 2: Transport level authentication with certificates</strong></p>
  
  <p>Then I built a new WCF service that uses transport authentication with certificates.  Again, all works great.</p>
  
  <p>The System.ServiceModel.ServiceSecurityContext.Current.PrimaryIdentity object is a System.IdentityModel.Claims.X509Identity type and has the following property values:</p>
  
  <ul>
  <li>AuthenticationType = X509</li>
  <li>IsAuthenticated = true</li>
  <li>Name = the subject attribute of the certificate, thumbprint </li>
  </ul>
  
  <p>The System.ServiceModel.ServiceSecurityContext.Current.WindowsIdentity object is a System.Security.Principal.WindowsIdentity type and has the following property values:</p>
  
  <ul>
  <li>Authentication Type = ""</li>
  <li>ImpersonationLevel = Anonymous</li>
  <li>IsAnonymous = true</li>
  <li>IsAuthenticated = false</li>
  </ul>
  
  <p><strong>Step 3: Transport level authentication with certificates &amp; active directory mapping</strong></p>
  
  <p>Next, I turned on active directory mapping by creating a service behavior and setting the serviceCredientials\clientCertificate\authentication mapClientCertificateToWindowsAccount attribute to true.</p>
  
  <p>The service appears to correctly map the certificate to my active directory account. </p>
  
  <p>The System.ServiceModel.ServiceSecurityContext.Current.PrimaryIdentity object is a System.Security.Principal.WindowsIdentity type and has the following property values:</p>
  
  <ul>
  <li>AuthenticationType = SSL/PCT</li>
  <li>ImpersonationLevel = Identification</li>
  <li>IsAnonymous = false</li>
  <li>IsAuthenticated = true</li>
  <li>Name = my ad account (DOMAIN\accountname)</li>
  </ul>
  
  <p>The System.ServiceModel.ServiceSecurityContext.Current.WindowsIdentity object is also a windowsIdentity and has the same property values as above.</p>
  
  <p><strong>Step 4:  Message level authentication and transport authentication</strong></p>
  
  <p>With the above scenarios working, I now want to combine them.  A created a web service with username password message level authentication and certificate transport authentication, with Active Directory user mapping disabled.</p>
  
  <pre><code>&lt;?xml version="1.0"?&gt;
  &lt;configuration&gt;
    &lt;appSettings&gt;    
      &lt;add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" /&gt;
    &lt;/appSettings&gt;
    &lt;system.web&gt;
      &lt;compilation debug="true" targetFramework="4.5" /&gt;
      &lt;httpRuntime targetFramework="4.5"/&gt;
    &lt;/system.web&gt;
  
    &lt;system.serviceModel&gt;
      &lt;services&gt;
        &lt;service name="WCFTransportAuthCertificateMessageAuthUserName.Service1"
                 behaviorConfiguration="MapClientCertificates"&gt;
          &lt;endpoint binding="customBinding"
                 bindingConfiguration="TransportCertificateAuthentication_MessageUserNameAuthenticiation"
                    contract="WCFTransportAuthCertificateMessageAuthUserName.IService1"&gt;
          &lt;/endpoint&gt;
        &lt;/service&gt;
      &lt;/services&gt;
  
      &lt;bindings&gt;    
        &lt;customBinding&gt;
          &lt;binding name="TransportCertificateAuthentication_MessageUserNameAuthenticiation"&gt;
            &lt;textMessageEncoding messageVersion="Soap11"&gt;&lt;/textMessageEncoding&gt;
            &lt;security authenticationMode="UserNameOverTransport"&gt;&lt;/security&gt;
            &lt;httpsTransport requireClientCertificate="true"&gt;&lt;/httpsTransport&gt;
          &lt;/binding&gt;
        &lt;/customBinding&gt;
      &lt;/bindings&gt;
  
      &lt;behaviors&gt;
        &lt;serviceBehaviors&gt;
          &lt;behavior&gt;
            &lt;serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/&gt;
            &lt;serviceDebug includeExceptionDetailInFaults="false"/&gt;
          &lt;/behavior&gt;
  
          &lt;behavior name="MapClientCertificates"&gt;
            &lt;serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/&gt;
            &lt;serviceDebug includeExceptionDetailInFaults="false"/&gt;
  
            &lt;serviceCredentials&gt;
              &lt;clientCertificate&gt;
                &lt;authentication mapClientCertificateToWindowsAccount="false" includeWindowsGroups="true" /&gt;
              &lt;/clientCertificate&gt;
            &lt;/serviceCredentials&gt;
          &lt;/behavior&gt;
  
        &lt;/serviceBehaviors&gt;
      &lt;/behaviors&gt;
  
      &lt;protocolMapping&gt;
        &lt;add binding="basicHttpsBinding" scheme="https"/&gt;
      &lt;/protocolMapping&gt;
  
      &lt;serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /&gt;
  
    &lt;/system.serviceModel&gt;
  
    &lt;system.webServer&gt;
      &lt;modules runAllManagedModulesForAllRequests="true"/&gt;
      &lt;directoryBrowse enabled="true"/&gt;
    &lt;/system.webServer&gt;
  
  &lt;/configuration&gt;
  </code></pre>
  
  <p>The System.ServiceModel.ServiceSecurityContext.Current.PrimaryIdentity object is a System.Security.Principal.GenericIdentity type and has the following property values:</p>
  
  <ul>
  <li>Authentication Type = ""</li>
  <li>IsAuthenticated = false</li>
  <li>Name = ""</li>
  </ul>
  
  <p>The System.ServiceModel.ServiceSecurityContext.Current.WindowsIdentity object is a System.IdentityModel.Claims.X509Identity type and has the following property values:</p>
  
  <ul>
  <li>AuthenticationType = X509</li>
  <li>IsAuthenticated = true</li>
  <li>Name = the subject attribute of the certificate, thumbprint </li>
  </ul>
  
  <p><strong>So at this point, I can see details of the certificate credentials, but not of the message credentials.  That is my first question - why can't I see the message level credentials?</strong></p>
  
  <p><strong>Step 5:  Message level authentication and transport authentication with AD mapping.</strong></p>
  
  <p>Now I turn on AD user mapping by setting the serviceCredientials\clientCertificate\authentication mapClientCertificateToWindowsAccount attribute to true.</p>
  
  <p>The System.ServiceModel.ServiceSecurityContext.Current.PrimaryIdentity object is a System.Security.Principal.GenericIdentity type and has the following property values:</p>
  
  <ul>
  <li>Authentication Type = ""</li>
  <li>IsAuthenticated = false</li>
  <li>Name = ""</li>
  </ul>
  
  <p>The System.ServiceModel.ServiceSecurityContext.Current.WindowsIdentity object is a System.Security.Principal.WindowsIdentity type and has the following property values:</p>
  
  <ul>
  <li>AuthenticationType = ""</li>
  <li>ImpersonationLevel = Anonymous</li>
  <li>IsAuthenticated = false</li>
  <li>IsAnonymous = true</li>
  <li>Name = ""</li>
  </ul>
  
  <p>Authentication appears to be happening correctly at both the transport and message level.  If I specify the incorrect username/password in the soap header, I will get a soap fault back.  If I do not provide a client certificate, or provide an untrusted one, I also get an error
  <strong>So here, I understand that the System.ServiceModel.ServiceSecurityContext.Current.WindowsIdentity would be a System.Security.Principal.WindowsIdentity type because the service is configured to map to an AD account, but why did it not actually map?</strong></p>