WP7 + WCF + IIS + HTTPS (Transport) + Basic Authentication - wcf

I've read a lot of posts about the problem to use WP7 + WCF (IIS 7) over HTTPS with basic authentication but I'm still in trouble with it...
If I just use HTTPS transport without BasicAuth it works like a charm. But both combinated doesn't work for me...
Maybe you can help me to identify my failure...
My ClientConfig:
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService1" maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="Transport" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"
contract="ServiceReference1.IService1" name="BasicHttpBinding_IService1" />
</client>
</system.serviceModel>
My ServiceConfig:
<?xml version="1.0"?>
<configuration>
<appSettings/>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
<pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WP7.CustomUserNameValidator, WP7" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<bindings>
<basicHttpBinding>
<binding maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security mode="TransportWithMessageCredential" >
<transport clientCredentialType="Basic"/>
<message clientCredentialType="UserName"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
My CustomUserNameValidator used in Service:
namespace WP7
{
public class CustomUserNameValidator : UserNamePasswordValidator
{
// This method validates users. It allows in two users,
// test1 and test2 with passwords 1tset and 2tset respectively.
// This code is for illustration purposes only and
// MUST NOT be used in a production environment because it
// is NOT secure.
public override void Validate(string userName, string password)
{
if (null == userName || null == password)
{
throw new ArgumentNullException();
}
if (!(userName == "test1" && password == "1tset") && !(userName == "test2" && password == "2tset"))
{
throw new FaultException("Unknown Username or Incorrect Password");
}
}
}
}
The code in my Wp7 App to call a method a sync (with a solution from: http://cisforcoder.wordpress.com/2010/12/01/how-to-implement-basic-http-authentication-in-wcf-on-windows-phone-7/#comment-174):
proxy = new ServiceReference1.Service1Client();
proxy.Endpoint.Address = new System.ServiceModel.EndpointAddress(new Uri(Details.mySettings.EndpointAddress));
proxy.PingServerCompleted += new EventHandler<ServiceReference1.PingServerCompletedEventArgs>(proxy_PingServerCompleted);
var credentials = EncodeBasicAuthenticationCredentials("test1", "1tset");
using (OperationContextScope scope =
new OperationContextScope(proxy.InnerChannel))
{
HttpRequestMessageProperty request = new HttpRequestMessageProperty();
request.Headers[System.Net.HttpRequestHeader.Authorization] = "Basic " + credentials;
OperationContext.Current.OutgoingMessageProperties.Add(
HttpRequestMessageProperty.Name, request);
proxy.PingServerAsync(myServer);
}
private string EncodeBasicAuthenticationCredentials(string username, string password)
{
//first concatenate the user name and password, separated with :
string credentials = username + ":" + password;
//Http uses ascii character encoding, WP7 doesn’t include
// support for ascii encoding but it is easy enough to convert
// since the first 128 characters of unicode are equivalent to ascii.
// Any characters over 128 can’t be expressed in ascii so are replaced
// by ?
var asciiCredentials = (from c in credentials
select c <= 0x7f ? (byte)c : (byte)'?').ToArray();
//finally Base64 encode the result
return Convert.ToBase64String(asciiCredentials);
}
Furthermore I've already set "Basic Authentication" setting in the IIS Virtual Directory to "Enabled".
Everytime I've got some different error exceptions:
either CommunicationException or SecurityException or what else...
Someone an idea which might be solve my problem?
Thanks.
Jason

The client needs to also specify the clientCredentialType - that's missing from the client config. So the client isn't expecting to have to send credentials but the service is expecting them

Related

WCF service not show metadata on server (return 404), only locally it works

I have a problem with metadata (wsdl on service soap).
Locally everything works fine. and at the address https: // localhost / DataImportSoapService /? wsdl the wsdl is exposed.
Nothing is shown on the server. Return 404.
I state that I have other active services in WCF and that the other .svc are correctly exposed.
While this basicHttpBinding service with basic auth doesn't work.
So on the server I activated everything:
My web.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="DatineoUser" value="zzzzzz" />
<add key="DatineoPass" value="xxxx" />
<add key="UrlServiceNewEra" value="https://xxxxx/common/importDataCarrier?satcode=datineo" />
</appSettings>
<system.web>
<compilation debug="true" />
</system.web>
<!-- When deploying the service library project, the content of the config file must be added to the host's
app.config file. System.Configuration does not support config files for libraries. -->
<system.serviceModel>
<services>
<service behaviorConfiguration="MetadataBehavior" name="TA_service_library_BA.DataImportSoapService">
<endpoint address="datineoV1" binding="basicHttpBinding"
bindingConfiguration="wsHttpBinding_LargeBinding" name="EndpointAVeryBigSum_BasicAuthentication"
contract="TA_service_library_BA.IDataImportSoapService" />
<endpoint address="mex"
binding="mexHttpsBinding"
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="https://localhost/DataImportSoapService/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MetadataBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="TA_service_library_BA.ServiceAuthenticator, TA_service_library_BA"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="wsHttpBinding_LargeBinding" closeTimeout="00:05:00" openTimeout="00:05:00" receiveTimeout="00:15:00" sendTimeout="00:15:00" maxBufferSize="15728640" maxBufferPoolSize="15728640" maxReceivedMessageSize="15728640">
<readerQuotas maxDepth="2000000" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
</configuration>
My classes:
namespace TA_service_library_BA
{
public class ServiceAuthenticator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password))
throw new SecurityTokenException("Username and password required");
if (!(userName == ConfigurationManager.AppSettings["DatineoUser"] && password == ConfigurationManager.AppSettings["DatineoPass"]))
throw new FaultException(string.Format("Wrong username ({0}) or password ", userName));
}
}
}
namespace TA_service_library_BA
{
// NOTA: è possibile utilizzare il comando "Rinomina" del menu "Refactoring" per modificare il nome di interfaccia "IDataImportService" nel codice e nel file di configurazione contemporaneamente.
[ServiceContract(Namespace = "http://sen.company.com")]
[XmlSerializerFormat]
public interface IDataImportSoapService
{
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare)]
PushResponse PushDataArray(PushData pushDataArray);
//++++++++++++++++++++++++++++++++++++++++++++++
}
}
namespace TA_service_library_BA
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in both code and config file together.
public class DataImportSoapService : IDataImportSoapService
{
public PushResponse PushDataArray(PushData pushDataArray)
{
PushResponse responsePush = new PushResponse();
try
{
string authkey = ConfigurationManager.AppSettings["DatineoUser"] + ":" + ConfigurationManager.AppSettings["DatineoPass"];
string encAuthkey = CommFun.Base64Encode(authkey);
WebRequest request = WebRequest.Create(ConfigurationManager.AppSettings["UrlServiceNewEra"]);
request.ContentType = "application/json";
request.Method = "POST";
request.Headers.Add("Authorization", "Basic " + encAuthkey);
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
string json = new JavaScriptSerializer().Serialize(pushDataArray);
streamWriter.Write(json);
}
var response = (HttpWebResponse)request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
if (response.StatusCode.Equals(HttpStatusCode.OK) || response.StatusCode.Equals(HttpStatusCode.NoContent))
{
//istanzio la risposta contenitore
responsePush.success = true;
responsePush.message = responseString;
}
else
{
//istanzio la risposta contenitore
responsePush.success = false;
responsePush.message = responseString;
}
}
catch (Exception ex)
{
//istanzio la risposta contenitore
responsePush.success = false;
responsePush.message = ex.Message;
}
return responsePush;
}
}
}
Why do some projects expose at least the .svc while this last service gives me 404 to the .svc file?
Beyond this then I don't understand what the hell is wrong with the configuration ... with IIS express everything goes .. on the server no.
Thanks for your help. I've been banging my head for a week and a half. I have tried the impossible. Tnx
You can check the following conditions:
Whether the permission of the managed directory is granted.
Whether the.NET version matches.
Is there a valid certificate on the site.
Try to put the image in the same path and find it.

Test program just wont invoke service, yet everything looks okay - getting 404 not found

I've created a WCF service to receive data from a sending application. When run, the usual index listing appears:
and clicking on the .svc shows the success page:
So, I've created a basic test form to invoke the methods of the service (there are 2, both are "POST" methods). Since the service methods use JSON & webhttp, I am attempting to invoke the service via an HttpWebRequest.
private void button1_Click(object sender, EventArgs e) {
try {
var request = (HttpWebRequest)WebRequest.Create("http://localhost:54945/Take2ToOffice.svc/ErrorReport");
request.ContentType = "'text/json; charset=utf-8'";
request.Method = "POST";
byte[] chunk = new byte[2048]; //chunk gets initialised, but what it
//contains is mostly irrelevant here
string result = "";
ErrorInfo EI = new ErrorInfo {
Edumis = "9999",
SiteName = "Unknown",
ErrorID = "123",
ContentBlock = chunk
};
string json = new JavaScriptSerializer().Serialize(EI);
//MessageBox.Show(json);
using (var sW = new StreamWriter(request.GetRequestStream())) {
sW.Write(json);
sW.Flush();
sW.Close();
}
var response = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(response.GetResponseStream())) {
result = streamReader.ReadToEnd();
}
MessageBox.Show(result);
}
catch (WebException wex) {
string lcResult;
if (wex.Response != null) {
lcResult = "ERROR (web exception, response generated): " + Environment.NewLine;
StreamReader sr = new StreamReader(wex.Response.GetResponseStream());
lcResult += sr.ReadToEnd();
}
else {
lcResult = "ERROR (web exception, NO RESPONSE): " + wex.Message + wex.StackTrace;
}
MessageBox.Show(lcResult);
}
catch (Exception ex) {
MessageBox.Show(ex.Message);
}
}
It all seems like pretty basic stuff - especially since I wrote another service very similar to this a few years ago, and my starting point for everything I've done now, was the code I wrote previously - and at this point, I'm just doing this in my development environment.
Yet,
var response = (HttpWebResponse)request.GetResponse();
Returns 404 Not Found. The WebException.Response contains:
{System.Net.HttpWebResponse}
[System.Net.HttpWebResponse]: {System.Net.HttpWebResponse}
base: {System.Net.HttpWebResponse}
ContentLength: 0
ContentType: ""
Headers: {X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcVXNlclxEb2N1bWVudHNcVmlzdWFsIFN0dWRpb1xWaXN1YWwgU3R1ZGlvIDIwMTNcUHJvamVjdHNcVGFrZTJPZmZpY2VDb21tdW5pY2F0aW9uc1xUYWtlMk9mZmljZUNvbW11bmljYXRpb25zXFRha2UyVG9PZmZpY2Uuc3ZjXEVycm9yUmVwb3J0?=
Content-Length: 0
Cache-Control: private
Date: Thu, 27 Apr 2017 03:19:29 GMT
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
}
IsFromCache: false
IsMutuallyAuthenticated: false
ResponseUri: {http://localhost:54945/Take2ToOffice.svc/ErrorReport}
SupportsHeaders: true
But there is no actual response at all (length = 0). If I put a breakpoint in the service, (and start both the test form, and the service - separate projects in the same solution), the breakpoint is never hit.
Logically it looks like the service isn't running, or for some reason can't be found - but the listing and success page are readily seen in a browser, and at the address (localhost:54945) shown in the program.
Can anyone explain what's going on, or maybe what's going wrong? Please help, I'm totally stumped.
The web.config is as follows:
<?xml version="1.0"?>
<configuration>
<connectionStrings>
...
</connectionStrings>
<appSettings>
...
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5"/>
<httpRuntime/>
</system.web>
<system.serviceModel>
<services>
<service name="Take2OfficeCommunications.Take2ToOffice" behaviorConfiguration="serviceBehavior">
<endpoint address="rest"
binding="webHttpBinding"
bindingConfiguration="WebBinding"
behaviorConfiguration="web"
name="restEndpoint"
contract="Take2OfficeCommunications.ITake2ToOffice"/>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex"/>
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="WebBinding" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647">
<security mode="Transport" />
<readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp helpEnabled="true" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="serviceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
Thanks for looking, and for any comments you can make.

WCF authentication is not working

I am using Message Security for WCF authentication. And my clientCredentialType="UserName".
Even if I am not providing valid username and password while accessing a service, it is working fine.
It should do authentication, If credentials are correct then only it should allow to access.`enter code here
The code is as follows:
WCF service behaviour section:
<behaviors>
<serviceBehaviors>
<behavior name="AuthenticationBehaviour">
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WcfServiceAuthentication.Authenticator, WcfServiceAuthentication"/>
</serviceCredentials>
<!-- 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="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
WCF Service Binding section in Web.config
<bindings>
<wsHttpBinding>
<binding name="Binding1">
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
My authentication class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IdentityModel.Selectors;
using System.ServiceModel;
using log4net;
using System.Reflection;
namespace WcfServiceAuthentication
{
public class Authenticator : UserNamePasswordValidator
{
private static ILog _logger = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public override void Validate(string userName, string password)
{
_logger.Info("Validate called with username:" + userName + " and password:" + password);
if (null == userName || null == password)
{
throw new ArgumentNullException();
}
if (!(userName == "Admin" && password == "Admin123"))
{
// This throws an informative fault to the client.
throw new FaultException("Unknown Username or Incorrect Password");
}
_logger.Info("End called");
}
}
}
My Authentication service
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)]
public class AuthenticationService : IAuthenticationService
{
public int add(int num1, int num2)
{
return (num1 + num2);
}
}
}
And Client application:
AuthenticationServiceClient proxy = new AuthenticationServiceClient();
//proxy.ClientCredentials.UserName.UserName = "Admin";
//proxy.ClientCredentials.UserName.Password = "Admin123";
int addition= proxy.add(10, 10);
return View();
Here even though I am not providing credentials, Add method is working fine. It should ask for Authentication.
Modify the web config by adding the below tags to enable authentication service.
<system.web.extensions> <scripting>
<webServices>
<authenticationService enabled="true"
requireSSL = "true"/>
</webServices> </scripting> </system.web.extensions>
It should be added to the web config file. The sample is as here
<system.web.extensions>
<scripting>
<webServices>
<authenticationService enabled="true"
requireSSL = "true"/>
</webServices>
</scripting>
</system.web.extensions>
<system.serviceModel>
<services>
<service name="System.Web.ApplicationServices.AuthenticationService"
behaviorConfiguration="AuthenticationServiceTypeBehaviors">
<endpoint contract=
"System.Web.ApplicationServices.AuthenticationService"
binding="basicHttpBinding"
bindingConfiguration="userHttps"
bindingNamespace="http://asp.net/ApplicationServices/v200"/>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="userHttps">
<security mode="Transport" />
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="AuthenticationServiceTypeBehaviors">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment
aspNetCompatibilityEnabled="true"/>
</system.serviceModel>

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