Silverlight with WIF and REST Web Services

Using Silverlight in conjunction with Windows Identity Foundation prevents you from calling REST web services, at least out of the box. Here is why:

  1. Using WIF to authenticate the Silverlight client against a server relies on security tokens being sent from client to server. These tokens are sent as cookies
  2. Sending cookies from a Silverlight application means you have to use the BrowserHTTP network stack since the other option for silverlight (ClientHTTP stack) is incapable of sending cookies
  3. BrowserHTTP in turn is limited to sending GET and POST requests. But REST web service methods often use other HTTP verbs as well, such as PUT and DELETE
  4. So this means we can’t use WIF with Silverlight AND call web service methods by HTTP verbs other than GET and POST, right?

Right, not as it is. But in certain circumstances, one is able to get over this limitation.

These are the moving parts:

So what is needed for our workaround? The web service has to understand and handle a request header that you can set in your Silverlight App and that is being used to tunnel the actual verb.

I chose to use X-HTTP-Method-Override as header. If you are responsible for the implementation of the web service, you could of course use every spare header you’d like, but I think X-HTTP-Method-Override pretty much sticks to the standards.

The solution can be outlined as follows:

  1. You still use the BrowserHTTP stack, which lets WIF do its magic.
  2. Whenever you want to send, lets say a PUT request, you set a header in the Silverlight app on this request which contains the actual verb you want to use (in this example, PUT) and let the stack issue a POST request to the resource.
  3. The web service recognizes the header and uses the contained verb to handle the requests as opposed to the verb the stack reported.

Here is some code to tunnel the verb by setting a header:

var request = (HttpWebRequest)System.Net.Browser.WebRequestCreator.BrowserHttp.Create(new Uri(fullUri));
request.Headers["X-HTTP-Method-Override"] = "PUT";
request.Method = "POST";

Next, here is what is necessary to make the web service understand this header.ASP.NET MVC offers the [Authorize] attribute to make sure incoming calls to a method are authenticated.

This attribute uses the actual verb and does not recognize our custom header, so we will need to provide another implementation for that. Luckily, that one is easy. Below is the full sample implementation of an attribute that uses the custom header if present, otherwise it will use the real verb. This implementation is mostly based on the ASP.NET MVC version of the Authorize attribute.

[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[AttributeUsage(AttributeTargets.Method, AllowMultiple =false, Inherited =true)]
public class AcceptTunneledVerbsAttribute : ActionMethodSelectorAttribute
{
   public AcceptTunneledVerbsAttribute(HttpVerbs verbs)
       : this(EnumToArray(verbs))
   {
   }

   public AcceptTunneledVerbsAttribute(paramsstring[] verbs)
   {
       if (verbs == null|| verbs.Length ==0)
       {
           throw new ArgumentException("Argument was null or empty", "verbs");
       }

       Verbs = new ReadOnlyCollection<string>(verbs);
   }

   public ICollection<string> Verbs
   {
       get;
       private set;
   }

   private static void AddEntryToList(HttpVerbs verbs, HttpVerbs match, List<string> verbList, string entryText)
   {
       if ((verbs & match) !=0)
       {
           verbList.Add(entryText);
       }
   }

   internal static string[] EnumToArray(HttpVerbs verbs)
   {
       List<string> verbList =new List<string>();

       AddEntryToList(verbs, HttpVerbs.Get, verbList, "GET");
       AddEntryToList(verbs, HttpVerbs.Post, verbList, "POST");
       AddEntryToList(verbs, HttpVerbs.Put, verbList, "PUT");
       AddEntryToList(verbs, HttpVerbs.Delete, verbList, "DELETE");
       AddEntryToList(verbs, HttpVerbs.Head, verbList, "HEAD");

       return verbList.ToArray();
   }

   public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
   {
       if (controllerContext ==null)
       {
           throw new ArgumentNullException("controllerContext");
       }

       string incomingVerb = controllerContext.HttpContext.Request.Headers["X-HTTP-Method-Override"] ?? controllerContext.HttpContext.Request.HttpMethod;
       return Verbs.Contains(incomingVerb, StringComparer.OrdinalIgnoreCase);
   }
}

Credit to whom credit is due: These have been my sources

 

Golo Roden: Hint for tunneling the verb (german) http://www.des-eisbaeren-blog.de/post/2009/07/13/ClientHttpStack-in-Silverlight-3.aspx

Jeff Prosise: Differences between Silverlight network stacks http://www.wintellect.com/CS/blogs/jprosise/archive/2009/10/14/silverlight-3-s-new-client-networking-stack.aspx

stackoverflow user Levi pointed into the right direction for handling the header in ASP.NET MVC. Make sure to read the comments, subclassing the Attribute does not work. http://stackoverflow.com/questions/467535/is-it-possible-to-implement-x-http-method-override-in-asp-net-mvc


 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s