次の方法で共有


OData and Authentication - Part 3 - ClientSide Hooks

So far in this series we’ve looked at Windows Authentication.

For both Windows and Basic Authentication, Data Services does the authentication handshake and subsequent sending of authentication headers – all without you directly setting a http header.

It can do this because there is a higher level abstraction – the Credentials property – that hides the implementation details from you.

All this works great for Windows and Basic Authentication. However if you are using a different authentication scheme, for arguments sake OAuth WRAP, the Credentials property is of no use. You have to get back down to the request and massage the headers directly.

You might need to set the request headers for all sorts of reasons, but probably the most common is Claims Based Authentication.

So before we look into how to set the headers, a little background…

Claims based Authentication 101

The basic idea of Claims Based Auth, is that you authenticate against an Identity Provider and request an ‘access token’ which you can use to make requests against a server of protected resources.

The server – essentially the gatekeeper – looks at the ‘access token’ and verifies it was issued by an Identity Provider it trusts, and if so, allows access to the resources.

Keeping the claim private

Any time you send a token on the wire it should be encrypted, so that it can’t be discovered and re-used by others. This means claims based authentication – at least in the REST world – generally uses HTTPS.

Many Authentication Topologies

While the final step – setting the access token – is very simple, the process of getting an authentication token can get much more complicated, with things like federated trust relationships, delegated access rights etc.

In fact, there are probably hundreds of ‘authentication topologies’ that can leverage claims based authentication. And each topology will involve a different process to acquire a valid ‘access token’.

We’re not quite ready to cover these complexities yet, but we will revisit the specifics in a later post.

Still, at the end of the day, the client application simply needs to pass a valid access token to the server to gain access.

Example Scenario: OAuth WRAP

So if for example you have an OData service that uses OAuth WRAP for authentication the client would need to send a request like this:

GET /OData.svc/Products(1)
Authorization: WRAP access_token=”123456789”

And the server would need to look at the Authorization header and decide if the provided access_token is valid or not.

Sounds simple enough.

The real question for today is how do you set these headers?

Client Side Hooks

Before making requests

On the client-side adding the necessary headers is pretty simple.

Both the Silverlight and the .NET DataServiceContext have an event called SendingRequest that you can hook up to. And in your event handler you can add the appropriate headers.

For OAuth WRAP your event handler might look something like this:

void OnSendingRequest(object sender, SendingRequestEventArgs e)
{
e.RequestHeaders.Add("Authorization","WRAP access_token=\"123456789\"");
}

What if the Access Token is invalid or missing?

If the Access Token is missing or invalid the server will respond with a 401 unauthorized response, which your code will need to handle.

Unfortunately in the Data Services client today there is no place to hook into the HttpResponse directly, so you have to wait for an Exception.

You will get either a DataServiceQueryException or DataServiceRequestException depending on what you were doing, and both of those have a Response property (which is *not* a HttpResponse) that you can interrogate like this:

try
{
foreach (Product p in ctx.Products)
Console.WriteLine(p.Name);
}
catch (DataServiceQueryException ex)
{
var scheme = ex.Response.Headers["WWW-Authenticate"];
var code = ex.Response.StatusCode;
if (code == 401 && scheme == "WRAP")
DoSomethingToGetAnOAuthAccessToken();
}

The problem with trying to get an Access Token when challenged like this, rather than up front before you make the request, is that you now have to go and acquire a valid token, and somehow maintain context too, so the user isn’t forced to start again.

It is also a little unfortunate that you can’t easily centralize this authentication failure code.

Summary

As you can see it is relatively easy to add custom headers to requests, which is ideal for integrating with various auth schemes on the client side.

It is however harder to look at the response headers. You can do it, but only if you put try / catch blocks into your code, and write code to handle the UI flow interruption.

So our recommendation is that you get the necessary tokens / claims etc – for example an OAuth access_token – before allowing users to even try to interact with the service.

In Part 4 we will look at the Server-Side hooks…

Alex James
Program Manager
Data Service Team

Comments

  • Anonymous
    June 12, 2010
    Seems simple enough... however, what if I can't access the service metadata withouth authentication ? In that case, the "add service reference" command in Visual Studio doesn't work... Using datasvcutil, it seems there is no way to specify the credentials either

  • Anonymous
    April 30, 2011
    Is there a way to utilise WIF on both the client and server to encapsulate authentication in a manageable way?

  • Anonymous
    January 17, 2012

  1. How can we integrate secured Novell OpenLdap for authentication?
  2.  How can we integrate secured Novell OpenLdap for authentication using powerpivot and Excel 2010? Are there any examples?
  • Anonymous
    August 01, 2012
    Thank you for this article. The lack of a way to hook the response is a significant hurdle for me. In my situation, I provide authentication credentials using Basic Auth, which is easy enough in a SendingRequest event handler. But, the service response contains a cookie which I'm expected to supply in future requests. Since I can't hook the response, it appears that I have no way to get that cookie.

  • Anonymous
    November 21, 2012
    I have a client app which is only html,css and javascript and I want to authenticate users using WCF restfull service ( which doesent have any *.svg file only calls base on URL). Both, the WCF service an the HTML client are on the same domain so I can do ajax calls. After I'm introducing the user and password I'm doing a ajax call to to wcf service. If the user exist in the database then the service will givem response 200 otherwise I receive reponse 401 unautorized. When I get response 200 I still not have access to the folder where my html files are located and I'm gettting response 200 from the server. Both, the client and service are runnig using IIS. Any thougts? I don't want tu use ASPX page or ASP.NET Mvc pages. I want to use in my client pure HTML/Javascript files.

  • Anonymous
    January 27, 2013
    It's not really obvious how to go to the previous article. A "series" navigation link would be helpful.

  • Anonymous
    November 05, 2013
    I see this post is 3 years old at this point, so I'm not surprised it doesn't work with the latest 5.6 version of WCF Data Services, but I wonder why? I've tried hooking into both SendingRequest and SendingRequest2 events on my DataServiceContext-derived class, and code runs to add the header, but when viewing in Fiddler2, the added header never shows up at the server. Any ideas?