Use Both Certificate and User/Pass to Consume Java Web Service From .Net - wcf-binding

I have a .Net c# client that needs to consume a Java web service from a third party. They require both a client cert and user name and password. I have the cert set up but constantly get 401 Unauthorized because I don't think the username and password are actually being attached to the request. It seems like WCF expects one or the other but not both cert and username/password. Surely I'm missing something.
<bindings>
<binding name="CC2WebSoap">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</bindings>
<client>
<endpoint address="https://url_goes_here.com"
binding="basicHttpBinding"
bindingConfiguration="CC2WebSoap"
contract="acontract"
name="CC2WebSoap"
behaviorConfiguration="SecureClientBehavior"/>
</client>
<behaviors>
<endpointBehaviors>
<behavior name="SecureClientBehavior">
<clientCredentials>
<clientCertificate findValue="mythumbprint" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
try
{
CC2WebSoap client= new CC2WebSoapClient("CC2WebSoap");
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
request = BuildRequest();
response = client.DoSomething(request);
}
catch(Exception e){ // Always get 401 exception here. }

This turned out to be reasonably simple by adding a MessageInspector and the related classes to get WCF to attach the username and password to the headers before each request. Specifically, I followed the advice in the blog post below exactly.
Using a MessageInspector To modify HTTP Headers

Related

Pass token from MVC to WCF service

I have a MVC app talking to ACS to get token for authentication. It's a claim based application. This works perfectly fine.
I am trying to call WCF service from MVC once authenticated with same taken so that i can use same claims for authorization.
MVC code is as below
var context = (BootstrapContext)identity.BootstrapContext;
var binding = new WS2007FederationHttpBinding(WSFederationHttpSecurityMode.Message);
binding.Security.Message.IssuedKeyType = SecurityKeyType.SymmetricKey;
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Message.IssuerBinding = new WS2007FederationHttpBinding();
EndpointAddress acsEndPoint =
new EndpointAddress("https://ACS namespace/v2/wsfederation");
binding.Security.Message.IssuerAddress = acsEndPoint;
binding.Security.Message.IssuedTokenType = "urn:ietf:params:oauth:token-type:jwt";
ChannelFactory<IService1> factory =
new ChannelFactory<IService1>(binding, new EndpointAddress("https://localhost/TestWCF/Service1.svc"));
factory.Credentials.SupportInteractive = false;
factory.Credentials.UseIdentityConfiguration = true;
var proxy = factory.CreateChannelWithIssuedToken(context.SecurityToken);
proxy.GetData(1);
WCF web config is as below
<system.serviceModel>
<services>
<service name="TestWCF.Service1">
<endpoint address="" behaviorConfiguration="webHttpAutoFormat" binding="ws2007FederationHttpBinding" bindingConfiguration="secureHttpBinding" contract="TestWCF.IService1"/>
<endpoint address="soap" binding="basicHttpBinding" contract="TestWCF.IService1" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<bindings>
<ws2007FederationHttpBinding>
<binding name="secureHttpBinding">
<security mode="None">
<message establishSecurityContext="false" issuedKeyType="SymmetricKey" issuedTokenType="urn:ietf:params:oauth:token- type:jwt">
<issuerMetadata address="https://ACS namespace/v2/wstrust/mex"></issuerMetadata>
</message>
</security>
</binding>
</ws2007FederationHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceCredentials useIdentityConfiguration="true"></serviceCredentials>
<serviceAuthorization principalPermissionMode="Always" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="webHttpAutoFormat">
</behavior>
</endpointBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true">
<serviceActivations>
<add relativeAddress="Service1.svc" service="TestWCF.Service1" />
</serviceActivations>
</serviceHostingEnvironment>
</system.serviceModel>
Please note my WCF service is not HTTPS also I am using JWT token from ACS. No certificates.
I get below error
The provided URI scheme 'https' is invalid; expected 'http'.
Parameter name: via
Can anyone help?
You are currently initializing your binding with
var binding = new WS2007FederationHttpBinding(WSFederationHttpSecurityMode.Message)
Try changing to
var binding = new WS2007FederationHttpBinding(WSFederationHttpSecurityMode.TransportWithMessageCredential)
From (MSDN - WS Transport With Message Credential):
By default, the wsHttpBinding binding provides HTTP communication.
When configured for transport security, the binding supports HTTPS
communication. HTTPS provides confidentiality and integrity protection
for the messages that are transmitted over the wire. However the set
of authentication mechanisms that can be used to authenticate the
client to the service is limited to what the HTTPS transport supports.
Windows Communication Foundation (WCF) offers a
TransportWithMessageCredential security mode that is designed to
overcome this limitation. When this security mode is configured, the
transport security is used to provide confidentiality and integrity
for the transmitted messages and to perform the service
authentication. However, the client authentication is performed by
putting the client credential directly in the message. This allows you
to use any credential type that is supported by the message security
mode for the client authentication while keeping the performance
benefit of transport security mode.
Your web config should have this instead for <ws2007FederationHttpBinding>:
<ws2007FederationHttpBinding>
<binding name="secureHttpBinding">
<security mode="TransportWithMessageCredential">
<message establishSecurityContext="false" issuedKeyType="SymmetricKey" issuedTokenType="urn:ietf:params:oauth:token- type:jwt">
<issuerMetadata address="https://ACS namespace/v2/wstrust/mex"></issuerMetadata>
</message>
</security>
</binding>
</ws2007FederationHttpBinding>
See also the following answer for some additional info as well: StackOverflow - The provided URI scheme 'https' is invalid; expected 'http'. Parameter name: via

CORS with WCF and windows authentication

Is it possible to handle "Cross Origin Resource Sharing" requests for a WCF service while enforcing Windows Authentication?
My scenario:
I have set up a self hosted WCF service exposed through a webHttpBinding.
This service is supposed to be called directly from the browser using jQuery. Practically speaking, this would limit me to using either the basicHttpBinding or the webHttpBinding. In this case, I'm using the webHttpBinding for calling the service operations.
The HTML pages (that will call the WCF service) are served from a web-server on the same machine but on a different port than the WCF service. This means I'll need CORS support to get this working in Firefox, Chrome, ...
Users must authenticate using Windows authentication when calling the WCF service. To this end, I have configured my webHttpBinding to use the transport security mode "TransportCredentialsOnly".
The W3C dictates that CORS should be used in such cases.
Simply stated, this means that the browser will detect that I am doing a cross-domain request. Before actually sending the request to my WCF service, it will send a so-called "preflight" request to my WCF service URL. This preflight request uses the HTTP method "OPTIONS" and asks whether the originating URL (= the webserver that served my HTML) is allowed to send the request to my service URL. The browser then expects an HTTP 200 response (= "OK") before sending the actual request to my WCF service. Any other reply from my service will prevent the actual request from being sent.
CORS is not built into WCF at this time, so I've used WCF extension points to add CORS compatibility.
The services section of the App.Config for my self-hosted service:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyApp.DefaultServiceBehavior">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="MyApp.DefaultEndpointBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="MyApp.DefaultWebHttpBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service
name="MyApp.FacadeLayer.LookupFacade"
behaviorConfiguration="MyApp.DefaultServiceBehavior"
>
<endpoint
contract="MyApp.Services.ILookupService"
binding="webHttpBinding"
bindingConfiguration="MyApp.DefaultWebHttpBinding"
address=""
behaviorConfiguration="MyApp.DefaultEndpointBehavior"
>
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost/Temporary_Listen_Addresses/myapp/LookupService"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
I have implemented an IDispatchMessageInspector that replies to preflight messages:
public class CORSSupport : IDispatchMessageInspector
{
private Dictionary<string, string> requiredHeaders;
public CORSSupport(Dictionary<string, string> requiredHeaders)
{
this.requiredHeaders = requiredHeaders ?? new Dictionary<string, string>();
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
HttpRequestMessageProperty httpRequest = request.Properties["httpRequest"] as HttpRequestMessageProperty;
if (httpRequest.Method.ToUpper() == "OPTIONS")
instanceContext.Abort();
return httpRequest;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
HttpRequestMessageProperty httpRequest = correlationState as HttpRequestMessageProperty;
HttpResponseMessageProperty httpResponse = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (KeyValuePair<string, string> item in this.requiredHeaders)
httpResponse.Headers.Add(item.Key, item.Value);
string origin = httpRequest.Headers["origin"];
if (origin != null)
httpResponse.Headers.Add("Access-Control-Allow-Origin", origin);
if (httpRequest.Method.ToUpper() == "OPTIONS")
httpResponse.StatusCode = HttpStatusCode.NoContent;
}
}
This IDispatchMessageInspector is registered through a custom IServiceBehavior attribute.
I call my service through jQuery like so:
$.ajax(
{
url: 'http://localhost/Temporary_Listen_Addresses/myapp/LookupService/SomeLookup',
type: 'GET',
xhrFields:
{
withCredentials: true
}
}
)
.done(function () { alert('Yay!'); })
.error(function () { alert('Nay!'); });
This works in IE10 and Chrome (I get a message box saying "Yay!"), but not in Firefox. In Firefox, I get a "Nay!" and a HTTP 401 (unauthorized) error.
This 401 is due to the "Windows Authentication" that I have set up in my service configuration. The way authentication works is the browser first sends a request without any authentication info. The server then replies back with HTTP 401 (unauthorized) indicating the authentication method to use. The browser would then normally re-submit the request including the user-credentials (after which the request would proceed normally).
Unfortunately, it seems the W3C has indicated that credentials should not be passed into the CORS preflight messages. Hence, WCF replies back with a HTTP 401. It seems that Chrome somehow does send the credentials in the preflight request header (which is actually incorrect, according to W3C specs), while Firefox does not.
Furthermore, the W3C recognizes only the HTTP 200 response to a preflight requests: any other response (such as the HTTP 401 I receive) simply means the CORS request failed and the actual request may not be submitted...
I don't know how to get this (simple) scenario working. Can anyone help?
Gotten a bit further.
With .NET 4.5, it is possible to support multiple authentication schemes for a single endpoint. This allowed me to define both Windows authentication and anonymous authentication simultaneously:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyApp.DefaultServiceBehavior">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
<serviceAuthenticationManager authenticationSchemes="Negotiate, Anonymous"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="MyApp.DefaultEndpointBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="MyApp.DefaultWebHttpBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="InheritedFromHost"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service
name="MyApp.FacadeLayer.LookupFacade"
behaviorConfiguration="MyApp.DefaultServiceBehavior"
>
<endpoint
contract="MyApp.Services.ILookupService"
binding="webHttpBinding"
bindingConfiguration="MyApp.DefaultWebHttpBinding"
address=""
behaviorConfiguration="MyApp.DefaultEndpointBehavior"
>
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost/Temporary_Listen_Addresses/myapp/LookupService"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
This way, my IDispatchMessageInspector does get called and I can properly handle the preflight messages for all browsers.
I then wanted to adapt my IDispatchMessageInspector to enforce authentication for any request other than the preflights:
public class CrossOriginResourceSharingMessageInspector : IDispatchMessageInspector
{
private Dictionary<string, string> requiredHeaders;
public CrossOriginResourceSharingMessageInspector(Dictionary<string, string> requiredHeaders)
{
this.requiredHeaders = requiredHeaders ?? new Dictionary<string, string>();
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
HttpRequestMessageProperty httpRequestHeader = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
if (httpRequestHeader.Method.ToUpper() == "OPTIONS")
instanceContext.Abort();
else if (httpRequestHeader.Headers[HttpRequestHeader.Authorization] == null)
instanceContext.Abort();
return httpRequestHeader;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
HttpRequestMessageProperty httpRequestHeader = correlationState as HttpRequestMessageProperty;
HttpResponseMessageProperty httpResponseHeader = reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;
foreach (KeyValuePair<string, string> item in this.requiredHeaders)
httpResponseHeader.Headers.Add(item.Key, item.Value);
string origin = httpRequestHeader.Headers["origin"];
if (origin != null)
httpResponseHeader.Headers.Add("Access-Control-Allow-Origin", origin);
string method = httpRequestHeader.Method;
if (method.ToUpper() == "OPTIONS")
{
httpResponseHeader.StatusCode = HttpStatusCode.NoContent;
}
else if (httpRequestHeader.Headers[HttpRequestHeader.Authorization] == null)
{
httpResponseHeader.StatusDescription = "Unauthorized";
httpResponseHeader.StatusCode = HttpStatusCode.Unauthorized;
}
}
}
Again, this seems to work for IE and Chrome, but not for Firefox. Preflight is now ok for Firefox, but it seems Firefox is not re-submitting the request after I replied with a HTTP 401 when the actual request didn't contain user credentials. In fact, I would expect Firefox to send the credentials along with the GET request immediately (seeing as I added "withCredentials: true" in my jQuery AJAX request; Chrome does seem to do this correctly, though).
What am I doing wrong?
Eureka (kind of). It seems that Firefox didn't like the "Negotiate" authentication I specified for my service. It seems to work when I change the authentication scheme from "Negotiate, Anonymous" to "Ntlm, Anonymous":
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyApp.DefaultServiceBehavior">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
<serviceAuthenticationManager authenticationSchemes="Ntlm, Anonymous"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="MyApp.DefaultEndpointBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="MyApp.DefaultWebHttpBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="InheritedFromHost"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service
name="MyApp.FacadeLayer.LookupFacade"
behaviorConfiguration="MyApp.DefaultServiceBehavior"
>
<endpoint
contract="MyApp.Services.ILookupService"
binding="webHttpBinding"
bindingConfiguration="MyApp.DefaultWebHttpBinding"
address=""
behaviorConfiguration="MyApp.DefaultEndpointBehavior"
>
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost/Temporary_Listen_Addresses/myapp/LookupService"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
I thought Firefox supported the "Negotiate" scheme... Anyone has any idea why it didn't work?

WCF message authentication with both username and certificate

Long story short:
My WCF clients should be able to provide both username and certificate to a service hosted in IIS, where I should use that information to validate requests using a custom policies.
Complete story:
I have the need to authenticate some WCF clients to verify if they can execute operations.
We have two kinds of clients: WPF applications and a web application. We would like to do the following:
The web application uses a certificate trusted by the service so that it is recognized as a special user with all permissions (the web application already verifies permissions by itself and we wouldn't like to touch it by now)
The WPF clients authenticate themselves with username/password provided by the user
In the implementation of the operations, I would like to verify if the certificate was provided (then I recognize the "super user"), otherwise fallback to username/password authentication.
Services are hosted in IIS 7 and we need to use NetTcpBinding.
I was able to implement the username validation, but the problem is that the AuthorizationContext inspected by the service contains only identity information, and not the certificate.
The following code is used on the client side to initialize the creation of channels (from a spike I'm using to test the solution):
var factory = new ChannelFactory<T>(this.Binding, address);
var defaultCredentials = factory.Endpoint.Behaviors.Find<ClientCredentials>();
factory.Endpoint.Behaviors.Remove(defaultCredentials);
var loginCredentials = new ClientCredentials();
loginCredentials.ServiceCertificate.Authentication.CertificateValidationMode =
X509CertificateValidationMode.None;
loginCredentials.UserName.UserName = username;
loginCredentials.UserName.Password = password;
if (useCertificate)
{
loginCredentials.SetCertificate();
}
factory.Endpoint.Behaviors.Add(loginCredentials);
return factory.CreateChannel();
With the SetCertificate extension being implemented like this:
public static void SetCertificate(this ClientCredentials loginCredentials)
{
loginCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "SecureWcfClient");
}
This is the configuration of the web application hosting the services:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="SecureBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<serviceCertificate findValue="Test"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
<clientCertificate>
<authentication certificateValidationMode="Custom" customCertificateValidatorType="AuthenticationProtectedService.Security.CertificateValidator, AuthenticationProtectedService.Security"/>
</clientCertificate>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="AuthenticationProtectedService.Security.UserNamePassValidator, AuthenticationProtectedService.Security" />
</serviceCredentials>
<serviceAuthorization serviceAuthorizationManagerType="AuthenticationProtectedService.Security.CertificateAuthorizationManager, AuthenticationProtectedService.Security"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding>
<security mode="None"/>
</binding>
<binding name="SecureNetTcp">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</netTcpBinding>
</bindings>
<service
name="AuthenticationProtectedService.Services.OneWayServiceB"
behaviorConfiguration="SecureBehavior">
<endpoint
address=""
binding="wsHttpBinding"
contract="AuthenticationProtectedService.ServiceModel.IOneWayServiceB">
</endpoint>
</service>
<service
name="AuthenticationProtectedService.Services.DuplexServiceB" behaviorConfiguration="SecureBehavior">
<endpoint
address=""
binding="netTcpBinding"
bindingConfiguration="SecureNetTcp"
contract="AuthenticationProtectedService.ServiceModel.IDuplexServiceB">
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
Finally, this is the implementation of the custom authorization manager (I also tried with a custom certificate validator but the function was never run)
public class CertificateAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
if (!base.CheckAccessCore(operationContext))
{
return false;
}
string thumbprint = GetCertificateThumbprint(operationContext);
// I'd need to verify the thumbprint, but it is always null
return true;
}
private string GetCertificateThumbprint(OperationContext operationContext)
{
foreach (var claimSet in operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)
{
foreach (Claim claim in claimSet.FindClaims(ClaimTypes.Thumbprint, Rights.Identity))
{
string tb = BitConverter.ToString((byte[])claim.Resource);
tb = tb.Replace("-", "");
return tb;
}
}
return null;
}
}
I think that the problem could be in the clientCredentialType property of the nettcpbinding.Security.Message node on the service configuration, but I don't see the option to use both Certificate and Username withing the Message security.
Any help appreciated, thanks
Remark: a specific goal of the project is to have very low level impact on server setup and in general in the system, so also SSL should be avoided if possible.
try out this link http://msdn.microsoft.com/en-us/library/ms733099.aspx ...it might resolve your issue where in you can have different binding configuration for same binding type and associate the same to different endpoints as per your need.

WCF Windows authentication issue with REST service

I'm having some difficulty setting up a WCF service to run under Windows authentication. The service is only consumed via jQuery using ajax.
IIS (version 6 on server 2003) is set to only allow Windows Authentication.
web.config has the <authentication mode="Windows" /> tag.
Here's the service section of the web.config:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="AspNetAjaxBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<services>
<service name="SearchService" behaviorConfiguration="ServiceBehavior">
<endpoint address="http://localhost:9534/SearchService.svc" behaviorConfiguration="AspNetAjaxBehavior"
binding="webHttpBinding" bindingConfiguration="webWinBinding"
name="searchServiceEndpoint" contract="MyApp.Services.ISearchService">
</endpoint>
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="webWinBinding" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows"/>
</security>
<readerQuotas maxArrayLength="100000" maxStringContentLength="2147483647" />
</binding>
</webHttpBinding>
</bindings>
The interface looks like this:
[ServiceContract(Namespace = "http://MyService.ServiceContracts/2012/02", Name = "SearchService")]
public interface ISearchService
{
[WebGet(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "GetSomeData?filter={filter}")]
[OperationContractAttribute(Action = "GetSomeData")]
string GetSomeData(string filter);
}
And the implementation:
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class SearchService : ISearchService
{
public string GetSomeData(string filter)
{
// Call Database and get some results
// return the results
return "";
}
}
When I navigate to the service in Internet Explorer, it prompts me for my username and password, despite having Windows Authentication turned on.
As soon as I enable Anonymous Authentication, the service loads just fine and everything works. Problem is, I have other things going on in the web application that require anonymous to be turned off.
I've scoured the web and can't find anything on this problem.

WCF service authentication using iisexpress transport security and basic authentication always returning 401

I have a WCF service configured to use Transport security and basic authentication.
The service is hosted in iiexpress withing vs2010.
I am able to connect from my client code but always receive:
"The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Basic realm=realm'."
And this has an inner exception of:
"The remote server returned an error: (401) Unauthorized."
Similar to Can not call web service with basic authentication using WCF although my client code already has the settings set out in the answer.
I also followed HTTP Basic Authentication against Non-Windows Accounts in IIS/ASP.NET (Part 3 - Adding WCF Support) and the previous blog to set up a Module and the IAuthorizationPolicy classes.
IISExpress is configed in classic mode with anonymous and windows authentication disabled and SSL enabled.
Client Config:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="NotificationHttpBinding">
<security mode="Transport">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://localhost/NotificationService.svc"
binding="basicHttpBinding" bindingConfiguration="NotificationHttpBinding"
contract="NotificationPortType" name="BasicHttpBinding_NotificationPortType" />
</client>
</system.serviceModel>
Service Config:
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="Notification.NotificationService" behaviorConfiguration="NotificationServiceBehavior">
<endpoint binding="basicHttpBinding" contract="NotificationPortType" bindingConfiguration="NotificationHttpBinding" >
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="NotificationServiceBehavior">
<serviceMetadata />
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceAuthorization>
<authorizationPolicies>
<add policyType="Notification.HttpContextIdentityPolicy, Notification" />
</authorizationPolicies>
</serviceAuthorization>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="NotificationHttpBinding">
<security mode="Transport">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
</bindings>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
<system.web>
<httpModules>
<add name="CustomBasicAuthentication" type="Notification.CustomBasicAuthenticationModule, Notification"/>
</httpModules>
<membership defaultProvider="SampleProvider">
<providers>
<add name="SampleProvider" type="Notification.HardcodedSecurityProviders, Notification" />
</providers>
</membership>
</system.web>
Client Code is nothing major:
static void Main(string[] args)
{
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => { return true; };
NotificationPortTypeClient client = new NotificationPortTypeClient("BasicHttpBinding_NotificationPortType");
client.ClientCredentials.UserName.UserName = "Test";
client.ClientCredentials.UserName.Password = "PWD";
client.sendNotification(new NotificationRequest());
}
Alternatively
If someone can show me an alternative of how to use IIS6 to host a service WCF which using basic http authentication while requiring SSL (https) I'll be happy with that!
UPDATE
Seems this was my culprit all along:
Avoid http 401 round trip
However, I found that my modules fired fine (in integrated mode) but I was then presented with a service error telling me that basic integration is required but not enabled on the host.
Opened up iisexpress applicationhost.config file and sure enough I found:
<section name="basicAuthentication" overrideModeDefault="Deny" />
followed by
<basicAuthentication enabled="false" />
further down
I've changed these to <section name="basicAuthentication" overrideModeDefault="Allow" />
and tried to enable in my web.config...no dice :(
You need to use WSHttpBinding.
There is a complete sample here.