0

First of all, I am new to WCF. I created a REST webservice using WCF. It only has one operation that retrieves the list of products (in json format). This is invoked from web using jquery. It works perfectly.

Now, what I need is to add a custom authentication to the webservice. Is it possible to add it to webHttpBinding? I added the validation:

public class ServiceAuthenticator : UserNamePasswordValidator
{
    public override void Validate(string userName, string password)
    {
        if (null == userName || null == password)
        {
            throw new ArgumentNullException();
        }

        if (userName != "testuser")
        {
            throw new SecurityTokenException("Unknown Username or Password");
        }
    }
}

And I tried to change web.config to make it work, but I can't.

This is my web.config working without custom authentication:

  <system.serviceModel>
<bindings>
  <webHttpBinding>
    <binding crossDomainScriptAccessEnabled="true">
    </binding>
  </webHttpBinding>
</bindings>
<behaviors>
  <endpointBehaviors>
    <behavior name="WCFEndPointBehavior">
      <webHttp />
    </behavior>
  </endpointBehaviors>
  <serviceBehaviors>
    <behavior name="ServiceBehavior">
      <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="true" />
      <serviceCredentials>
        <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyRestService.ServiceAuthenticator, MyRestService" />
      </serviceCredentials>
    </behavior>
  </serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
  <service name="MyRestService.ProductRESTService" behaviorConfiguration="ServiceBehavior">
    <endpoint address=""
              behaviorConfiguration="WCFEndPointBehavior"
              binding="webHttpBinding"
              contract="MyRestService.IProductRESTService">
      <identity>

        <dns
        value="localhost"/>

      </identity>          

    </endpoint>
  </service>
</services>

Here is the jquery invoking the ws:

$("#myButton").click(function () {
    var artistURL = "http://localhost:56866/ProductRESTService.svc/GetProductList/";
    var returnData = "";
    $.ajax({
        cache: false,
        type: "GET",
        async: false,
        processData: true,
        data: "",
        url: artistURL,
        contentType: "application/json; charset=utf-8",
        dataType: "jsonp",
        beforeSend: function (xhr) {
            xhr.setRequestHeader("Authorization", "Basic " + btoa("testuser:123456789"));
        },
        error: function (request, status, error) { alert("Error"); },
        success: function (data, status, jqXHR) {
            $("div#myOutput").html(" ");
            returnData = "<table style='font-size:12pt;font-family:sans-serif'><tr><th>Product</th><th></th></tr>";
            for (prop in data) {
                if (!data.hasOwnProperty(prop)) { continue; }
                if (data[prop] != null) {
                    for (var i = 0; i < data[prop].length; i++) {
                        returnData += "<tr><td>" + data[prop][i]["ProductId"]
                                   + " " + data[prop][i]["Name"] + "</td><td align='right'>"
                                   + data[prop][i]["CategoryName"] + "</td></tr>";
                    }
                }
            }
            returnData = returnData + "</table>";
            $("div#myOutput").html(returnData);
        }
    });
    return (false);
});

When i execute this, it works ok, but the custom validator is never reached. Is anything missing in my web.config? How can I apply custom authentication to this kind of webservices?

I am using: .NET Framework 4.5 IIS Express with VS2012

Thanks in advance!

EDIT!!!!!!!!!!!!!!!!!!

Authentication is performed by IIS, that is why the custom validation wasn't triggered. There are some workarounds:

  1. Self-hosted service,
  2. ISS-Hosted performing custom authentication with ServiceAuthorizationManager (it should be used for authorization, no authentication, but this is the only way I've found). Here you have more info: Basic Authentication with IIS hosted REST services using the webHttpBinding

If anyone has another workaround...you are free to comment! I hope this helps.

VeRo
  • 81
  • 1
  • 9

1 Answers1

0

I know I'm late to the party, but I just had to solve this exact problem with one of my own services where IIS was usurping authentication and it wasn't obvious what was happening or how to resolve it, so here's a step by step:

First up a class that carries out the authentication:

Your class must be derived from UserNamePasswordValidator in the System.IdentityModel.Selectors (System.IdentityModel) namespace:

using System.IdentityModel.Selectors;

namespace MyProject
{
    public class CustomUserNameValidator : UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            if (username != "Bob" || password != "Letmein")
                through new SecurityTokenException("If you're name's not Bob, you're not coming in!")
        }
    }
}

Next You'll need to have an SSL certificate for your development endpoint:

There are a number of tutorials for generating your own self signed certificates. I used this tutorial, but there are many and you may find alternative easier methods. I had limited success with using a directly self signed certificate because of browser restrictions on self signed certificates. To circumvent this, I used the same process to create a Root Certificate Authority and then created a signed certificate using my RCA. This appeared to satisfy the browser restrictions and get rid of any warnings. [Strictly speaking, this was most likely surplus to requirement, but warnings bother me.]

When you've created your SSL cert, register it using certificates.msc into Certificates (Local Computer)\Personal\Certificates.

Bind your IIS endpoint with port 443 (or whichever port you wish) to have the cert you just created.

Next up, you need to define a service behavior:

Your service behavior must contain the references to your credential validator and your SSL certificate.

    <behavior name="MyCustomCredentialBehavior">
      <serviceCredentials>
        <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyProject.MyCustomCredentialValidator, MyAssembly" />
        <serviceCertificate findValue="CN=*.dev.myco.com"                             
                            x509FindType="FindBySubjectDistinguishedName" 
                            storeLocation="LocalMachine" />

      </serviceCredentials>
      <serviceMetadata httpGetEnabled="true" />
    </behavior>

You can either reference your cert using the distinguished name or the fingerprint. In my example here, I used the distinguished name I chose when I created the cert. If you use the fingerprint, be careful if you copy/paste the fingerprint because there's a byte mark character at the start of the sequence that'll screw up your reference so your app won't find it. Easier to copy the sequence by hand than suffer the frustration - or copy via some other plaintext editor such as Notepad that'll strip off the non-printed characters.

Lastly you'll need your service configuration to reference your service behavior:

  <service name="MySecureService"
           behaviorConfiguration="MyCustomCredentialBehavior">
    <host>
      <baseAddresses>
        <add baseAddress="https://myservice.com/mysecureservice/" />
      </baseAddresses>
    </host>
    <endpoint name="MySecureServiceEndpoint" 
              binding="webHttpBinding" 
              contract="IMySecureService" />
  </service>

If I recall correctly, this should be everything you need.

BenAlabaster
  • 39,070
  • 21
  • 110
  • 151
  • Could you please take a look at this :https://stackoverflow.com/questions/45770217/my-customauthorizationpolicy-evaluate-method-never-fires – Ehsan Akbar Aug 21 '17 at 07:44