Authenticate a service call from a Windows Phone 7 application using Forms Authentication

After some time developing for the Windows Phone 7 platform (initially with the Beta version and now with the RTM) I've come across several challenging scenarios and decided to document some of them. First of all and as a disclaimer I'd like to point out that this article is valid with the current RTM version of the WP7 development tools and with future releases, there might be better solutions to the problem exposed, but enough of this and let's get into the action !

To start with, I'd like to introduce the solution I was trying to build. This is a Windows Phone 7 application built on the Siverlight platform, this application will connect to a asp.net web site configured for FormsAuthentication to gather some data through a WCF Data Services layer and expose it to end users (Consuming WCF Data Services from a WP7 application is a topic on its own that hopefully I will cover in another article) but this one is about securing the service and authenticating the service calls.

The problem I was facing was how to authenticate the end users using Forms Authentication when they consumed the WCF Data Services. This issue is not unique to WCF Data Services but to any service call against an asp.net website configured for Forms Authentication. The reason is that it is not possible to pass the credentials of the user from the client to the server using the DataServiceContext class (or using a WebClient or HttpWebRequest classes), the reason for that is because Forms Authentication requires clients to authenticate first at the login page and then they receive a cookie that is used to authenticate against the rest of the pages. Normally on a Silverlight environment what would happen is that the application is stored in the same site that hosts the WCF services which means that users have already been authenticated and have the cookie available in their environment so any call from Silverlight to the services will work. This of course, won't work on a Windows Phone 7 environment (or any other client solution such as WPF) because accessing the application doesn't mean that the user has been alread

The most suggested solution to this problem is to create a hidden POST request to the login page of the web site in order to get the authentication cookie that will then be passed in subsequent requests to the same site. This solution involves parsing the viewstate of the login page and not only tightly couples your clients with the structure of that page but it can be quite complicated to implement (specially from a WP7 application) but is also prone to errors and not elegant at all. It was clear to me that I had to come up with a different approach !

My solution instead was to pass the user credentials in each service call as headers in the http request: User account information was being stored on each client device, my mobile app would access those credentials and create request headers in the call to the WCF Data Service to pass the values, the service would then authenticate the user using those credentials before returning any result or performing any operation. This is quite similar to how Basic Authentication works and it suffers from the same problems, mainly that user account information would be sent as clear text ! That wasn't an issue in my application (data wasn't that sensitive) although since we have control over the client application, we could easily encrypt that data using a private key deployed included on the client (I'll leave that to you!)

So the first thing we need to do to get this working is to intercept any request from our WCF entity in the client and add those headers. We can easily do that by creating a Factory class that will be responsible for creating all instances of our WCF service proxies, this class will use the SendingRequest event to pass the credentials


  1 public class Factory
  2     {
  3         private static MyEntities _context;
  4
  5         public static Services.MyEntities CreateMyEntities()
  6         {
  7             if (_context == null)
  8             {
  9                 _context = new Services.MyEntities(new Uri(SERVICE_URL));
 10
 11                 _context.SendingRequest += ((o, e) =>
 12                 {
 13             //I'm am storing the credentials from the current user in the 
 14             //'AppConfiguration' helper class
 15                     e.RequestHeaders["UserName"] = AppConfiguration.Instance.UserName;
 16                     e.RequestHeaders["Password"] = AppConfiguration.Instance.Password;
 17                 });
 18             }
 19
 20             return _context;
 21         }
 22
 23         
 24     }

We need to make sure that all classes that call the WCF services use this Factory to get an instance of the entities class. With that, the credentials will be added to any request. In the server side, we will have to intercept the request and extract the credentials from it, we will use then that information to authenticate the user using the current Forms authentication provider. It is important to notice that for this to work, we have to configure the service to allow anonymous access to it, otherwise our requests from the client device would be redirected to the login page because they don't contain the authentication cookie !

In a typical WCF service I could add the authentication section at the start of my service code, this would extract the user information from the request header and ensure it's a valid user before continuing the execution. This option is not available in a WCF Data Service because there is no actual implementation of the method call, what we have to do in that scenario is to use QueryInterceptors and ChangeInterceptors to add our authentication code in those operations that we need to secure.

In the following piece of code, I will use a QueryInterceptor to authenticate all Read requests to a certain entity of my WCF Data Service and it will also filter the results of that requests according to the supplied credentials (a very typical requirement in this type of scenarios), the same approach could then be used in a ChangeInterceptor to secure update, insert or delete operations.


  1     public class MYEntitiesService : DataService
  2     {
  3         // This method is called only once to initialize service-wide policies.
  4         public static void InitializeService(DataServiceConfiguration config)
  5         {
  6             config.SetEntitySetAccessRule("SecuredEntity", EntitySetRights.AllRead);            
  7             config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
  8         }
  9         
 10         [QueryInterceptor("SecuredEntity")]
 11         public Expressionbool>> OnQuerySecuredEntity()
 12         {
 13             MembershipUser u;
 14         if (!string.IsNullOrEmpty(HttpContext.Current.User.Identity.Name))
 15             {
 16         //for requests coming from the web site (already authenticated)
 17                 u = Membership.GetUser(HttpContext.Current.User.Identity.Name);
 18             }
 19             else
 20             {
 21                 string user = HttpContext.Current.Request.Headers["UserName"];
 22                 string pass = HttpContext.Current.Request.Headers["Password"];
 23
 24                 if (Membership.ValidateUser(user, pass))
 25                     u = Membership.GetUser(user);
 26             }
 27
 28             if (u == null)
 29                 return entity => false;
 30
 31             Guid userId = new Guid(u.ProviderUserKey.ToString());
 32
 33             return entity => entity.UserId == userId;
 34         }
 35
 36     }
 37

An that should do the trick ! I hope you liked this and feel free to contact me if you have any comment or improvement.


Created on 29/09/2010



Get Microsoft Silverlight

Get Microsoft Silverlight