Wednesday, June 30, 2010

Custom Authentication with Silverlight and WCF Data Services

I’m currently ripping out the .NET web forms configuration component for my Chaos Filter product and replacing it with a Silverlight component that is being fed by and OData WCF Data Service.  One of the requirements I have is to secure the services.  My strategy for securing my ASMX web services was to provide an AppId, DateStamp, User Context ID and a security hash, where AppId identified the client application, the User Context ID was used to identify the current users session, the DateStamp was used to eliminate any replay attacks a secure key was used to create a security hash to make sure that every thing is valid.  So each ASMX web request contained (appId,  security hash, userContextId, dateStamp).  It’s a little bit of an overhead but it seemed to be pretty secure.

With a traditional ASMX request this information was always just sent up as parameters to the call.  When creating and OData query I needed a different approach, and needed to extract that information on the server to perform authentication (confirm the users identity) and then authorization (confirm the user can do what they are asking to do).

So after creating the my security hash, here are the steps necessary to complete the hand-shaking.

  • Within the Silverlight application create a partial class for the main DataServiceContext on the generated proxy for your WCF Service.
  • Provide a partial method implementation for OnContextCreated within the constructor attach a handler to the SendingRequest event.

partial void OnContextCreated()
{
   this.SendingRequest += MyEntities_SendingRequest;
}

  • Add a method to set the auth fields on the partial class (these are custom fields created to hold the values until the actual call is made)

public void SetAuthParams(string appId, string hash, string userContextId, DateTime dateStamp)

  • In your event handler for SendingRequest, add the following code

void MyEntities_SendingRequest(object sender, SendingRequestEventArgs e)
{
    e.RequestHeaders["ApiId"] = _appId;
    e.RequestHeaders["SessionId"] = _userContextId;
    e.RequestHeaders["SecurityHash"] = _hash;
    e.RequestHeaders["TimeStamp"] = _timeStamp;
}

  • Then to make the call simply:
  • serviceContext.SetAuthParams(appId, securityHash, userContextId, dateStamp);
    conferences.LoadAsync(conferenceQuery);

  • Next on the server side we can extract these values by simply looking at the Request.Headers collection

var headers = System.Web.HttpContext.Current.Request.Headers;
foreach (var header in headers)
    Debug.WriteLine("{0} -> {1}", header, headers[header.ToString()]);

  • Finally we need to add some configuration to our applications.  On the Silverlight side we need to add the following to the root node Manifest for our .XAP file.

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            ExternalCallersFromCrossDomain="ScriptableOnly">

  • On the server side that hosts the WCF service we need to add the following to the <system.serviceModel> node.
  •     <serviceHostingEnvironment aspNetCompatibilityEnabled="true"  />

    With this approach you should be able to pass parameters via the header from your Silverlight Client to a WCF Data Service for what ever your application requires.

    -twb

2 comments:

  1. In SL4 it seems System.Data.Services.Client.DataServiceContext is not partial.

    ReplyDelete
  2. Hey Now Wolf Bytes,

    Stellar Post!

    Thx 4 the info,
    Catto

    ReplyDelete