Pages

Friday, March 9, 2012

Custom authentication for WCF REST over Https

I recently had to build a WCF REST service over HTTPS. Once built and functioning we decided to include  some sort of authentication mechanism, so that the service could only be used by authorized members and not anyone on the internet, which makes perfect sense.

I thought it would be a piece of cake, as it seems to be a pretty common requirement doesn't it?
To my surprise, this task was a little complicated and not something available "out of the box" in Visual Studio.

It's all in the Bindings
I had had the opportunity to build WCF Services running over HTTPS in the past and with authentication credentials included. You just have to declare your wsHttpBinding and add a 'security' node to it with attribute mode="TransportWithMessageCredential". After that, a specified credentials validator class would take care of examining the incoming requests' credentials and approve or disapprove the requests.

(Click on image to enlarge)

The class with name  "MyUserValidatorClass" would validate the credentials sent in request. It needs to inherit 'UserNamePasswordValidator' and override the Validate method. There is plenty of documentation about that.

But there was a problem: that was not a REST service :(
I thought something similar would be possible for REST services, but there is no 'TransportWithMessageCredential' for the webHttpBinding, which is the binding used by REST services.

There is a solid solution for validating custom credentials in WCF REST services explained in this article. Unfortunately this is for services that are not hosted on IIS. Mine was to be hosted on IIS.

And there is also another solution for this problem using HTTP Modules to intercept requests. This one solves the problems and does it for IIS!!! Check it out here.

However I decided to use a simpler approach to tackle the problem, by using the Authorization header of the HTTP Requests. While it may not be the strongest solution, it has the benefit of being really simple and not time consuming.


The Project
As an example I built a simple service: There is only one method (GetWorkers) and therefore I defined a sample Worker class:

(Click on any image in order to enlarge)


Then I defined a simple Repository to pull a list of workers:



The contract and implementation of the service:
Notice the use of the WebGet attritube, so in fact the method will be invoked via Service.svc/workers/


I also added a class to validate the Authorization header. The class is expecting the Authorization header of the HTTP Request to have a value represented by the combination of user name and password all in one line, separated by a column(":") like:
username:password


For simplicity I decided to validate requests by expecting the user name "Paul" and password "freedom". It is here where you will implement your own credentials validation.

And then the chicken in all this: the configuration.

You need to define a behavior that specifies that you have a class which will filter every request in order to validate credentials. That is done with the serviceAuthorization node:


Then the webHttpBinding to support REST, and the added security node, to support HTTPS:


And finally the assignment of the behavior and binding to the service:


And that's it!
Now, in order to test the service you need to expose it via HTTPS. In your local environment you can do that by publishing your site on your IIS, and then you have to create a self-signed certificate.

In order to consume the service from code, you have to add an Authorization header to the HTTP Request before making the web calls.

In order to consume it quickly you can use Fiddler, and compose your request passing an Authorization header manually like so:


I hope it helps somebody out there!

Download source code

9 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. please post the code to make a call to above service from a client.

    ReplyDelete
  3. Hey Shekkar, there is a "Download source code" link at the end of the article for that purpose. It is in blue color.

    ReplyDelete
  4. Hi Henrik, thanks for that tidy solution, simple & superb. Regards, Robert.

    ReplyDelete
  5. How do you get WCF REST to return an 401 Unauthorized error when returning false from CheckAccessCore? I get back a 400 Bad Request.

    Also, I believe the username/password need to be base64 encoded and prefixed with "Basic " in the Authorization header.

    ReplyDelete
  6. hey anonymous, it's been a while since I wrote this article and I dont remember all the details. But if you download the source code you will see it all there working

    ReplyDelete
  7. Hey Henrik,

    No problem. I have downloaded the code, but I think there are a couple of issues with it.

    You throw a FaultException from CheckAccessCore when the Authorization header is missing. This gets translated into a HTTP 400 Bad Request response by WCF. I think that instead of throwing a FaultException your catch statement should look something like this:
    WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
    WebOperationContext.Current.OutgoingResponse.Headers.Add(HttpResponseHeader.WwwAuthenticate, "Basic realm=\"example.com\"");
    return false;

    To clarify, your Authorization header will look something like this when you receive it:
    Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
    See http://www.ietf.org/rfc/rfc2617.txt for more details.

    ReplyDelete
  8. AnonymousJuly 18, 2013

    I am getting an issue where I fiddler is kicking a wobbler about my certificate ( RemoteCertificateNameMismatch). Have the service hosted in IIS, any ideas?

    ReplyDelete