This blog post was supposed to be about porting the Dive Log Silverlight application to WPF and how easy that transition was. Well, turned out I got stuck with WCF problems long before I could even start touching the XAML. Before diving into the problem, lets step back and refresh how authentication and data access is handled in the Dive Log Silverlight application.
The database schema for the application is really straight forward; it consists of a user table and a dives table. On top of the user table I’ve implemented a simple ASP.NET Membership Provider and configured it in web.config. I’ve also added authorization rules to my configuration file limiting access to the web service and the web page hosting the Silverlight content. The web service used by the Silverlight application is created using the “Silverlight-enabled WCF service” template. That the template does is to create a WCF service that is configured to use the basicHttpBinding and with ASP.NET compatibility turned on. WCF is built to be agnostic to the hosting environment, but by turning on ASP.NET compatibility you get access to the HttpContext object, and other web related stuff like cookies and headers. Since the service is protected using the membership provider I can access the currently authenticated user by accessing the the HttpContext.Current.User.Identity.Name property.
So how does Silverlight deal with authentication? Well, the cool thing is that it normally doesn’t! The Silverlight control is hosted on a web page that is protected by the membership provider. That means that you have to logon before you can run the application. When you logon the ASP.NET Forms Authentication mechanism will write an authentication cookie in the browser. When Silverlight loads and starts making requests back to the server any request goes through the browsers networking stack. The browser will add any cookies or authentication headers set in the browser before sending the request. In our case the request back to the WCF service will contain our authentication cookie, and we can access the current authenticated user. All good!
When I started porting the application to WPF I wanted to reuse the same WCF service used by Silverlight. But since I’m not going to logon using my browser I know that I had to provide some authentication mechanism for my WPF application. I assumed this should be fairly trivial, since one of the new features of .NET 3.5 and Visual Studio 2008 is the Client Application Services. This is a set of services enabling you to reuse your ASP.NET membership- role- and profile providers from client applications such as AJAX or Windows Forms/WPF applications. You get a full client side API to authenticate users, check their roles, or store settings online. This is handy, but I don’t quite get the point of the Membership functionality. There is no obvious way to authenticate the user using the Client Application Services and then use the same identity on other services on the same domain. All the tutorials online are limited to show how the user can change some setting and persist this using the profile services. I tried messing around with the System.Web.Security.Membership class, but couldn’t find a way to grab the authentication cookie and add it to my other service clients.
While researching what I can do with the Client Application Services I came across the WCF Authentication Service. This is part of the Client App Services, and is basically a service sitting on top of your membership provider. The service is implemented by the class System.Web.ApplicationServices.AuthenticationService, and to enable an authentication service all you have to do is configure a WCF end-point. I’m not going into all the details, but enabling the service isn’t too hard. It contains methods like login and logout, and will use whatever membership provider you have configured for your web application. It works similar to the Client App Services, and when you login it will write an authentication cookie, taking us back to the original problem of getting that cookie added to our other service clients. Thankfully it is possible to access the request and response cookies from WCF, even if it’s not really obvious how to do it. Or it could just be me not having done much WCF work… The following code calls the authentication service and logs in and grabs the authentication cookie. Next step is to add the authentication header to the other service client before calling the Dive Log service. The code smells like a little like dog-poo, but that’s what it takes to make it work.
I don’t know if this is the only way to use the authentication service and grab the authentication cookie. Some more experienced WCF developers recommended that I should add a new service endpoint with a wsHttpBinding (the ws-deathstar binding). You can configure your wsHttpBinding to use a membership provider for authentication, but it kind of sucks that you can’t just port a Silverlight app to the desktop without having to change your server code. While messing around with the cookie handling I remembered that this stuff used to be a lot easier before we got WCF. You can use the old web service stack from .NET 2.0 and you can even add a new web reference (not service reference) in Visual Studio 2008. However you need to dig a little before you find the dialog. You find under the advanced button in the service reference dialog, and then by clicking the add web reference button.
After adding a web reference you get two folders, one for service references and one for web references. So using the old .NET 2.0 Web Services the code to authenticate and use the same cookie for the Dive Log service is a lot more compact.
I’m sure some WCF experts have a better solution for this, and I would love to hear suggestions in the comments. However I do think this is a quite common scenario if you have a set of services built using the Silverlight WCF template that you want to use in a WPF version of your application. In other cases you might not be the one controlling the server side, and I think WCF should support those scenarios. WCF have made a lot of advances, adding support for REST style services and support for username/password authentication etc. However, in cases like this where you depend on cookies for authentication it really isn’t obvious how to do that using WCF.